diff --git a/tests/cxx/CMakeLists.txt b/tests/cxx/CMakeLists.txt index 41dd5dad9..8d0322d46 100644 --- a/tests/cxx/CMakeLists.txt +++ b/tests/cxx/CMakeLists.txt @@ -50,14 +50,10 @@ set(_pythonpath "${_pythonpath}${_separator}${ITK_DIR}/Wrapping/Generators/Pytho set(_pythonpath "${_pythonpath}${_separator}$ENV{PYTHONPATH}") # Add the test cases -# OperatorPython, ModulePlot, PipelineExecution, DockerUtilities test legacy -# infrastructure (legacy::OperatorPython / legacy modules / legacy Pipeline) -# that's being removed and are no longer kept in sync. add_cxx_test(Variant) add_cxx_test(ScanID) add_cxx_test(Utilities) add_cxx_test(VolumeBricking) -add_cxx_qtest(InterfaceBuilder) # AcquisitionClient needs the `bottle` Python package (acquisition/requirements-dev.txt) # to start its test server; not part of the conda build env, so disabled here. # if(NOT WIN32) diff --git a/tests/cxx/DockerUtilitiesTest.cxx b/tests/cxx/DockerUtilitiesTest.cxx deleted file mode 100644 index d01ba0676..000000000 --- a/tests/cxx/DockerUtilitiesTest.cxx +++ /dev/null @@ -1,255 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include -#include -#include -#include -#include -#include - -#include "legacy/DockerUtilities.h" - -using namespace tomviz; - -class DockerUtilitiesTest : public QObject -{ - Q_OBJECT - -private: - int m_invocationTimeout = 30000; - - docker::DockerRunInvocation* run( - const QString& image, const QString& entryPoint = QString(), - const QStringList& containerArgs = QStringList(), - const QMap& bindMounts = QMap()) - { - auto runInvocation = - docker::run(image, entryPoint, containerArgs, bindMounts); - QSignalSpy runError(runInvocation, &docker::DockerPullInvocation::error); - QSignalSpy runFinished(runInvocation, - &docker::DockerPullInvocation::finished); - runFinished.wait(m_invocationTimeout); - - return runInvocation; - } - - void remove(const QString& containerId) - { - auto removeInvocation = docker::remove(containerId); - QSignalSpy removeError(removeInvocation, - &docker::DockerRemoveInvocation::error); - QSignalSpy removeFinished(removeInvocation, - &docker::DockerRemoveInvocation::finished); - QVERIFY(removeFinished.wait(m_invocationTimeout)); - removeInvocation->deleteLater(); - } - - void pull(const QString& image) - { - auto alpinePullInvocation = docker::pull(image); - QSignalSpy alpinePullError(alpinePullInvocation, - &docker::DockerPullInvocation::error); - QSignalSpy alpinePullFinished(alpinePullInvocation, - &docker::DockerPullInvocation::finished); - QVERIFY(alpinePullFinished.wait(m_invocationTimeout)); - } - -private slots: - void initTestCase() - { - qRegisterMetaType(); - qRegisterMetaType(); - pull("alpine"); - pull("hello-world"); - } - - void cleanupTestCase() {} - - void runTest() - { - auto runInvocation = docker::run("hello-world"); - QSignalSpy runError(runInvocation, &docker::DockerPullInvocation::error); - QSignalSpy runFinished(runInvocation, - &docker::DockerPullInvocation::finished); - - QVERIFY(runFinished.wait(m_invocationTimeout)); - QVERIFY(runError.isEmpty()); - QCOMPARE(runFinished.size(), 1); - auto arguments = runFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 0); - - auto containerId = runInvocation->containerId(); - QVERIFY(!containerId.isEmpty()); - runInvocation->deleteLater(); - - auto logInvocation = docker::logs(containerId); - QSignalSpy logError(logInvocation, &docker::DockerLogsInvocation::error); - QSignalSpy logFinished(logInvocation, - &docker::DockerLogsInvocation::finished); - QVERIFY(logFinished.wait(m_invocationTimeout)); - QVERIFY(logError.isEmpty()); - QCOMPARE(logFinished.size(), 1); - arguments = logFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 0); - QVERIFY(logInvocation->logs().trimmed().startsWith("Hello from Docker!")); - logInvocation->deleteLater(); - remove(containerId); - } - - void pullTest() - { - auto pullInvocation = docker::run("alpine"); - QSignalSpy pullError(pullInvocation, &docker::DockerPullInvocation::error); - QSignalSpy pullFinished(pullInvocation, - &docker::DockerPullInvocation::finished); - QVERIFY(pullFinished.wait(m_invocationTimeout)); - QVERIFY(pullError.isEmpty()); - QCOMPARE(pullFinished.size(), 1); - auto arguments = pullFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 0); - } - - void runBindMountTest() - { - QMap bindMounts; - QTemporaryDir tempDir; - - bindMounts[tempDir.path()] = "/test"; - QString entryPoint = "/bin/sh"; - QStringList args; - args.append("-c"); - args.append("echo 'world' > /test/hello.txt"); - - auto runInvocation = docker::run("alpine", entryPoint, args, bindMounts); - QSignalSpy runError(runInvocation, &docker::DockerPullInvocation::error); - QSignalSpy runFinished(runInvocation, - &docker::DockerPullInvocation::finished); - QVERIFY(runFinished.wait(m_invocationTimeout)); - QVERIFY(runError.isEmpty()); - QCOMPARE(runFinished.size(), 1); - auto arguments = runFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 0); - auto containerId = runInvocation->containerId(); - remove(containerId); - runInvocation->deleteLater(); - - QFile file(QDir(tempDir.path()).filePath("hello.txt")); - QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); - QCOMPARE(QString(file.readLine()).trimmed(), QString("world")); - } - - void dockerErrorTest() - { - auto runInvocation = docker::run("alpine", "/bin/bash"); - QSignalSpy runError(runInvocation, &docker::DockerPullInvocation::error); - QSignalSpy runFinished(runInvocation, - &docker::DockerPullInvocation::finished); - QVERIFY(runFinished.wait(m_invocationTimeout)); - QVERIFY(runError.isEmpty()); - QCOMPARE(runFinished.size(), 1); - auto arguments = runFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 127); - runInvocation->deleteLater(); - } - - void stopTest() - { - QString entryPoint = "/bin/sh"; - QStringList args; - args.append("-c"); - args.append("sleep 30"); - - auto runInvocation = run("alpine", entryPoint, args); - auto containerId = runInvocation->containerId(); - QVERIFY(!containerId.isEmpty()); - runInvocation->deleteLater(); - - auto stopInvocation = docker::stop(containerId, 1); - QSignalSpy stopError(stopInvocation, &docker::DockerPullInvocation::error); - QSignalSpy stopFinished(stopInvocation, - &docker::DockerPullInvocation::finished); - QVERIFY(stopFinished.wait(m_invocationTimeout)); - QVERIFY(stopError.isEmpty()); - QCOMPARE(stopFinished.size(), 1); - auto arguments = stopFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 0); - stopInvocation->deleteLater(); - - auto inspectInvocation = docker::inspect(containerId); - QSignalSpy inspectError(inspectInvocation, - &docker::DockerPullInvocation::error); - QSignalSpy inspectFinished(inspectInvocation, - &docker::DockerPullInvocation::finished); - QVERIFY(inspectFinished.wait(m_invocationTimeout)); - QVERIFY(inspectError.isEmpty()); - QCOMPARE(inspectFinished.size(), 1); - arguments = inspectFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 0); - QCOMPARE(inspectInvocation->status(), QString("exited")); - inspectInvocation->deleteLater(); - remove(containerId); - } - - void inspectTest() - { - auto runInvocation = run("alpine"); - auto containerId = runInvocation->containerId(); - QVERIFY(!containerId.isEmpty()); - runInvocation->deleteLater(); - - // Sleep for a second to let the previous container cleanup - QTest::qSleep(1000); - - auto inspectInvocation = docker::inspect(containerId); - QSignalSpy inspectError(inspectInvocation, - &docker::DockerPullInvocation::error); - QSignalSpy inspectFinished(inspectInvocation, - &docker::DockerPullInvocation::finished); - QVERIFY(inspectFinished.wait(m_invocationTimeout)); - QVERIFY(inspectError.isEmpty()); - QCOMPARE(inspectFinished.size(), 1); - auto arguments = inspectFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 0); - QCOMPARE(inspectInvocation->status(), QString("exited")); - QCOMPARE(inspectInvocation->exitCode(), 0); - inspectInvocation->deleteLater(); - remove(containerId); - } - - void removeTest() - { - auto runInvocation = run("alpine"); - auto containerId = runInvocation->containerId(); - QVERIFY(!containerId.isEmpty()); - runInvocation->deleteLater(); - - // Sleep for a second to let the previous container cleanup - QTest::qSleep(1000); - - auto removeInvocation = docker::remove(containerId); - QSignalSpy removeError(removeInvocation, - &docker::DockerRemoveInvocation::error); - QSignalSpy removeFinished(removeInvocation, - &docker::DockerRemoveInvocation::finished); - QVERIFY(removeFinished.wait(m_invocationTimeout)); - QVERIFY(removeError.isEmpty()); - QCOMPARE(removeFinished.size(), 1); - removeInvocation->deleteLater(); - - auto inspectInvocation = docker::inspect(containerId); - QSignalSpy inspectError(inspectInvocation, - &docker::DockerPullInvocation::error); - QSignalSpy inspectFinished(inspectInvocation, - &docker::DockerPullInvocation::finished); - QVERIFY(inspectFinished.wait(m_invocationTimeout)); - QVERIFY(inspectError.isEmpty()); - QCOMPARE(inspectFinished.size(), 1); - auto arguments = inspectFinished.takeFirst(); - QCOMPARE(arguments.at(0).toInt(), 1); - inspectInvocation->deleteLater(); - } -}; - -QTEST_GUILESS_MAIN(DockerUtilitiesTest) -#include "DockerUtilitiesTest.moc" diff --git a/tests/cxx/InterfaceBuilderTest.cxx b/tests/cxx/InterfaceBuilderTest.cxx deleted file mode 100644 index b44016412..000000000 --- a/tests/cxx/InterfaceBuilderTest.cxx +++ /dev/null @@ -1,199 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "InterfaceBuilder.h" - -using namespace tomviz; - -class InterfaceBuilderTest : public QObject -{ - Q_OBJECT - -private: - InterfaceBuilder* builder; - QWidget* parentWidget; - -private slots: - void init() - { - builder = new InterfaceBuilder(this); - parentWidget = new QWidget(); - } - - void cleanup() - { - delete parentWidget; - parentWidget = nullptr; - } - - void selectScalarsWidgetCreated() - { - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "selected_scalars", "type": "select_scalars", - "label": "Scalars"} - ] - })"; - - builder->setJSONDescription(desc); - QLayout* layout = builder->buildInterface(); - QVERIFY(layout != nullptr); - - parentWidget->setLayout(layout); - - auto* widget = parentWidget->findChild("selected_scalars"); - QVERIFY(widget != nullptr); - QCOMPARE(widget->property("type").toString(), QString("select_scalars")); - } - - void selectScalarsParameterValues() - { - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "selected_scalars", "type": "select_scalars", - "label": "Scalars"} - ] - })"; - - builder->setJSONDescription(desc); - QLayout* layout = builder->buildInterface(); - QVERIFY(layout != nullptr); - parentWidget->setLayout(layout); - - // The combo box is empty because we have no DataSource. - // Manually populate the model and check items to test parameterValues(). - auto* container = - parentWidget->findChild("selected_scalars"); - QVERIFY(container != nullptr); - - auto* applyAllCB = - container->findChild("selected_scalars_apply_all"); - QVERIFY(applyAllCB != nullptr); - - auto* combo = - container->findChild("selected_scalars_combo"); - QVERIFY(combo != nullptr); - - auto* model = qobject_cast(combo->model()); - QVERIFY(model != nullptr); - - // Add items and check them - auto* itemA = new QStandardItem("scalar_a"); - itemA->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); - itemA->setData(Qt::Checked, Qt::CheckStateRole); - model->appendRow(itemA); - - auto* itemB = new QStandardItem("scalar_b"); - itemB->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); - itemB->setData(Qt::Checked, Qt::CheckStateRole); - model->appendRow(itemB); - - // Uncheck "Apply to all" so individual selections are used - applyAllCB->setChecked(false); - - auto result = InterfaceBuilder::parameterValues(parentWidget); - QVERIFY(result.contains("selected_scalars")); - - auto resultScalars = result["selected_scalars"].toList(); - QCOMPARE(resultScalars.size(), 2); - QCOMPARE(resultScalars[0].toString(), QString("scalar_a")); - QCOMPARE(resultScalars[1].toString(), QString("scalar_b")); - } - - void basicParameterTypes() - { - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "int_param", "type": "int", "label": "Int", "default": 5}, - {"name": "double_param", "type": "double", "label": "Double", - "default": 1.5}, - {"name": "bool_param", "type": "bool", "label": "Bool", "default": true} - ] - })"; - - builder->setJSONDescription(desc); - QLayout* layout = builder->buildInterface(); - QVERIFY(layout != nullptr); - - parentWidget->setLayout(layout); - - auto result = InterfaceBuilder::parameterValues(parentWidget); - QVERIFY(result.contains("int_param")); - QVERIFY(result.contains("double_param")); - QVERIFY(result.contains("bool_param")); - - QCOMPARE(result["int_param"].toInt(), 5); - QCOMPARE(result["double_param"].toDouble(), 1.5); - QCOMPARE(result["bool_param"].toBool(), true); - } - - void enableIfVisibleIf() - { - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "toggle", "type": "bool", "label": "Toggle", "default": false}, - {"name": "enabled_dep", "type": "int", "label": "Enabled Dep", - "default": 0, "enable_if": "toggle == true"}, - {"name": "visible_dep", "type": "int", "label": "Visible Dep", - "default": 0, "visible_if": "toggle == true"} - ] - })"; - - builder->setJSONDescription(desc); - QLayout* layout = builder->buildInterface(); - QVERIFY(layout != nullptr); - - parentWidget->setLayout(layout); - // Must show the parent so isVisible() works on children -- - // Qt's isVisible() requires all ancestors to be visible. - parentWidget->show(); - - auto* toggleCheckBox = - parentWidget->findChild("toggle"); - QVERIFY(toggleCheckBox != nullptr); - - auto* enabledWidget = parentWidget->findChild("enabled_dep"); - QVERIFY(enabledWidget != nullptr); - - auto* visibleWidget = parentWidget->findChild("visible_dep"); - QVERIFY(visibleWidget != nullptr); - - // Toggle is false by default -- enabled_dep should be disabled, - // visible_dep should be hidden - QVERIFY(!enabledWidget->isEnabled()); - QVERIFY(!visibleWidget->isVisible()); - - // Toggle on -- both should become active - toggleCheckBox->setChecked(true); - QVERIFY(enabledWidget->isEnabled()); - QVERIFY(visibleWidget->isVisible()); - - // Toggle back off -- both should revert - toggleCheckBox->setChecked(false); - QVERIFY(!enabledWidget->isEnabled()); - QVERIFY(!visibleWidget->isVisible()); - } -}; - -QTEST_MAIN(InterfaceBuilderTest) -#include "InterfaceBuilderTest.moc" diff --git a/tests/cxx/ModulePlotTest.cxx b/tests/cxx/ModulePlotTest.cxx deleted file mode 100644 index 8967e5c60..000000000 --- a/tests/cxx/ModulePlotTest.cxx +++ /dev/null @@ -1,273 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "legacy/modules/ModulePlot.h" -#include "legacy/operators/OperatorResult.h" - -using namespace tomviz; - -class ModulePlotTest : public QObject -{ - Q_OBJECT - -private: - ModulePlot* modulePlot; - - // Create a simple vtkTable with x and y columns - vtkNew createTestTable() - { - vtkNew table; - - vtkNew xCol; - xCol->SetName("x"); - xCol->SetNumberOfTuples(5); - for (int i = 0; i < 5; ++i) { - xCol->SetValue(i, static_cast(i)); - } - - vtkNew yCol; - yCol->SetName("y"); - yCol->SetNumberOfTuples(5); - for (int i = 0; i < 5; ++i) { - yCol->SetValue(i, static_cast(i * i)); - } - - table->AddColumn(xCol); - table->AddColumn(yCol); - return table; - } - -private slots: - void init() - { - modulePlot = new ModulePlot(this); - } - - void cleanup() - { - delete modulePlot; - modulePlot = nullptr; - } - - void label() - { - QCOMPARE(modulePlot->label(), QString("Plot")); - } - - void visibilityDefault() - { - QVERIFY(modulePlot->visibility()); - } - - void setVisibilityToggle() - { - QVERIFY(modulePlot->setVisibility(false)); - QVERIFY(!modulePlot->visibility()); - - QVERIFY(modulePlot->setVisibility(true)); - QVERIFY(modulePlot->visibility()); - } - - void exportDataTypeString() - { - QCOMPARE(modulePlot->exportDataTypeString(), QString("")); - } - - void dataToExportReturnsNull() - { - QVERIFY(modulePlot->dataToExport() == nullptr); - } - - void initializeDataSourceReturnsFalse() - { - // Plot only works with OperatorResult, not DataSource - QVERIFY(!modulePlot->initialize(static_cast(nullptr), - nullptr)); - } - - void finalize() - { - QVERIFY(modulePlot->finalize()); - } - - void finalizeTwice() - { - QVERIFY(modulePlot->finalize()); - QVERIFY(modulePlot->finalize()); - } - - void deserializeVisibility() - { - QJsonObject props; - props["visibility"] = false; - QJsonObject json; - json["properties"] = props; - - QVERIFY(modulePlot->deserialize(json)); - QVERIFY(!modulePlot->visibility()); - } - - void deserializeRestoresVisibilityTrue() - { - modulePlot->setVisibility(false); - QVERIFY(!modulePlot->visibility()); - - QJsonObject props; - props["visibility"] = true; - QJsonObject json; - json["properties"] = props; - - QVERIFY(modulePlot->deserialize(json)); - QVERIFY(modulePlot->visibility()); - } - - void dataSourceMovedDoesNotCrash() - { - modulePlot->dataSourceMoved(1.0, 2.0, 3.0); - } - - void dataSourceRotatedDoesNotCrash() - { - modulePlot->dataSourceRotated(45.0, 90.0, 0.0); - } - - void initializeWithOperatorResult() - { - auto* objBuilder = pqApplicationCore::instance()->getObjectBuilder(); - auto* server = pqApplicationCore::instance()->getActiveServer(); - QVERIFY(server != nullptr); - - auto* pqView = objBuilder->createView("XYChartView", server); - QVERIFY(pqView != nullptr); - auto* viewProxy = pqView->getViewProxy(); - QVERIFY(viewProxy != nullptr); - - auto table = createTestTable(); - auto* result = new OperatorResult(this); - result->setName("test_result"); - result->setDataObject(table); - - QVERIFY(modulePlot->initialize(result, viewProxy)); - QVERIFY(modulePlot->visibility()); - - QVERIFY(modulePlot->finalize()); - objBuilder->destroy(pqView); - } - - void addToPanel() - { - auto* objBuilder = pqApplicationCore::instance()->getObjectBuilder(); - auto* server = pqApplicationCore::instance()->getActiveServer(); - auto* pqView = objBuilder->createView("XYChartView", server); - auto* viewProxy = pqView->getViewProxy(); - - auto table = createTestTable(); - auto* result = new OperatorResult(this); - result->setName("test_result"); - result->setDataObject(table); - - QVERIFY(modulePlot->initialize(result, viewProxy)); - - // addToPanel should create the label/log-scale controls - QWidget panel; - modulePlot->addToPanel(&panel); - - auto* layout = qobject_cast(panel.layout()); - QVERIFY(layout != nullptr); - - auto* xLogCheckBox = panel.findChild(); - QVERIFY(xLogCheckBox != nullptr); - - auto* xLabelEdit = panel.findChild(); - QVERIFY(xLabelEdit != nullptr); - - QVERIFY(modulePlot->finalize()); - objBuilder->destroy(pqView); - } - - void serializeAfterInit() - { - auto* objBuilder = pqApplicationCore::instance()->getObjectBuilder(); - auto* server = pqApplicationCore::instance()->getActiveServer(); - auto* pqView = objBuilder->createView("XYChartView", server); - auto* viewProxy = pqView->getViewProxy(); - - auto table = createTestTable(); - auto* result = new OperatorResult(this); - result->setName("test_result"); - result->setDataObject(table); - - QVERIFY(modulePlot->initialize(result, viewProxy)); - - auto json = modulePlot->serialize(); - - // Should contain properties with visibility - QVERIFY(json.contains("properties")); - auto props = json["properties"].toObject(); - QCOMPARE(props["visibility"].toBool(), true); - - // Should contain the operator result name - QCOMPARE(json["operatorResultName"].toString(), QString("test_result")); - - QVERIFY(modulePlot->finalize()); - objBuilder->destroy(pqView); - } - - void visibilityToggleWithChart() - { - auto* objBuilder = pqApplicationCore::instance()->getObjectBuilder(); - auto* server = pqApplicationCore::instance()->getActiveServer(); - auto* pqView = objBuilder->createView("XYChartView", server); - auto* viewProxy = pqView->getViewProxy(); - - auto table = createTestTable(); - auto* result = new OperatorResult(this); - result->setName("test_result"); - result->setDataObject(table); - - QVERIFY(modulePlot->initialize(result, viewProxy)); - - // Toggle visibility off and on with an actual chart backing it - QVERIFY(modulePlot->setVisibility(false)); - QVERIFY(!modulePlot->visibility()); - - QVERIFY(modulePlot->setVisibility(true)); - QVERIFY(modulePlot->visibility()); - - QVERIFY(modulePlot->finalize()); - objBuilder->destroy(pqView); - } -}; - -int main(int argc, char** argv) -{ - QApplication app(argc, argv); - pqPVApplicationCore appCore(argc, argv); - - // Create a builtin server connection so views and proxies can be created - auto* builder = pqApplicationCore::instance()->getObjectBuilder(); - builder->createServer(pqServerResource("builtin:")); - - ModulePlotTest tc; - return QTest::qExec(&tc, argc, argv); -} - -#include "ModulePlotTest.moc" diff --git a/tests/cxx/OperatorPythonTest.cxx b/tests/cxx/OperatorPythonTest.cxx deleted file mode 100644 index e3e052852..000000000 --- a/tests/cxx/OperatorPythonTest.cxx +++ /dev/null @@ -1,477 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "legacy/PipelineProxy.h" -#include "TomvizTest.h" -#include "legacy/operators/OperatorProxy.h" -#include "legacy/operators/OperatorPython.h" - -using namespace tomviz; - -class OperatorPythonTest : public ::testing::Test -{ -public: - // This should be called once globally - it is not in my testing :-( - static void SetUpTestSuite() - { - // Register our factories for Python wrapping. - OperatorProxyFactory::registerWithFactory(); - PipelineProxyFactory::registerWithFactory(); - } - -protected: - void SetUp() override - { - // Register our factories for Python wrapping. Should be done globally - // above, but for some reason isn't running for me. Putting it here as a - // hack - FIXME: remove this when the single invocation works. - OperatorProxyFactory::registerWithFactory(); - PipelineProxyFactory::registerWithFactory(); - dataObject = vtkDataObject::New(); - pythonOperator = new OperatorPython(nullptr); - } - - void TearDown() override - { - dataObject->Delete(); - pythonOperator->deleteLater(); - } - - vtkDataObject* dataObject; - OperatorPython* pythonOperator; -}; - -TEST_F(OperatorPythonTest, transform_function) -{ - pythonOperator->setLabel("transform_function"); - QFile file(QString("%1/fixtures/function.py").arg(SOURCE_DIR)); - if (file.open(QIODevice::ReadOnly)) { - QByteArray array = file.readAll(); - QString script(array); - pythonOperator->setScript(script); - pythonOperator->transform(dataObject); - file.close(); - } else { - FAIL() << "Unable to load script."; - } -} - -TEST_F(OperatorPythonTest, operator_transform) -{ - pythonOperator->setLabel("operator_transform"); - QFile file(QString("%1/fixtures/test_operator.py").arg(SOURCE_DIR)); - if (file.open(QIODevice::ReadOnly)) { - QByteArray array = file.readAll(); - QString script(array); - pythonOperator->setScript(script); - ASSERT_EQ(pythonOperator->transform(dataObject), TransformResult::Complete); - file.close(); - } else { - FAIL() << "Unable to load script."; - } -} - -TEST_F(OperatorPythonTest, cancelable_operator_transform) -{ - pythonOperator->setLabel("cancelable_operator_transform"); - QFile file(QString("%1/fixtures/cancelable.py").arg(SOURCE_DIR)); - if (file.open(QIODevice::ReadOnly)) { - QByteArray array = file.readAll(); - QString script(array); - file.close(); - pythonOperator->setScript(script); - ASSERT_EQ(pythonOperator->transform(dataObject), TransformResult::Complete); - - // Mimic user canceling operator - std::thread canceler([this]() { - while (!pythonOperator->isCanceled()) { - // Wait until we are running to cancel - if (pythonOperator->state() == OperatorState::Running) { - pythonOperator->cancelTransform(); - } - } - }); - TransformResult result = pythonOperator->transform(dataObject); - canceler.join(); - ASSERT_EQ(result, TransformResult::Canceled); - - } else { - FAIL() << "Unable to load script."; - } -} - -TEST_F(OperatorPythonTest, set_max_progress) -{ - pythonOperator->setLabel("set_max_progress"); - QFile file(QString("%1/fixtures/set_max_progress.py").arg(SOURCE_DIR)); - if (file.open(QIODevice::ReadOnly)) { - QByteArray array = file.readAll(); - QString script(array); - file.close(); - pythonOperator->setScript(script); - - TransformResult result = pythonOperator->transform(dataObject); - ASSERT_EQ(result, TransformResult::Complete); - ASSERT_EQ(pythonOperator->totalProgressSteps(), 10); - - } else { - FAIL() << "Unable to load script."; - } -} - -TEST_F(OperatorPythonTest, update_progress) -{ - pythonOperator->setLabel("update_progress"); - QFile file(QString("%1/fixtures/update_progress.py").arg(SOURCE_DIR)); - if (file.open(QIODevice::ReadOnly)) { - QByteArray array = file.readAll(); - QString script(array); - file.close(); - pythonOperator->setScript(script); - - QSignalSpy spy(pythonOperator, SIGNAL(progressStepChanged(int))); - TransformResult result = pythonOperator->transform(dataObject); - ASSERT_EQ(result, TransformResult::Complete); - - // One from applyTransform() and one from our python code - ASSERT_EQ(spy.count(), 2); - - // Take the signal emitted from our python code - QList args = spy.takeAt(1); - ASSERT_EQ(args.at(0).toInt(), 100); - - } else { - FAIL() << "Unable to load script."; - } -} - -TEST_F(OperatorPythonTest, update_progress_message) -{ - pythonOperator->setLabel("update_progress_message"); - QFile file(QString("%1/fixtures/update_progress_message.py").arg(SOURCE_DIR)); - if (file.open(QIODevice::ReadOnly)) { - QByteArray array = file.readAll(); - QString script(array); - file.close(); - pythonOperator->setScript(script); - - QSignalSpy spy(pythonOperator, - SIGNAL(progressMessageChanged(const QString&))); - TransformResult result = pythonOperator->transform(dataObject); - ASSERT_EQ(result, TransformResult::Complete); - - ASSERT_EQ(spy.count(), 1); - - QList args = spy.takeAt(0); - ASSERT_STREQ(args.at(0).toString().toLatin1().constData(), - "Is there anyone out there?"); - - } else { - FAIL() << "Unable to load script."; - } -} - -TEST_F(OperatorPythonTest, update_data) -{ - pythonOperator->setLabel("update_data"); - // Disconnect these signals as they will cause us to reach into - // part of ParaView that aren't available in the test executable. - // We only need to test that the childDataSourceUpdated signal is - // fired, not the data source creation. - QObject::disconnect(pythonOperator, - static_cast( - &OperatorPython::newChildDataSource), - nullptr, nullptr); - QObject::disconnect(pythonOperator, &OperatorPython::childDataSourceUpdated, - nullptr, nullptr); - QFile file(QString("%1/fixtures/update_data.py").arg(SOURCE_DIR)); - if (file.open(QIODevice::ReadOnly)) { - QByteArray array = file.readAll(); - QString script(array); - file.close(); - pythonOperator->setScript(script); - - QSignalSpy spy(pythonOperator, SIGNAL(childDataSourceUpdated( - vtkSmartPointer))); - TransformResult result = pythonOperator->transform(dataObject); - ASSERT_EQ(result, TransformResult::Complete); - - ASSERT_EQ(spy.count(), 1); - - QList args = spy.takeFirst(); - vtkSmartPointer data = - args.at(0).value>(); - vtkImageData* imageData = vtkImageData::SafeDownCast(data); - int dims[3]; - imageData->GetDimensions(dims); - ASSERT_EQ(dims[0], 3); - ASSERT_EQ(dims[1], 4); - ASSERT_EQ(dims[2], 5); - - for (int z = 0; z < dims[2]; z++) { - for (int y = 0; y < dims[1]; y++) { - for (int x = 0; x < dims[0]; x++) { - ASSERT_EQ(imageData->GetScalarComponentAsDouble(x, y, z, 0), 2.0); - } - } - } - } else { - FAIL() << "Unable to load script."; - } -} - -// --- Breakpoint API tests --- - -TEST_F(OperatorPythonTest, breakpoint_default_false) -{ - ASSERT_FALSE(pythonOperator->hasBreakpoint()); -} - -TEST_F(OperatorPythonTest, breakpoint_set_emits_signal) -{ - QSignalSpy spy(pythonOperator, SIGNAL(breakpointChanged())); - pythonOperator->setBreakpoint(true); - ASSERT_TRUE(pythonOperator->hasBreakpoint()); - ASSERT_EQ(spy.count(), 1); -} - -TEST_F(OperatorPythonTest, breakpoint_no_signal_on_same_value) -{ - pythonOperator->setBreakpoint(true); - QSignalSpy spy(pythonOperator, SIGNAL(breakpointChanged())); - pythonOperator->setBreakpoint(true); - ASSERT_EQ(spy.count(), 0); -} - -TEST_F(OperatorPythonTest, breakpoint_toggle) -{ - QSignalSpy spy(pythonOperator, SIGNAL(breakpointChanged())); - pythonOperator->setBreakpoint(true); - ASSERT_TRUE(pythonOperator->hasBreakpoint()); - pythonOperator->setBreakpoint(false); - ASSERT_FALSE(pythonOperator->hasBreakpoint()); - ASSERT_EQ(spy.count(), 2); -} - -// --- Serialization tests --- - -TEST_F(OperatorPythonTest, serialize_breakpoint) -{ - pythonOperator->setLabel("test"); - pythonOperator->setBreakpoint(true); - auto json = pythonOperator->serialize(); - ASSERT_TRUE(json.contains("breakpoint")); - ASSERT_TRUE(json["breakpoint"].toBool()); -} - -TEST_F(OperatorPythonTest, serialize_no_breakpoint_by_default) -{ - pythonOperator->setLabel("test"); - auto json = pythonOperator->serialize(); - ASSERT_FALSE(json.contains("breakpoint")); -} - -TEST_F(OperatorPythonTest, deserialize_restores_label_and_script) -{ - QJsonObject json; - json["label"] = "My Label"; - json["script"] = "def transform(dataset): pass"; - json["description"] = ""; - pythonOperator->deserialize(json); - ASSERT_STREQ(pythonOperator->label().toLatin1().constData(), "My Label"); - ASSERT_STREQ(pythonOperator->script().toLatin1().constData(), - "def transform(dataset): pass"); -} - -// --- JSON description parsing tests --- - -TEST_F(OperatorPythonTest, json_description_parameters) -{ - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "param1", "type": "int", "default": 0}, - {"name": "param2", "type": "double", "default": 1.0}, - {"name": "param3", "type": "bool", "default": true} - ] - })"; - pythonOperator->setJSONDescription(desc); - ASSERT_EQ(pythonOperator->numberOfParameters(), 3); - ASSERT_STREQ(pythonOperator->label().toLatin1().constData(), - "Test Operator"); -} - -TEST_F(OperatorPythonTest, json_description_no_parameters) -{ - QString desc = R"({ - "name": "SimpleOp", - "label": "Simple Operator" - })"; - pythonOperator->setJSONDescription(desc); - ASSERT_EQ(pythonOperator->numberOfParameters(), 0); - ASSERT_STREQ(pythonOperator->label().toLatin1().constData(), - "Simple Operator"); -} - -TEST_F(OperatorPythonTest, json_description_with_results) -{ - QString desc = R"({ - "name": "ResultOp", - "label": "Result Operator", - "parameters": [ - {"name": "threshold", "type": "double", "default": 0.5} - ], - "results": [ - {"name": "output_image", "label": "Output Image"} - ] - })"; - pythonOperator->setJSONDescription(desc); - ASSERT_EQ(pythonOperator->numberOfParameters(), 1); - ASSERT_EQ(pythonOperator->numberOfResults(), 1); -} - -// --- Argument serialization round-trip tests --- - -TEST_F(OperatorPythonTest, serialize_deserialize_double_argument) -{ - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "rotation_center", "type": "double", "default": 0.0} - ] - })"; - pythonOperator->setJSONDescription(desc); - - QMap args; - args["rotation_center"] = 42.5; - pythonOperator->setArguments(args); - pythonOperator->setScript("def transform(dataset): pass"); - - auto json = pythonOperator->serialize(); - - // Deserialize onto a fresh operator - auto* newOp = new OperatorPython(nullptr); - ASSERT_TRUE(newOp->deserialize(json)); - - auto newArgs = newOp->arguments(); - ASSERT_DOUBLE_EQ(newArgs["rotation_center"].toDouble(), 42.5); - - newOp->deleteLater(); -} - -TEST_F(OperatorPythonTest, serialize_deserialize_enumeration_argument) -{ - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "transform_source", "type": "enumeration", "default": 0, - "options": [{"Manual": "manual"}, {"Load From File": "from_file"}]} - ] - })"; - pythonOperator->setJSONDescription(desc); - - QMap args; - args["transform_source"] = 1; - pythonOperator->setArguments(args); - pythonOperator->setScript("def transform(dataset): pass"); - - auto json = pythonOperator->serialize(); - - auto* newOp = new OperatorPython(nullptr); - ASSERT_TRUE(newOp->deserialize(json)); - - auto newArgs = newOp->arguments(); - ASSERT_EQ(newArgs["transform_source"].toInt(), 1); - - newOp->deleteLater(); -} - -TEST_F(OperatorPythonTest, deserialize_select_scalars) -{ - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "selected_scalars", "type": "select_scalars"} - ] - })"; - - QJsonObject json; - json["description"] = desc; - json["label"] = "Test Operator"; - json["script"] = ""; - - QJsonObject args; - QJsonArray scalarsArray; - scalarsArray.append("scalar_a"); - scalarsArray.append("scalar_b"); - args["selected_scalars"] = scalarsArray; - json["arguments"] = args; - - pythonOperator->deserialize(json); - - auto resultArgs = pythonOperator->arguments(); - auto scalars = resultArgs["selected_scalars"].toList(); - ASSERT_EQ(scalars.size(), 2); - ASSERT_STREQ(scalars[0].toString().toLatin1().constData(), "scalar_a"); - ASSERT_STREQ(scalars[1].toString().toLatin1().constData(), "scalar_b"); -} - -TEST_F(OperatorPythonTest, serialize_deserialize_roundtrip) -{ - QString desc = R"({ - "name": "TestOp", - "label": "Test Operator", - "parameters": [ - {"name": "value", "type": "double", "default": 0.0} - ] - })"; - pythonOperator->setJSONDescription(desc); - pythonOperator->setScript("def transform(dataset): pass"); - - QMap args; - args["value"] = 3.14; - pythonOperator->setArguments(args); - - auto json = pythonOperator->serialize(); - - // Verify the serialized JSON contains expected fields - ASSERT_TRUE(json.contains("description")); - ASSERT_TRUE(json.contains("label")); - ASSERT_TRUE(json.contains("script")); - ASSERT_TRUE(json.contains("arguments")); - ASSERT_STREQ(json["type"].toString().toLatin1().constData(), "Python"); - - auto* newOp = new OperatorPython(nullptr); - ASSERT_TRUE(newOp->deserialize(json)); - - ASSERT_DOUBLE_EQ(newOp->arguments()["value"].toDouble(), 3.14); - ASSERT_STREQ(newOp->label().toLatin1().constData(), "Test Operator"); - ASSERT_STREQ(newOp->script().toLatin1().constData(), - "def transform(dataset): pass"); - - newOp->deleteLater(); -} - diff --git a/tests/cxx/PipelineExecutionTest.cxx b/tests/cxx/PipelineExecutionTest.cxx deleted file mode 100644 index a660c7bed..000000000 --- a/tests/cxx/PipelineExecutionTest.cxx +++ /dev/null @@ -1,256 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "legacy/DataSource.h" -#include "legacy/Pipeline.h" -#include "legacy/PipelineProxy.h" -#include "PythonUtilities.h" -#include "TomvizTest.h" -#include "legacy/operators/OperatorProxy.h" -#include "legacy/operators/OperatorPython.h" - -using namespace tomviz; - -static QString loadFixture(const QString& name) -{ - QFile file(QString("%1/fixtures/%2").arg(SOURCE_DIR, name)); - if (!file.open(QIODevice::ReadOnly)) { - return QString(); - } - return QString(file.readAll()); -} - -static vtkSmartPointer createImageData(int dim, double fill) -{ - auto image = vtkSmartPointer::New(); - image->SetDimensions(dim, dim, dim); - image->AllocateScalars(VTK_DOUBLE, 1); - for (int z = 0; z < dim; ++z) { - for (int y = 0; y < dim; ++y) { - for (int x = 0; x < dim; ++x) { - image->SetScalarComponentFromDouble(x, y, z, 0, fill); - } - } - } - return image; -} - -class PipelineExecutionTest : public QObject -{ - Q_OBJECT - -private slots: - void initTestCase() - { - OperatorProxyFactory::registerWithFactory(); - PipelineProxyFactory::registerWithFactory(); - } - - void pipelineStopsAtBreakpoint() - { - auto image = createImageData(2, 0.0); - auto* ds = new DataSource(image); - - QString script = loadFixture("increment_scalars.py"); - QVERIFY2(!script.isEmpty(), "Failed to load increment_scalars.py"); - - // Create a pipeline (paused so operators aren't auto-executed on add) - Pipeline pipeline(ds); - pipeline.pause(); - - // Add 3 increment operators - auto* op0 = new OperatorPython(ds); - op0->setLabel("increment_0"); - op0->setScript(script); - ds->addOperator(op0); - - auto* op1 = new OperatorPython(ds); - op1->setLabel("increment_1"); - op1->setScript(script); - ds->addOperator(op1); - - auto* op2 = new OperatorPython(ds); - op2->setLabel("increment_2"); - op2->setScript(script); - ds->addOperator(op2); - - // Set a breakpoint on the 3rd operator - op2->setBreakpoint(true); - - // Resume the pipeline and execute -- it should stop before op2 - pipeline.resume(); - QSignalSpy breakpointSpy(&pipeline, &Pipeline::breakpointReached); - QSignalSpy finishedSpy(&pipeline, &Pipeline::finished); - auto* future = pipeline.execute(ds, op0); - - // Wait for the pipeline to finish (up to 10 seconds) - QVERIFY(finishedSpy.wait(10000)); - - // breakpointReached should have been emitted with op2 - QCOMPARE(breakpointSpy.count(), 1); - auto* reachedOp = breakpointSpy.takeFirst().at(0).value(); - QCOMPARE(reachedOp, op2); - - // The first 2 operators should be Complete, the 3rd should be Queued - QCOMPARE(op0->state(), OperatorState::Complete); - QCOMPARE(op1->state(), OperatorState::Complete); - QCOMPARE(op2->state(), OperatorState::Queued); - - // Delete future before pipeline goes out of scope to avoid double-free - // (PipelineFutureInternal's QScopedPointer vs executor's QObject parent) - delete future; - } - - void breakpointSkipsRemainingOps() - { - auto image = createImageData(2, 0.0); - auto* ds = new DataSource(image); - - QString addOneScript = loadFixture("increment_scalars.py"); - QString addTenScript = loadFixture("add_ten.py"); - QVERIFY(!addOneScript.isEmpty()); - QVERIFY(!addTenScript.isEmpty()); - - Pipeline pipeline(ds); - pipeline.pause(); - - // Op0: add 1, Op1: add 10, Op2 (breakpoint): would add 1 again - auto* op0 = new OperatorPython(ds); - op0->setLabel("add_one"); - op0->setScript(addOneScript); - ds->addOperator(op0); - - auto* op1 = new OperatorPython(ds); - op1->setLabel("add_ten"); - op1->setScript(addTenScript); - ds->addOperator(op1); - - auto* op2 = new OperatorPython(ds); - op2->setLabel("add_one_again"); - op2->setScript(addOneScript); - op2->setBreakpoint(true); - ds->addOperator(op2); - - pipeline.resume(); - QSignalSpy finishedSpy(&pipeline, &Pipeline::finished); - auto* future = pipeline.execute(ds, op0); - - QVERIFY(finishedSpy.wait(10000)); - - // Result should be 11.0 (0 + 1 + 10), not 12.0 (if 3rd ran too) - auto result = future->result(); - QVERIFY(result != nullptr); - int dims[3]; - result->GetDimensions(dims); - for (int z = 0; z < dims[2]; ++z) { - for (int y = 0; y < dims[1]; ++y) { - for (int x = 0; x < dims[0]; ++x) { - QCOMPARE(result->GetScalarComponentAsDouble(x, y, z, 0), 11.0); - } - } - } - - delete future; - } - - void executionOrderMatters() - { - QString addTenScript = loadFixture("add_ten.py"); - QString multiplyTwoScript = loadFixture("multiply_two.py"); - QVERIFY(!addTenScript.isEmpty()); - QVERIFY(!multiplyTwoScript.isEmpty()); - - // Order 1: [add_ten, multiply_two] on data starting at 0 - // Expected: (0 + 10) * 2 = 20 - { - auto image = createImageData(2, 0.0); - auto* ds = new DataSource(image); - Pipeline pipeline(ds); - pipeline.pause(); - - auto* opAdd = new OperatorPython(ds); - opAdd->setLabel("add_ten"); - opAdd->setScript(addTenScript); - ds->addOperator(opAdd); - - auto* opMul = new OperatorPython(ds); - opMul->setLabel("multiply_two"); - opMul->setScript(multiplyTwoScript); - ds->addOperator(opMul); - - pipeline.resume(); - QSignalSpy finishedSpy(&pipeline, &Pipeline::finished); - auto* future = pipeline.execute(ds, opAdd); - - QVERIFY(finishedSpy.wait(10000)); - - auto result = future->result(); - QVERIFY(result != nullptr); - QCOMPARE(result->GetScalarComponentAsDouble(0, 0, 0, 0), 20.0); - - delete future; - } - - // Order 2: [multiply_two, add_ten] on data starting at 0 - // Expected: (0 * 2) + 10 = 10 - { - auto image = createImageData(2, 0.0); - auto* ds = new DataSource(image); - Pipeline pipeline(ds); - pipeline.pause(); - - auto* opMul = new OperatorPython(ds); - opMul->setLabel("multiply_two"); - opMul->setScript(multiplyTwoScript); - ds->addOperator(opMul); - - auto* opAdd = new OperatorPython(ds); - opAdd->setLabel("add_ten"); - opAdd->setScript(addTenScript); - ds->addOperator(opAdd); - - pipeline.resume(); - QSignalSpy finishedSpy(&pipeline, &Pipeline::finished); - auto* future = pipeline.execute(ds, opMul); - - QVERIFY(finishedSpy.wait(10000)); - - auto result = future->result(); - QVERIFY(result != nullptr); - QCOMPARE(result->GetScalarComponentAsDouble(0, 0, 0, 0), 10.0); - - delete future; - } - } -}; - -int main(int argc, char** argv) -{ - QApplication app(argc, argv); - pqPVApplicationCore appCore(argc, argv); - - // Create a builtin server connection so proxies can be created - auto* builder = pqApplicationCore::instance()->getObjectBuilder(); - builder->createServer(pqServerResource("builtin:")); - - Python::initialize(); - - PipelineExecutionTest tc; - return QTest::qExec(&tc, argc, argv); -} - -#include "PipelineExecutionTest.moc" diff --git a/tests/cxx/ScanIDTest.cxx b/tests/cxx/ScanIDTest.cxx index 3eb1e96ef..d7233d158 100644 --- a/tests/cxx/ScanIDTest.cxx +++ b/tests/cxx/ScanIDTest.cxx @@ -3,35 +3,35 @@ #include -#include +#include +#include #include -#include "legacy/DataSource.h" -#include "TomvizTest.h" +#include "data/VolumeData.h" -using namespace tomviz; +using tomviz::pipeline::VolumeData; class ScanIDTest : public ::testing::Test { protected: - void SetUp() override { dataObject = vtkSmartPointer::New(); } + void SetUp() override { image = vtkSmartPointer::New(); } - vtkSmartPointer dataObject; + vtkSmartPointer image; }; TEST_F(ScanIDTest, scan_ids_not_present_by_default) { - ASSERT_FALSE(DataSource::hasScanIDs(dataObject)); + ASSERT_FALSE(VolumeData::hasScanIds(image)); } TEST_F(ScanIDTest, set_and_get_scan_ids) { QVector ids = { 1, 2, 3 }; - DataSource::setScanIDs(dataObject, ids); + VolumeData::setScanIds(image, ids); - ASSERT_TRUE(DataSource::hasScanIDs(dataObject)); + ASSERT_TRUE(VolumeData::hasScanIds(image)); - auto retrieved = DataSource::getScanIDs(dataObject); + auto retrieved = VolumeData::getScanIds(image); ASSERT_EQ(retrieved.size(), 3); ASSERT_EQ(retrieved[0], 1); ASSERT_EQ(retrieved[1], 2); @@ -41,21 +41,22 @@ TEST_F(ScanIDTest, set_and_get_scan_ids) TEST_F(ScanIDTest, clear_scan_ids) { QVector ids = { 1, 2, 3 }; - DataSource::setScanIDs(dataObject, ids); - ASSERT_TRUE(DataSource::hasScanIDs(dataObject)); + VolumeData::setScanIds(image, ids); + ASSERT_TRUE(VolumeData::hasScanIds(image)); - DataSource::clearScanIDs(dataObject); - ASSERT_FALSE(DataSource::hasScanIDs(dataObject)); + VolumeData::clearScanIds(image); + ASSERT_FALSE(VolumeData::hasScanIds(image)); } -TEST_F(ScanIDTest, empty_scan_ids) +TEST_F(ScanIDTest, empty_scan_ids_are_treated_as_absent) { + // VolumeData treats an empty scan-id set as "no scan IDs" (hasScanIds is + // true only when there is at least one tuple), so getScanIds is empty too. QVector ids; - DataSource::setScanIDs(dataObject, ids); + VolumeData::setScanIds(image, ids); - ASSERT_TRUE(DataSource::hasScanIDs(dataObject)); - auto retrieved = DataSource::getScanIDs(dataObject); - ASSERT_EQ(retrieved.size(), 0); + ASSERT_FALSE(VolumeData::hasScanIds(image)); + ASSERT_EQ(VolumeData::getScanIds(image).size(), 0); } TEST_F(ScanIDTest, large_scan_id_set) @@ -64,10 +65,10 @@ TEST_F(ScanIDTest, large_scan_id_set) for (int i = 0; i < 200; ++i) { ids.append(i * 10); } - DataSource::setScanIDs(dataObject, ids); + VolumeData::setScanIds(image, ids); - ASSERT_TRUE(DataSource::hasScanIDs(dataObject)); - auto retrieved = DataSource::getScanIDs(dataObject); + ASSERT_TRUE(VolumeData::hasScanIds(image)); + auto retrieved = VolumeData::getScanIds(image); ASSERT_EQ(retrieved.size(), 200); for (int i = 0; i < 200; ++i) { ASSERT_EQ(retrieved[i], i * 10); diff --git a/tomviz/AddPythonTransformReaction.cxx b/tomviz/AddPythonTransformReaction.cxx index 07e134217..59f544551 100644 --- a/tomviz/AddPythonTransformReaction.cxx +++ b/tomviz/AddPythonTransformReaction.cxx @@ -208,21 +208,20 @@ void AddPythonTransformReaction::updateEnableState() pipeline::isPortTypeCompatible(tipPort->type(), m_acceptedInputTypes)); } -OperatorPython* AddPythonTransformReaction::addExpression(DataSource*) +void AddPythonTransformReaction::addExpression(DataSource*) { auto* mainWindow = MainWindow::instance(); auto* pip = mainWindow ? mainWindow->pipeline() : nullptr; if (!pip) { - return nullptr; + return; } auto* transform = makePythonTransform(scriptLabel, scriptSource, jsonSource, {}); if (!transform) { - return nullptr; + return; } insertTransformIntoPipeline(transform); - return nullptr; } void AddPythonTransformReaction::addExpressionFromNonModalDialog() diff --git a/tomviz/AddPythonTransformReaction.h b/tomviz/AddPythonTransformReaction.h index 86074b67f..840782b8a 100644 --- a/tomviz/AddPythonTransformReaction.h +++ b/tomviz/AddPythonTransformReaction.h @@ -10,7 +10,6 @@ namespace tomviz { class DataSource; -class OperatorPython; namespace pipeline { class OutputPort; @@ -25,7 +24,7 @@ class AddPythonTransformReaction : public pqReaction const QString& source, const QString& json = QString()); - OperatorPython* addExpression(DataSource* source = nullptr); + void addExpression(DataSource* source = nullptr); void setInteractive(bool isInteractive) { interactive = isInteractive; } diff --git a/tomviz/AddResampleReaction.cxx b/tomviz/AddResampleReaction.cxx deleted file mode 100644 index 310bc526c..000000000 --- a/tomviz/AddResampleReaction.cxx +++ /dev/null @@ -1,139 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "AddResampleReaction.h" - -#include "ActiveObjects.h" -#include "legacy/DataSource.h" -#include "LoadDataReaction.h" -#include "Utilities.h" - -#include "pipeline/PortData.h" -#include "pipeline/PortType.h" -#include "pipeline/SourceNode.h" -#include "pipeline/data/VolumeData.h" - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -AddResampleReaction::AddResampleReaction(QAction* parentObject) - : pqReaction(parentObject) -{ - connect(&ActiveObjects::instance(), - &ActiveObjects::activePipelineChanged, - this, &AddResampleReaction::updateEnableState); - updateEnableState(); -} - -void AddResampleReaction::updateEnableState() -{ - parentAction()->setEnabled( - ActiveObjects::instance().pipeline() != nullptr); -} - -namespace { -vtkImageData* imageData(DataSource* source) -{ - auto t = source->producer(); - return vtkImageData::SafeDownCast(t->GetOutputDataObject(0)); -} -} // namespace - -void AddResampleReaction::resample(DataSource* source) -{ - if (!source) { - qDebug() << "Exiting early - no data :-("; - return; - } - vtkImageData* originalData = imageData(source); - int extents[6]; - originalData->GetExtent(extents); - int resolution[3] = { extents[1] - extents[0] + 1, - extents[3] - extents[2] + 1, - extents[5] - extents[4] + 1 }; - - // Find out how big they want to resample it - QDialog dialog(tomviz::mainWidget()); - QHBoxLayout* layout = new QHBoxLayout; - QLabel* label0 = new QLabel(QString("Current resolution: %1, %2, %3") - .arg(resolution[0]) - .arg(resolution[1]) - .arg(resolution[2])); - QLabel* label1 = new QLabel("New resolution:"); - layout->addWidget(label1); - QSpinBox* spinx = new QSpinBox; - spinx->setRange(2, resolution[0]); - spinx->setValue(resolution[0]); - QSpinBox* spiny = new QSpinBox; - spiny->setRange(2, resolution[1]); - spiny->setValue(resolution[1]); - QSpinBox* spinz = new QSpinBox; - spinz->setRange(2, resolution[2]); - spinz->setValue(resolution[2]); - layout->addWidget(spinx); - layout->addWidget(spiny); - layout->addWidget(spinz); - QVBoxLayout* v = new QVBoxLayout; - QDialogButtonBox* buttons = - new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); - v->addWidget(label0); - v->addLayout(layout); - v->addWidget(buttons); - dialog.setLayout(v); - if (dialog.exec() == QDialog::Accepted) { - // Compute the resampled data - double origin[3]; - double spacing[3]; - originalData->GetOrigin(origin); - originalData->GetSpacing(spacing); - int newResolution[3] = { spinx->value(), spiny->value(), spinz->value() }; - double newOrigin[3], newSpacing[3]; - int newExtents[6]; - for (int i = 0; i < 3; ++i) { - newOrigin[i] = origin[i] + extents[2 * i] * spacing[i]; - newExtents[2 * i] = 0; - newExtents[2 * i + 1] = newResolution[i] - 1; - newSpacing[i] = spacing[i] * (extents[2 * i + 1] - extents[2 * i]) / - (double)(newResolution[i]); - } - vtkNew reslice; - reslice->SetInputData(originalData); - reslice->SetInterpolationModeToLinear(); // for now - reslice->SetOutputExtent(newExtents); - reslice->SetOutputSpacing(newSpacing); - reslice->SetOutputOrigin(newOrigin); - reslice->Update(); - - // Create a SourceNode with the resampled data - auto* newSource = new pipeline::SourceNode(); - QString name = "Downsampled_" + source->label(); - newSource->setLabel(name); - newSource->addOutput("volume", pipeline::PortType::ImageData); - vtkSmartPointer resampledImage = reslice->GetOutput(); - auto vol = std::make_shared(resampledImage); - vol->setLabel(name); - newSource->setOutputData( - "volume", - pipeline::PortData(vol, pipeline::PortType::ImageData)); - - LoadDataReaction::sourceNodeAdded(newSource); - } -} -} // namespace tomviz diff --git a/tomviz/AddResampleReaction.h b/tomviz/AddResampleReaction.h deleted file mode 100644 index ae032f0d8..000000000 --- a/tomviz/AddResampleReaction.h +++ /dev/null @@ -1,30 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizAddResampleReaction_h -#define tomvizAddResampleReaction_h - -#include - -namespace tomviz { -class DataSource; - -class AddResampleReaction : public pqReaction -{ - Q_OBJECT - -public: - AddResampleReaction(QAction* parent); - - void resample(DataSource* source = nullptr); - -protected: - void updateEnableState() override; - void onTriggered() override { resample(); } - -private: - Q_DISABLE_COPY(AddResampleReaction) -}; -} // namespace tomviz - -#endif diff --git a/tomviz/CMakeLists.txt b/tomviz/CMakeLists.txt index 58276f9e5..5519ee61c 100644 --- a/tomviz/CMakeLists.txt +++ b/tomviz/CMakeLists.txt @@ -20,8 +20,6 @@ set(SOURCES AddPythonTransformReaction.cxx AddRenderViewContextMenuBehavior.cxx AddRenderViewContextMenuBehavior.h - AddResampleReaction.cxx - AddResampleReaction.h AlignWidget.cxx AlignWidget.h AnimationHelperDialog.cxx @@ -46,10 +44,6 @@ set(SOURCES ColorMapSettingsWidget.h ComboTextEditor.cxx ComboTextEditor.h - ConformVolumeDialog.cxx - ConformVolumeDialog.h - ConformVolumeReaction.cxx - ConformVolumeReaction.h ConvertToFloatReaction.cxx ConvertToFloatReaction.h CropReaction.cxx @@ -72,28 +66,16 @@ set(SOURCES DataExchangeFormat.h DataPropertiesModel.cxx DataPropertiesModel.h - legacy/DataSource.cxx - legacy/DataSource.h DataTransformMenu.cxx DataTransformMenu.h DeleteDataReaction.cxx DeleteDataReaction.h - legacy/DockerExecutor.cxx - legacy/DockerExecutor.h - legacy/DockerUtilities.cxx - legacy/DockerUtilities.h DoubleSliderWidget.cxx DoubleSliderWidget.h DoubleSpinBox.cxx DoubleSpinBox.h - DuplicateModuleReaction.h - DuplicateModuleReaction.cxx EmdFormat.cxx EmdFormat.h - ExportDataReaction.cxx - ExportDataReaction.h - legacy/ExternalPythonExecutor.cxx - legacy/ExternalPythonExecutor.h FileFormatManager.cxx FileFormatManager.h FxiFormat.cxx @@ -116,8 +98,6 @@ set(SOURCES ImageStackDialog.cxx ImageStackModel.h ImageStackModel.cxx - InterfaceBuilder.h - InterfaceBuilder.cxx InternalPythonHelper.h InternalPythonHelper.cxx IntSliderWidget.cxx @@ -136,32 +116,12 @@ set(SOURCES Logger.h ManualManipulationWidget.cxx ManualManipulationWidget.h - MergeImagesDialog.cxx - MergeImagesDialog.h - MergeImagesReaction.cxx - MergeImagesReaction.h - MoleculeSource.cxx - MoleculeSource.h OperatorSearchDialog.cxx OperatorSearchDialog.h MoleculeProperties.cxx MoleculeProperties.h - MoleculePropertiesPanel.cxx - MoleculePropertiesPanel.h InteractiveTransformWidget.cxx InteractiveTransformWidget.h - legacy/Pipeline.cxx - legacy/Pipeline.h - legacy/PipelineExecutor.cxx - legacy/PipelineExecutor.h - legacy/PipelineManager.cxx - legacy/PipelineManager.h - legacy/PipelineModel.cxx - legacy/PipelineModel.h - legacy/PipelineProxy.cxx - legacy/PipelineProxy.h - legacy/PipelineWorker.cxx - legacy/PipelineWorker.h PresetDialog.cxx PresetDialog.h PresetModel.cxx @@ -174,8 +134,6 @@ set(SOURCES PtychoWidget.h PtychoRunner.cxx PtychoRunner.h - legacy/PythonGeneratedDatasetReaction.cxx - legacy/PythonGeneratedDatasetReaction.h PythonReader.cxx PythonReader.h PythonUtilities.cxx @@ -222,12 +180,8 @@ set(SOURCES SetDataTypeReaction.cxx SetTiltAnglesReaction.cxx SetTiltAnglesReaction.h - SliceViewDialog.cxx - SliceViewDialog.h SpinBox.cxx SpinBox.h - legacy/ThreadedExecutor.cxx - legacy/ThreadedExecutor.h TimeSeriesLabel.h TimeSeriesLabel.cxx TimeSeriesStep.h @@ -462,109 +416,27 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" COMPILE_OPTIONS "-Wno-c++20-extensions;-Wno-format-truncation") endif() -list(APPEND SOURCES - legacy/modules/Module.cxx - legacy/modules/Module.h - legacy/modules/ModuleClip.cxx - legacy/modules/ModuleClip.h - legacy/modules/ModuleContour.cxx - legacy/modules/ModuleContour.h - legacy/modules/ModuleContourWidget.cxx - legacy/modules/ModuleContourWidget.h - legacy/modules/ModuleFactory.cxx - legacy/modules/ModuleFactory.h - legacy/modules/ModuleManager.cxx - legacy/modules/ModuleManager.h - legacy/modules/ModuleScaleCube.cxx - legacy/modules/ModuleScaleCube.h - legacy/modules/ModuleScaleCubeWidget.cxx - legacy/modules/ModuleScaleCubeWidget.h - legacy/modules/ModuleMenu.cxx - legacy/modules/ModuleMenu.h - legacy/modules/ModuleMolecule.cxx - legacy/modules/ModuleMolecule.h - legacy/modules/ModuleOutline.cxx - legacy/modules/ModuleOutline.h - legacy/modules/ModulePlot.cxx - legacy/modules/ModulePlot.h - legacy/modules/ModuleRuler.cxx - legacy/modules/ModuleRuler.h - legacy/modules/ModuleSegment.cxx - legacy/modules/ModuleSegment.h - legacy/modules/ModuleSlice.cxx - legacy/modules/ModuleSlice.h - legacy/modules/ModuleThreshold.cxx - legacy/modules/ModuleThreshold.h - legacy/modules/ModuleVolume.cxx - legacy/modules/ModuleVolume.h - legacy/modules/ModuleVolumeWidget.cxx - legacy/modules/ModuleVolumeWidget.h - legacy/modules/ScalarsComboBox.cxx - legacy/modules/ScalarsComboBox.h - legacy/modules/VolumeManager.cxx - legacy/modules/VolumeManager.h -) -# legacy/ subdirectories are NOT on the include path — use explicit -# prefixed includes (e.g. "legacy/DataSource.h", "legacy/modules/Module.h"). - # acquisition/ list(APPEND SOURCES acquisition/AcquisitionWidget.cxx acquisition/AcquisitionWidget.h acquisition/AcquisitionClient.cxx acquisition/AcquisitionClient.h - acquisition/AdvancedFormatWidget.cxx - acquisition/AdvancedFormatWidget.h - acquisition/BasicFormatWidget.cxx - acquisition/BasicFormatWidget.h acquisition/Connection.cxx acquisition/Connection.h acquisition/ConnectionsWidget.cxx acquisition/ConnectionsWidget.h acquisition/ConnectionDialog.cxx acquisition/ConnectionDialog.h - acquisition/CustomFormatWidget.cxx - acquisition/CustomFormatWidget.h acquisition/JsonRpcClient.cxx acquisition/JsonRpcClient.h - acquisition/PassiveAcquisitionWidget.cxx - acquisition/PassiveAcquisitionWidget.h - acquisition/RegexGroupDialog.cxx - acquisition/RegexGroupDialog.h - acquisition/RegexGroupsWidget.cxx - acquisition/RegexGroupsWidget.h - acquisition/RegexGroupSubstitution.cxx - acquisition/RegexGroupSubstitution.h - acquisition/RegexGroupSubstitutionDialog.cxx - acquisition/RegexGroupSubstitutionDialog.h - acquisition/RegexGroupsSubstitutionsWidget.cxx - acquisition/RegexGroupsSubstitutionsWidget.h - acquisition/StartServerDialog.cxx - acquisition/StartServerDialog.h ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/acquisition) list(APPEND SOURCES - legacy/operators/CustomPythonOperatorWidget.cxx - legacy/operators/CustomPythonOperatorWidget.h - legacy/operators/EditOperatorWidget.cxx - legacy/operators/EditOperatorWidget.h - legacy/operators/Operator.cxx - legacy/operators/Operator.h - legacy/operators/OperatorDialog.cxx - legacy/operators/OperatorDialog.h - legacy/operators/OperatorFactory.cxx - legacy/operators/OperatorFactory.h - legacy/operators/OperatorProxy.cxx - legacy/operators/OperatorProxy.h - legacy/operators/OperatorPython.cxx - legacy/operators/OperatorPython.h - legacy/operators/OperatorResult.cxx - legacy/operators/OperatorResult.h PipelineModuleMenu.cxx PipelineModuleMenu.h ) -# legacy/operators/ is NOT on the include path — use "legacy/operators/Operator.h" etc. # animations/ list(APPEND SOURCES @@ -942,7 +814,7 @@ if(NOT WIN32) set_target_properties(tomvizlib PROPERTIES OUTPUT_NAME tomviz) endif() set_target_properties(tomvizlib PROPERTIES AUTOUIC_SEARCH_PATHS - "${CMAKE_CURRENT_SOURCE_DIR}/legacy;${CMAKE_CURRENT_SOURCE_DIR}/legacy/modules;${CMAKE_CURRENT_SOURCE_DIR}/legacy/operators;${CMAKE_CURRENT_SOURCE_DIR}/pipeline;${CMAKE_CURRENT_SOURCE_DIR}/pipeline/sinks") + "${CMAKE_CURRENT_SOURCE_DIR}/pipeline;${CMAKE_CURRENT_SOURCE_DIR}/pipeline/sinks") # Need to set this property so that in stack traces, tomviz symbols # can be resolved. diff --git a/tomviz/ConformVolumeDialog.cxx b/tomviz/ConformVolumeDialog.cxx deleted file mode 100644 index 0f38c1d29..000000000 --- a/tomviz/ConformVolumeDialog.cxx +++ /dev/null @@ -1,56 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ConformVolumeDialog.h" -#include "ui_ConformVolumeDialog.h" - -#include "legacy/DataSource.h" - -namespace tomviz { - -ConformVolumeDialog::ConformVolumeDialog(QWidget* parent) - : QDialog(parent), m_ui(new Ui::ConformVolumeDialog) -{ - m_ui->setupUi(this); - - setupConnections(); -} - -ConformVolumeDialog::~ConformVolumeDialog() = default; - -void ConformVolumeDialog::setupConnections() -{ - connect(m_ui->conformingVolume, - QOverload::of(&QComboBox::currentIndexChanged), this, - &ConformVolumeDialog::updateConformToLabel); -} - -void ConformVolumeDialog::updateConformToLabel() -{ - auto current = m_ui->conformingVolume->currentText(); - for (const auto* volume : m_volumes) { - if (current != volume->label()) { - m_ui->conformToVolumeLabel->setText(volume->label()); - break; - } - } -} - -void ConformVolumeDialog::setVolumes(QList volumes) -{ - m_volumes = volumes; - - // Set up the combo box options - m_ui->conformingVolume->clear(); - for (const auto* volume : volumes) { - m_ui->conformingVolume->addItem(volume->label()); - } -} - -DataSource* ConformVolumeDialog::selectedVolume() -{ - auto selectedIndex = m_ui->conformingVolume->currentIndex(); - return m_volumes[selectedIndex]; -} - -} // namespace tomviz diff --git a/tomviz/ConformVolumeDialog.h b/tomviz/ConformVolumeDialog.h deleted file mode 100644 index e7c1fdd73..000000000 --- a/tomviz/ConformVolumeDialog.h +++ /dev/null @@ -1,41 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizConformVolumeDialog_h -#define tomvizConformVolumeDialog_h - -#include -#include - -namespace Ui { -class ConformVolumeDialog; -} - -namespace tomviz { - -class DataSource; - -class ConformVolumeDialog : public QDialog -{ - Q_OBJECT - -public: - ConformVolumeDialog(QWidget* parent = nullptr); - ~ConformVolumeDialog() override; - - void setupConnections(); - - void setVolumes(QList volumes); - DataSource* selectedVolume(); - -private: - void updateConformToLabel(); - - QList m_volumes; - - Q_DISABLE_COPY(ConformVolumeDialog) - QScopedPointer m_ui; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/ConformVolumeDialog.ui b/tomviz/ConformVolumeDialog.ui deleted file mode 100644 index 3511d8319..000000000 --- a/tomviz/ConformVolumeDialog.ui +++ /dev/null @@ -1,123 +0,0 @@ - - - ConformVolumeDialog - - - - 0 - 0 - 460 - 268 - - - - Conform Volume - - - - - - Conforming volume: - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - The conforming volume will be resampled and reshaped so that the size, shape, extents, and origin of the data will match the volume it is conforming to. - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - true - - - - - - - - - - will be conformed to: - - - - - - - - 75 - true - - - - TextLabel - - - Qt::AlignCenter - - - - - - - - - buttonBox - accepted() - ConformVolumeDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - ConformVolumeDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/tomviz/ConformVolumeReaction.cxx b/tomviz/ConformVolumeReaction.cxx deleted file mode 100644 index d8e1751ca..000000000 --- a/tomviz/ConformVolumeReaction.cxx +++ /dev/null @@ -1,137 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ConformVolumeReaction.h" - -#include "vtkImageChangeInformation.h" -#include "vtkImageData.h" -#include "vtkImageResize.h" -#include "vtkNew.h" -#include "vtkSmartPointer.h" - -#include "ConformVolumeDialog.h" -#include "legacy/DataSource.h" -#include "LoadDataReaction.h" - -#include "pipeline/PortData.h" -#include "pipeline/PortType.h" -#include "pipeline/SourceNode.h" -#include "pipeline/data/VolumeData.h" - -namespace tomviz { - -ConformVolumeReaction::ConformVolumeReaction(QAction* parentObject) - : pqReaction(parentObject) -{ - updateEnableState(); -} - -void ConformVolumeReaction::onTriggered() -{ - // Check which volume should be the conforming one - ConformVolumeDialog dialog; - dialog.setVolumes(m_dataSources.values()); - - if (dialog.exec() == QDialog::Rejected) { - return; - } - - m_conformingVolume = dialog.selectedVolume(); - - auto* newSource = createConformedVolume(); - if (newSource) { - auto* source = new pipeline::SourceNode(); - source->setLabel("Conformed Volume"); - source->addOutput("volume", pipeline::PortType::ImageData); - vtkSmartPointer img = newSource->imageData(); - auto vol = std::make_shared(img); - vol->setLabel("Conformed Volume"); - source->setOutputData( - "volume", - pipeline::PortData(vol, pipeline::PortType::ImageData)); - LoadDataReaction::sourceNodeAdded(source); - delete newSource; // Clean up the temporary DataSource - } -} - -void ConformVolumeReaction::updateDataSources(QSet sources) -{ - m_dataSources = sources; - updateEnableState(); -} - -void ConformVolumeReaction::updateEnableState() -{ - updateVisibleState(); -} - -void ConformVolumeReaction::updateVisibleState() -{ - parentAction()->setVisible(false); - - if (m_dataSources.size() != 2) { - return; - } - - QList sourceList = m_dataSources.values(); - // Check that both DataSources are volumes - for (auto ds : m_dataSources) { - if (ds->type() != DataSource::Volume) { - return; - } - } - - // Make sure the names are not the same... - if (sourceList[0]->label() == sourceList[1]->label()) { - return; - } - - parentAction()->setVisible(true); -} - -DataSource* ConformVolumeReaction::createConformedVolume() -{ - if (m_dataSources.size() != 2 || - !m_dataSources.contains(m_conformingVolume)) { - return nullptr; - } - - auto* conformingVolume = m_conformingVolume; - - DataSource* conformToVolume = nullptr; - for (auto it = m_dataSources.cbegin(); it != m_dataSources.cend(); ++it) { - if (*it != conformingVolume) { - conformToVolume = *it; - break; - } - } - - if (!conformToVolume) { - return nullptr; - } - - // Begin the volume conforming - vtkNew resize; - resize->SetInputData(conformingVolume->imageData()); - resize->SetOutputDimensions(conformToVolume->imageData()->GetDimensions()); - resize->Update(); - - vtkNew changeInfo; - changeInfo->SetInputData(resize->GetOutputDataObject(0)); - changeInfo->SetInformationInputData(conformToVolume->imageData()); - changeInfo->Update(); - - auto* output = vtkImageData::SafeDownCast(changeInfo->GetOutputDataObject(0)); - if (!output) { - return nullptr; - } - - // TODO: Fully migrate ConformVolumeReaction to use SourceNode - auto* newSource = new DataSource(output); - newSource->setFileName("Conformed Volume"); - // Make the display position match as well - newSource->setDisplayPosition(conformToVolume->displayPosition()); - return newSource; -} - -} // namespace tomviz diff --git a/tomviz/ConformVolumeReaction.h b/tomviz/ConformVolumeReaction.h deleted file mode 100644 index 210e780ce..000000000 --- a/tomviz/ConformVolumeReaction.h +++ /dev/null @@ -1,40 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizConformVolumeReaction_h -#define tomvizConformVolumeReaction_h - -#include - -#include - -namespace tomviz { - -class DataSource; - -class ConformVolumeReaction : pqReaction -{ - Q_OBJECT - -public: - ConformVolumeReaction(QAction* action); - -public slots: - void updateDataSources(QSet); - -protected: - void onTriggered() override; - void updateEnableState() override; - void updateVisibleState(); - - DataSource* createConformedVolume(); - -private: - Q_DISABLE_COPY(ConformVolumeReaction) - - QSet m_dataSources; - DataSource* m_conformingVolume = nullptr; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/DataBrokerSaveReaction.cxx b/tomviz/DataBrokerSaveReaction.cxx index af8a8eb49..b5690fa19 100644 --- a/tomviz/DataBrokerSaveReaction.cxx +++ b/tomviz/DataBrokerSaveReaction.cxx @@ -2,17 +2,10 @@ It is released under the 3-Clause BSD License, see "LICENSE". */ #include "DataBrokerSaveReaction.h" -#include "DataBrokerSaveDialog.h" -#include "legacy/DataSource.h" -#include "GenericHDF5Format.h" -#include "LoadDataReaction.h" #include "Utilities.h" -#include - #include -#include namespace tomviz { @@ -39,61 +32,9 @@ void DataBrokerSaveReaction::setDataBrokerInstalled(bool installed) void DataBrokerSaveReaction::saveData() { - auto dataBroker = new DataBroker(tomviz::mainWidget()); - DataBrokerSaveDialog dialog(dataBroker, tomviz::mainWidget()); - - if (dialog.exec() == QDialog::Accepted) { - auto name = dialog.name(); - - // TODO: migrate to new pipeline - DataSource* ds = nullptr; // was: 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(); - }); - } + // TODO: migrate to new pipeline. Export to DataBroker is not yet supported + // with the new pipeline; the action is disabled in the constructor. + qWarning() << "Export to DataBroker is not supported in the new pipeline."; } } // namespace tomviz diff --git a/tomviz/DataExchangeFormat.cxx b/tomviz/DataExchangeFormat.cxx index 7dab208e8..a94e738f4 100644 --- a/tomviz/DataExchangeFormat.cxx +++ b/tomviz/DataExchangeFormat.cxx @@ -3,7 +3,6 @@ #include "DataExchangeFormat.h" -#include "legacy/DataSource.h" #include "GenericHDF5Format.h" #include "Utilities.h" @@ -44,66 +43,6 @@ bool DataExchangeFormat::read(const std::string& fileName, vtkImageData* image, return readDataSet(fileName, path, image, options); } -bool DataExchangeFormat::read(const std::string& fileName, - DataSource* dataSource, - const QVariantMap& options) -{ - vtkNew image; - if (!read(fileName, image, options)) { - std::cerr << "Failed to read data in: " + fileName + "\n"; - return false; - } - - dataSource->setData(image); - - // Use the same strides and volume bounds for the dark and white data, - // except for the tilt axis. - QVariantMap darkWhiteOptions = options; - int strides[3]; - int bs[6]; - dataSource->subsampleStrides(strides); - dataSource->subsampleVolumeBounds(bs); - - QVariantList stridesList = { 1, strides[1], strides[2] }; - QVariantList boundsList = { 0, 1, bs[2], bs[3], bs[4], bs[5] }; - - darkWhiteOptions["subsampleStrides"] = stridesList; - darkWhiteOptions["subsampleVolumeBounds"] = boundsList; - darkWhiteOptions["askForSubsample"] = false; - - // Read in the dark and white image data as well - vtkNew darkImage, whiteImage; - readDark(fileName, darkImage, darkWhiteOptions); - if (darkImage->GetPointData()->GetNumberOfArrays() != 0) - dataSource->setDarkData(std::move(darkImage)); - - readWhite(fileName, whiteImage, darkWhiteOptions); - if (whiteImage->GetPointData()->GetNumberOfArrays() != 0) - dataSource->setWhiteData(std::move(whiteImage)); - - QVector angles = readTheta(fileName, options); - - if (angles.isEmpty()) { - // Re-order the data to Fortran ordering - GenericHDF5Format::reorderData(image, ReorderMode::CToFortran); - GenericHDF5Format::reorderData(dataSource->darkData(), - ReorderMode::CToFortran); - GenericHDF5Format::reorderData(dataSource->whiteData(), - ReorderMode::CToFortran); - } else { - // No re-order needed. Just re-label the axes. - relabelXAndZAxes(image); - relabelXAndZAxes(dataSource->darkData()); - relabelXAndZAxes(dataSource->whiteData()); - dataSource->setTiltAngles(angles); - dataSource->setType(DataSource::TiltSeries); - } - - dataSource->dataModified(); - - return true; -} - HDF5ReadResult DataExchangeFormat::readAll(const std::string& fileName, const QVariantMap& options) { @@ -188,101 +127,5 @@ QVector DataExchangeFormat::readTheta(const std::string& fileName, return GenericHDF5Format::readAngles(reader, path, options); } -namespace { - -bool writeData(h5::H5ReadWrite& writer, vtkImageData* image) -{ - vtkNew permutedImage; - if (DataSource::hasTiltAngles(image)) { - // No deep copies of data needed. Just re-label the axes. - permutedImage->ShallowCopy(image); - relabelXAndZAxes(permutedImage); - } else { - // Need to re-order to C ordering before writing - GenericHDF5Format::reorderData(image, permutedImage, - ReorderMode::FortranToC); - } - - // Assume /exchange already exists - return GenericHDF5Format::writeVolume(writer, "/exchange", "data", - permutedImage); -} - -bool writeExtraData(h5::H5ReadWrite& writer, vtkImageData* image, - const std::string& path, const std::string& name, - bool isTiltSeries = false) -{ - vtkNew permutedImage; - if (isTiltSeries) { - // No deep copying needed. Just re-label the axes. - permutedImage->ShallowCopy(image); - relabelXAndZAxes(permutedImage); - } else { - GenericHDF5Format::reorderData(image, permutedImage, - ReorderMode::FortranToC); - } - - // Assume /exchange already exists - return GenericHDF5Format::writeVolume(writer, path, name, permutedImage); -} - -bool writeDark(h5::H5ReadWrite& writer, vtkImageData* image, - bool isTiltSeries = false) -{ - return writeExtraData(writer, image, "/exchange", "data_dark", isTiltSeries); -} - -bool writeWhite(h5::H5ReadWrite& writer, vtkImageData* image, - bool isTiltSeries = false) -{ - return writeExtraData(writer, image, "/exchange", "data_white", isTiltSeries); -} - -bool writeTheta(h5::H5ReadWrite& writer, vtkImageData* image) -{ - QVector angles = DataSource::getTiltAngles(image); - - if (angles.isEmpty()) - return false; - - // Assume /exchange already exists - return writer.writeData("/exchange", "theta", { static_cast(angles.size()) }, - angles.data()); -} - -} // namespace - -bool DataExchangeFormat::write(const std::string& fileName, DataSource* source) -{ - using h5::H5ReadWrite; - H5ReadWrite::OpenMode mode = H5ReadWrite::OpenMode::WriteOnly; - H5ReadWrite writer(fileName, mode); - - // Create a "/exchange" group - writer.createGroup("/exchange"); - - auto t = source->producer(); - auto image = vtkImageData::SafeDownCast(t->GetOutputDataObject(0)); - if (!writeData(writer, image)) - return false; - - bool isTiltSeries = source->hasTiltAngles(); - - if (source->darkData()) { - if (!writeDark(writer, source->darkData(), isTiltSeries)) - return false; - } - - if (source->whiteData()) { - if (!writeWhite(writer, source->whiteData(), isTiltSeries)) - return false; - } - - if (isTiltSeries) { - return writeTheta(writer, image); - } - - return true; -} } // namespace tomviz diff --git a/tomviz/DataExchangeFormat.h b/tomviz/DataExchangeFormat.h index 842e8ddbc..fc9c5d93d 100644 --- a/tomviz/DataExchangeFormat.h +++ b/tomviz/DataExchangeFormat.h @@ -14,24 +14,15 @@ class vtkImageData; namespace tomviz { -class DataSource; - class DataExchangeFormat { public: // This will only read /exchange/data, nothing else bool read(const std::string& fileName, vtkImageData* data, const QVariantMap& options = QVariantMap()); - // This will read the data as well as dark, white, and the - // theta angles, and it will swap x and z for tilt series. - bool read(const std::string& fileName, DataSource* source, - const QVariantMap& options = QVariantMap()); // Read everything into an HDF5ReadResult (no DataSource needed) HDF5ReadResult readAll(const std::string& fileName, const QVariantMap& options = QVariantMap()); - // A data source is required for writing - bool write(const std::string& fileName, DataSource* source); - private: // Read the dark dataset into the image data bool readDark(const std::string& fileName, vtkImageData* data, diff --git a/tomviz/DuplicateModuleReaction.cxx b/tomviz/DuplicateModuleReaction.cxx deleted file mode 100644 index 299022a30..000000000 --- a/tomviz/DuplicateModuleReaction.cxx +++ /dev/null @@ -1,62 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "DuplicateModuleReaction.h" - -#include "ActiveObjects.h" -#include "legacy/modules/Module.h" -#include "legacy/modules/ModuleFactory.h" -#include "legacy/modules/ModuleManager.h" - -#include - -namespace tomviz { - -DuplicateModuleReaction::DuplicateModuleReaction(QAction* action) - : pqReaction(action) -{ - // TODO: migrate to new pipeline - // was: connected to ActiveObjects::moduleChanged to update enable state - parentAction()->setEnabled(false); -} - -void DuplicateModuleReaction::updateEnableState() -{ - // TODO: migrate to new pipeline - // was: enabled when ActiveObjects::instance().activeModule() != nullptr - parentAction()->setEnabled(false); -} - -void DuplicateModuleReaction::onTriggered() -{ - // TODO: migrate to new pipeline - // was: auto module = ActiveObjects::instance().activeModule(); - Module* module = nullptr; - if (!module) { - return; - } - auto dataSource = module->dataSource(); - auto operatorResult = module->operatorResult(); - auto moleculeSource = module->moleculeSource(); - auto view = ActiveObjects::instance().activeView(); - auto moduleType = ModuleFactory::moduleType(module); - // Copy the module - Module* copy; - if (ModuleFactory::moduleApplicable(moduleType, dataSource, view)) { - copy = ModuleFactory::createModule(moduleType, dataSource, view); - } else if (ModuleFactory::moduleApplicable(moduleType, moleculeSource, - view)) { - copy = ModuleFactory::createModule(moduleType, moleculeSource, view); - } else { - copy = ModuleFactory::createModule(moduleType, operatorResult, view); - } - - if (copy) { - // Copy its settings - QJsonObject json = module->serialize(); - copy->deserialize(json); - ModuleManager::instance().addModule(copy); - } -} - -} // end namespace tomviz diff --git a/tomviz/DuplicateModuleReaction.h b/tomviz/DuplicateModuleReaction.h deleted file mode 100644 index 376494163..000000000 --- a/tomviz/DuplicateModuleReaction.h +++ /dev/null @@ -1,30 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizDuplicateModuleReaction_h -#define tomvizDuplicateModuleReaction_h - -#include - -namespace tomviz { - -/// SaveDataReaction handles the "Save Data" action in tomviz. On trigger, -/// this will save the data file. -class DuplicateModuleReaction : public pqReaction -{ - Q_OBJECT - -public: - DuplicateModuleReaction(QAction* parentAction); - -protected: - /// Called when the data changes to enable/disable the menu item - void updateEnableState() override; - - /// Called when the action is triggered. - void onTriggered() override; -}; - -} // namespace tomviz - -#endif diff --git a/tomviz/EmdFormat.cxx b/tomviz/EmdFormat.cxx index b6b8c820a..3d4cb53f1 100644 --- a/tomviz/EmdFormat.cxx +++ b/tomviz/EmdFormat.cxx @@ -3,7 +3,7 @@ #include "EmdFormat.h" -#include "legacy/DataSource.h" +#include "pipeline/data/VolumeData.h" #include "GenericHDF5Format.h" #include "Utilities.h" @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -155,8 +156,9 @@ bool EmdFormat::readNode(h5::H5ReadWrite& reader, const std::string& emdNode, } else { // No deep copying of the data needed. Just relabel the X and Z axes. relabelXAndZAxes(image); - DataSource::setTiltAngles(image, angles); - DataSource::setType(image, DataSource::TiltSeries); + pipeline::VolumeData::setTiltAngles(image, angles); + pipeline::VolumeData::setType( + image, pipeline::VolumeData::DataType::TiltSeries); } // /data carries the first scalar in insertion order; the true @@ -180,18 +182,13 @@ bool EmdFormat::readNode(h5::H5ReadWrite& reader, const std::string& emdNode, for (auto& id : scanIdsData) { scanIDs.push_back(id); } - DataSource::setScanIDs(image, scanIDs); + pipeline::VolumeData::setScanIds(image, scanIDs); } } return true; } -bool EmdFormat::write(const std::string& fileName, DataSource* source) -{ - return write(fileName, source->imageData()); -} - bool EmdFormat::write(const std::string& fileName, vtkImageData* image) { using h5::H5ReadWrite; @@ -216,10 +213,10 @@ bool EmdFormat::writeNode(h5::H5ReadWrite& writer, const std::string& path, writer.setAttribute(path, "emd_group_type", 1u); // See if we have tilt angles - auto hasTiltAngles = DataSource::hasTiltAngles(image); + auto hasTiltAngles = pipeline::VolumeData::hasTiltAngles(image); vtkNew permutedImage; - if (DataSource::hasTiltAngles(image)) { + if (pipeline::VolumeData::hasTiltAngles(image)) { // No deep copies of data needed. Just re-label the axes. permutedImage->ShallowCopy(image); relabelXAndZAxes(permutedImage); @@ -248,7 +245,7 @@ bool EmdFormat::writeNode(h5::H5ReadWrite& writer, const std::string& path, std::vector imageDimDataZ(dimensions[2]); if (hasTiltAngles) { - auto angles = DataSource::getTiltAngles(permutedImage); + auto angles = pipeline::VolumeData::getTiltAngles(permutedImage); imageDimDataX.reserve(angles.size()); for (int i = 0; i < angles.size(); ++i) { imageDimDataX[i] = static_cast(angles[i]); @@ -296,8 +293,8 @@ bool EmdFormat::writeNode(h5::H5ReadWrite& writer, const std::string& path, writeExtraScalars(writer, path, permutedImage); // Write scan IDs if present - if (DataSource::hasScanIDs(image)) { - auto scanIDs = DataSource::getScanIDs(image); + if (pipeline::VolumeData::hasScanIds(image)) { + auto scanIDs = pipeline::VolumeData::getScanIds(image); std::vector scanIdsVec(scanIDs.begin(), scanIDs.end()); std::vector dims(1, static_cast(scanIdsVec.size())); writer.writeData(path, "scan_ids", dims, scanIdsVec); diff --git a/tomviz/EmdFormat.h b/tomviz/EmdFormat.h index e8978b520..8b727a401 100644 --- a/tomviz/EmdFormat.h +++ b/tomviz/EmdFormat.h @@ -16,14 +16,11 @@ class H5ReadWrite; namespace tomviz { -class DataSource; - class EmdFormat { public: static bool read(const std::string& fileName, vtkImageData* data, const QVariantMap& options = QVariantMap()); - static bool write(const std::string& fileName, DataSource* source); static bool write(const std::string& fileName, vtkImageData* image); // Read EMD data from a specified node in the HDF5 file diff --git a/tomviz/ExportDataReaction.h b/tomviz/ExportDataReaction.h deleted file mode 100644 index 5266537d7..000000000 --- a/tomviz/ExportDataReaction.h +++ /dev/null @@ -1,43 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizExportDataReaction_h -#define tomvizExportDataReaction_h - -#include - -class vtkImageData; -class vtkSMSourceProxy; - -namespace tomviz { -class Module; - -/// ExportDataReaction handles the "Export as ..." action in tomviz. On trigger, -/// this will save the data from the active module (or the module that is set) -/// to a file -class ExportDataReaction : public pqReaction -{ - Q_OBJECT - -public: - ExportDataReaction(QAction* parentAction, Module* module = nullptr); - - /// Save the file - bool exportData(const QString& filename); - bool exportColoredSlice(vtkImageData* imageData, vtkSMSourceProxy* writer, - const QString& filename); - -protected: - /// Called when the data changes to enable/disable the menu item - void updateEnableState() override; - - /// Called when the action is triggered. - void onTriggered() override; - -private: - Module* m_module; - - Q_DISABLE_COPY(ExportDataReaction) -}; -} // namespace tomviz -#endif diff --git a/tomviz/FxiFormat.cxx b/tomviz/FxiFormat.cxx index 67613a1f3..bd8064639 100644 --- a/tomviz/FxiFormat.cxx +++ b/tomviz/FxiFormat.cxx @@ -3,7 +3,6 @@ #include "FxiFormat.h" -#include "legacy/DataSource.h" #include "GenericHDF5Format.h" #include "Utilities.h" @@ -44,68 +43,6 @@ bool FxiFormat::read(const std::string& fileName, vtkImageData* image, return readDataSet(fileName, path, image, options); } -bool FxiFormat::read(const std::string& fileName, DataSource* dataSource, - const QVariantMap& options) -{ - vtkNew image; - if (!read(fileName, image, options)) { - std::cerr << "Failed to read data in: " + fileName + "\n"; - return false; - } - - dataSource->setData(image); - - // Use the same strides and volume bounds for the dark and white data, - // except for the tilt axis. - QVariantMap darkWhiteOptions = options; - int strides[3]; - int bs[6]; - dataSource->subsampleStrides(strides); - dataSource->subsampleVolumeBounds(bs); - - QVariantList stridesList = { 1, strides[1], strides[2] }; - QVariantList boundsList = { 0, 1, bs[2], bs[3], bs[4], bs[5] }; - - darkWhiteOptions["subsampleStrides"] = stridesList; - darkWhiteOptions["subsampleVolumeBounds"] = boundsList; - darkWhiteOptions["askForSubsample"] = false; - - // Read in the dark and white image data as well - vtkNew darkImage, whiteImage; - readDark(fileName, darkImage, darkWhiteOptions); - if (darkImage->GetPointData()->GetNumberOfArrays() != 0) - dataSource->setDarkData(std::move(darkImage)); - - readWhite(fileName, whiteImage, darkWhiteOptions); - if (whiteImage->GetPointData()->GetNumberOfArrays() != 0) - dataSource->setWhiteData(std::move(whiteImage)); - - QVector angles = readTheta(fileName, options); - - if (angles.isEmpty()) { - // Re-order the data to Fortran ordering - GenericHDF5Format::reorderData(image, ReorderMode::CToFortran); - GenericHDF5Format::reorderData(dataSource->darkData(), - ReorderMode::CToFortran); - GenericHDF5Format::reorderData(dataSource->whiteData(), - ReorderMode::CToFortran); - } else { - // No re-order needed. Just re-label the axes. - relabelXAndZAxes(image); - relabelXAndZAxes(dataSource->darkData()); - relabelXAndZAxes(dataSource->whiteData()); - dataSource->setTiltAngles(angles); - dataSource->setType(DataSource::TiltSeries); - } - - auto metadata = readMetadata(fileName, options); - dataSource->setMetadata(metadata); - - dataSource->dataModified(); - - return true; -} - HDF5ReadResult FxiFormat::readAll(const std::string& fileName, const QVariantMap& options) { diff --git a/tomviz/FxiFormat.h b/tomviz/FxiFormat.h index 5b9ad6d14..26b78ee4d 100644 --- a/tomviz/FxiFormat.h +++ b/tomviz/FxiFormat.h @@ -15,8 +15,6 @@ class vtkImageData; namespace tomviz { -class DataSource; - /** * Format used by the FXI beamline at BNL. */ @@ -27,10 +25,6 @@ class FxiFormat // This will only read /exchange/data, nothing else bool read(const std::string& fileName, vtkImageData* data, const QVariantMap& options = QVariantMap()); - // This will read the data as well as dark, white, and the - // theta angles, and it will swap x and z for tilt series. - bool read(const std::string& fileName, DataSource* source, - const QVariantMap& options = QVariantMap()); // Read everything into an HDF5ReadResult (no DataSource needed) HDF5ReadResult readAll(const std::string& fileName, const QVariantMap& options = QVariantMap()); diff --git a/tomviz/GenericHDF5Format.cxx b/tomviz/GenericHDF5Format.cxx index 157ff8dba..4953f52b9 100644 --- a/tomviz/GenericHDF5Format.cxx +++ b/tomviz/GenericHDF5Format.cxx @@ -3,8 +3,9 @@ #include "GenericHDF5Format.h" +#include "pipeline/data/VolumeData.h" + #include -#include "legacy/DataSource.h" #include #include @@ -240,12 +241,12 @@ bool GenericHDF5Format::addScalarArray(h5::H5ReadWrite& reader, // Set up the strides and counts int strides[3] = { 1, 1, 1 }; size_t start[3], counts[3]; - if (DataSource::wasSubsampled(image)) { + if (pipeline::VolumeData::wasSubsampled(image)) { // If the main image was subsampled, we need to use the same // subsampling for the scalars - DataSource::subsampleStrides(image, strides); + pipeline::VolumeData::subsampleStrides(image, strides); int bs[6]; - DataSource::subsampleVolumeBounds(image, bs); + pipeline::VolumeData::subsampleVolumeBounds(image, bs); for (int i = 0; i < 3; ++i) { start[i] = static_cast(bs[i * 2]); @@ -270,7 +271,7 @@ bool GenericHDF5Format::addScalarArray(h5::H5ReadWrite& reader, for (int i = 0; i < 3; ++i) { if (vtkCounts[i] != imageDims[i]) { std::stringstream ss; - if (DataSource::wasSubsampled(image)) { + if (pipeline::VolumeData::wasSubsampled(image)) { ss << "Subsampled dimensions of "; } else { ss << "Dimensions of "; @@ -353,8 +354,8 @@ bool GenericHDF5Format::readVolume(h5::H5ReadWrite& reader, QVariantList list = options["subsampleVolumeBounds"].toList(); for (int i = 0; i < list.size() && i < 6; ++i) bs[i] = list[i].toInt(); - DataSource::setWasSubsampled(image, true); - DataSource::setSubsampleVolumeBounds(image, bs); + pipeline::VolumeData::setWasSubsampled(image, true); + pipeline::VolumeData::setSubsampleVolumeBounds(image, bs); } else { // Set it to the defaults for (int i = 0; i < 3; ++i) { @@ -372,8 +373,8 @@ bool GenericHDF5Format::readVolume(h5::H5ReadWrite& reader, strides[i] = 1; } - DataSource::setWasSubsampled(image, true); - DataSource::setSubsampleStrides(image, strides); + pipeline::VolumeData::setWasSubsampled(image, true); + pipeline::VolumeData::setSubsampleStrides(image, strides); } bool askForSubsample = false; @@ -402,12 +403,12 @@ bool GenericHDF5Format::readVolume(h5::H5ReadWrite& reader, Hdf5SubsampleWidget widget(dimensions, size); layout.addWidget(&widget); - if (DataSource::wasSubsampled(image)) { + if (pipeline::VolumeData::wasSubsampled(image)) { // If it was previously subsampled, start with the previous values - DataSource::subsampleStrides(image, strides); + pipeline::VolumeData::subsampleStrides(image, strides); widget.setStrides(strides); - DataSource::subsampleVolumeBounds(image, bs); + pipeline::VolumeData::subsampleVolumeBounds(image, bs); widget.setBounds(bs); } @@ -428,9 +429,9 @@ bool GenericHDF5Format::readVolume(h5::H5ReadWrite& reader, widget.bounds(bs); widget.strides(strides); - DataSource::setWasSubsampled(image, true); - DataSource::setSubsampleStrides(image, strides); - DataSource::setSubsampleVolumeBounds(image, bs); + pipeline::VolumeData::setWasSubsampled(image, true); + pipeline::VolumeData::setSubsampleStrides(image, strides); + pipeline::VolumeData::setSubsampleVolumeBounds(image, bs); } // Do one final check to make sure all bounds are valid @@ -450,7 +451,7 @@ bool GenericHDF5Format::readVolume(h5::H5ReadWrite& reader, if (changed) { // Update the volume bounds that were used - DataSource::setSubsampleVolumeBounds(image, bs); + pipeline::VolumeData::setSubsampleVolumeBounds(image, bs); } // Set up the strides and counts @@ -620,8 +621,8 @@ bool GenericHDF5Format::read(const std::string& fileName, vtkImageData* image, } else { // No deep copying of the data needed. Just relabel the X and Z axes. relabelXAndZAxes(image); - DataSource::setTiltAngles(image, angles); - DataSource::setType(image, DataSource::TiltSeries); + pipeline::VolumeData::setTiltAngles(image, angles); + pipeline::VolumeData::setType(image, pipeline::VolumeData::DataType::TiltSeries); } // Made it to the end... diff --git a/tomviz/ImageStackDialog.cxx b/tomviz/ImageStackDialog.cxx index 17e0a13c9..8b81d8e6c 100644 --- a/tomviz/ImageStackDialog.cxx +++ b/tomviz/ImageStackDialog.cxx @@ -59,10 +59,8 @@ ImageStackDialog::ImageStackDialog(QWidget* parent) m_ui->loadedContainer->hide(); m_ui->stackTypeCombo->setDisabled(true); - m_ui->stackTypeCombo->insertItem(DataSource::DataSourceType::Volume, - QString("Volume")); - m_ui->stackTypeCombo->insertItem(DataSource::DataSourceType::TiltSeries, - QString("Tilt Series")); + m_ui->stackTypeCombo->insertItem(0, QString("Volume")); + m_ui->stackTypeCombo->insertItem(1, QString("Tilt Series")); // Due to an overloaded signal I am force to use static_cast here. QObject::connect(m_ui->stackTypeCombo, static_cast( @@ -100,12 +98,13 @@ void ImageStackDialog::setStackSummary(const QList& summary, setAcceptDrops(false); } -void ImageStackDialog::setStackType(const DataSource::DataSourceType& stackType) +void ImageStackDialog::setStackType(pipeline::PortType stackType) { if (m_stackType != stackType) { m_stackType = stackType; emit stackTypeChanged(m_stackType); - m_ui->stackTypeCombo->setCurrentIndex(m_stackType); + m_ui->stackTypeCombo->setCurrentIndex( + m_stackType == pipeline::PortType::TiltSeries ? 1 : 0); } } @@ -171,13 +170,13 @@ void ImageStackDialog::processFiles(const QStringList& fileNames) bool isVolume = false; bool isTilt = false; bool isNumbered = false; - DataSource::DataSourceType stackType = DataSource::DataSourceType::Volume; + pipeline::PortType stackType = pipeline::PortType::Volume; isVolume = detectVolume(fNames, summary); if (!isVolume) { isTilt = detectTilt(fNames, summary); if (isTilt) { - stackType = DataSource::DataSourceType::TiltSeries; + stackType = pipeline::PortType::TiltSeries; } else { isNumbered = detectVolume(fNames, summary, false); if (!isNumbered) { @@ -389,7 +388,7 @@ QList ImageStackDialog::getStackSummary() const return m_summary; } -DataSource::DataSourceType ImageStackDialog::getStackType() const +pipeline::PortType ImageStackDialog::getStackType() const { return m_stackType; } @@ -407,10 +406,10 @@ void ImageStackDialog::onImageToggled(int row, bool value) void ImageStackDialog::onStackTypeChanged(int stackType) { - if (stackType == DataSource::DataSourceType::Volume) { - setStackType(DataSource::DataSourceType::Volume); - } else if (stackType == DataSource::DataSourceType::TiltSeries) { - setStackType(DataSource::DataSourceType::TiltSeries); + if (stackType == 0) { + setStackType(pipeline::PortType::Volume); + } else if (stackType == 1) { + setStackType(pipeline::PortType::TiltSeries); } } diff --git a/tomviz/ImageStackDialog.h b/tomviz/ImageStackDialog.h index bc96c3f74..bb227a0b1 100644 --- a/tomviz/ImageStackDialog.h +++ b/tomviz/ImageStackDialog.h @@ -6,8 +6,8 @@ #include -#include "legacy/DataSource.h" #include "ImageStackModel.h" +#include "pipeline/PortType.h" #include @@ -26,11 +26,11 @@ class ImageStackDialog : public QDialog ~ImageStackDialog() override; void setStackSummary(const QList& summary, bool sort = true); - void setStackType(const DataSource::DataSourceType& stackType); + void setStackType(pipeline::PortType stackType); void processDirectory(const QString& path); void processFiles(const QStringList& fileNames); QList getStackSummary() const; - DataSource::DataSourceType getStackType() const; + pipeline::PortType getStackType() const; bool getImageViewerMode() const; public slots: @@ -43,7 +43,7 @@ public slots: signals: void summaryChanged(const QList&); - void stackTypeChanged(const DataSource::DataSourceType&); + void stackTypeChanged(pipeline::PortType); protected: void dragEnterEvent(QDragEnterEvent* event) override; @@ -53,7 +53,7 @@ public slots: QScopedPointer m_ui; QList m_summary; - DataSource::DataSourceType m_stackType; + pipeline::PortType m_stackType; ImageStackModel m_tableModel; void openFileDialog(int mode); bool detectVolume(QStringList fileNames, QList& summary, diff --git a/tomviz/ImageStackModel.cxx b/tomviz/ImageStackModel.cxx index 7c81cd5fd..0f53f38d4 100644 --- a/tomviz/ImageStackModel.cxx +++ b/tomviz/ImageStackModel.cxx @@ -86,9 +86,9 @@ QVariant ImageStackModel::headerData(int section, Qt::Orientation orientation, } else if (section == c_yCol) { return QString("Y"); } else if (section == c_posCol) { - if (m_stackType == DataSource::DataSourceType::Volume) { + if (m_stackType == pipeline::PortType::Volume) { return QString("Slice"); - } else if (m_stackType == DataSource::DataSourceType::TiltSeries) { + } else if (m_stackType == pipeline::PortType::TiltSeries) { return QString("Angle"); } } @@ -134,7 +134,7 @@ void ImageStackModel::onFilesInfoChanged(QList filesInfo) endResetModel(); } -void ImageStackModel::onStackTypeChanged(DataSource::DataSourceType stackType) +void ImageStackModel::onStackTypeChanged(pipeline::PortType stackType) { beginResetModel(); m_stackType = stackType; diff --git a/tomviz/ImageStackModel.h b/tomviz/ImageStackModel.h index b804d5f2c..00a8878ae 100644 --- a/tomviz/ImageStackModel.h +++ b/tomviz/ImageStackModel.h @@ -6,7 +6,7 @@ #include -#include "legacy/DataSource.h" +#include "pipeline/PortType.h" #include #include @@ -37,14 +37,14 @@ class ImageStackModel : public QAbstractTableModel public slots: void onFilesInfoChanged(QList filesInfo); - void onStackTypeChanged(DataSource::DataSourceType stackType); + void onStackTypeChanged(pipeline::PortType stackType); signals: void toggledSelected(int row, bool value); private: QList m_filesInfo; - DataSource::DataSourceType m_stackType = DataSource::DataSourceType::Volume; + pipeline::PortType m_stackType = pipeline::PortType::Volume; const int c_numCol = 5; const int c_checkCol = 0; const int c_fileCol = 1; diff --git a/tomviz/InterfaceBuilder.cxx b/tomviz/InterfaceBuilder.cxx deleted file mode 100644 index 5f0dba861..000000000 --- a/tomviz/InterfaceBuilder.cxx +++ /dev/null @@ -1,1415 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "InterfaceBuilder.h" - -#include "ActiveObjects.h" -#include "legacy/DataSource.h" -#include "DoubleSpinBox.h" -#include "legacy/modules/ModuleManager.h" -#include "SpinBox.h" -#include "Utilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using tomviz::DataSource; - -Q_DECLARE_METATYPE(DataSource*) - -namespace { - -// Templated query of QJsonValue type -template -bool isType(const QJsonValue&) -{ - return false; -} - -template <> -bool isType(const QJsonValue& value) -{ - return value.isDouble(); -} - -template <> -bool isType(const QJsonValue& value) -{ - return value.isDouble(); -} - -// Templated accessor of QJsonValue value -template -T getAs(const QJsonValue&) -{ - return 0; -} - -template <> -int getAs(const QJsonValue& value) -{ - int iValue = 0; - try { - iValue = value.toInt(); - } catch (...) { - qCritical() << "Could not get int from QJsonValue"; - } - return iValue; -} - -template <> -double getAs(const QJsonValue& value) -{ - double dValue = 0.0; - try { - dValue = value.toDouble(); - } catch (...) { - qCritical() << "Could not get double from QJsonValue"; - } - return dValue; -} - -template <> -QString getAs(const QJsonValue& value) -{ - QString strValue; - try { - strValue = value.toString(); - } catch (...) { - qCritical() << "Could not get QString from QJsonValue"; - } - return strValue; -} - -// Templated generation of numeric editing widgets. -template -QWidget* getNumericWidget(T, T, T, int, T) -{ - return nullptr; -} - -template <> -QWidget* getNumericWidget(int defaultValue, int rangeMin, int rangeMax, - int /*precision*/, int step) -{ - tomviz::SpinBox* spinBox = new tomviz::SpinBox(); - if (step == -1) { - spinBox->setSingleStep(1); - } else { - spinBox->setSingleStep(step); - } - spinBox->setMinimum(rangeMin); - spinBox->setMaximum(rangeMax); - spinBox->setValue(defaultValue); - return spinBox; -} - -template <> -QWidget* getNumericWidget(double defaultValue, double rangeMin, double rangeMax, - int precision, double step) -{ - tomviz::DoubleSpinBox* spinBox = new tomviz::DoubleSpinBox(); - if (step == -1) { - spinBox->setSingleStep(0.5); - } else { - spinBox->setSingleStep(step); - } - if (precision != -1) { - spinBox->setDecimals(precision); - } else { - spinBox->setDecimals(3); - } - spinBox->setMinimum(rangeMin); - spinBox->setMaximum(rangeMax); - spinBox->setValue(defaultValue); - return spinBox; -} - -template -bool isWidgetType(const QWidget* widget) { - return qobject_cast(widget) != nullptr; -} - -bool isWidgetNumeric(const QWidget* widget) { - return isWidgetType(widget) || isWidgetType(widget); -} - -template -auto changedSignal() -{ - // Get a generic symbol a widget has changed - if constexpr (std::is_same_v) { - return QOverload::of(&QComboBox::currentIndexChanged); - } else if constexpr (std::is_same_v) { - return &QCheckBox::toggled; - } else if constexpr (std::is_same_v) { - return QOverload::of(&QDoubleSpinBox::valueChanged); - } else if constexpr (std::is_same_v) { - return QOverload::of(&QSpinBox::valueChanged); - } else if constexpr (std::is_same_v) { - return &QLineEdit::textChanged; - } -} - -template -auto widgetValue(const T* w) -{ - // Get the widget value. - // This will be an int for QSpinBox, double for QDoubleSpinBox, - // string for QComboBox, etc. - if constexpr (std::is_same_v) { - return w->currentData().toString(); - } else if constexpr (std::is_same_v) { - return w->isChecked(); - } else if constexpr (std::is_same_v) { - return w->value(); - } else if constexpr (std::is_same_v) { - return w->value(); - } else if constexpr (std::is_same_v) { - return w->text(); - } -} - -void addBoolWidget(QGridLayout* layout, int row, QJsonObject& parameterNode) -{ - QJsonValueRef nameValue = parameterNode["name"]; - QJsonValueRef labelValue = parameterNode["label"]; - - if (nameValue.isUndefined()) { - QJsonDocument document(parameterNode); - qWarning() << QString("Parameter %1 has no name. Skipping.") - .arg(document.toJson().data()); - return; - } - - QLabel* label = new QLabel(nameValue.toString()); - if (!labelValue.isUndefined()) { - label->setText(labelValue.toString()); - } - layout->addWidget(label, row, 0, 1, 1); - - bool defaultValue = false; - if (parameterNode.contains("default")) { - QJsonValueRef defaultNode = parameterNode["default"]; - if (defaultNode.isBool()) { - defaultValue = defaultNode.toBool(); - } - } - QCheckBox* checkBox = new QCheckBox(); - checkBox->setObjectName(nameValue.toString()); - checkBox->setCheckState(defaultValue ? Qt::Checked : Qt::Unchecked); - label->setBuddy(checkBox); - layout->addWidget(checkBox, row, 1, 1, 1); -} - -template -void addNumericWidget(QGridLayout* layout, int row, QJsonObject& parameterNode, - DataSource* dataSource) -{ - QJsonValueRef nameValue = parameterNode["name"]; - QJsonValueRef labelValue = parameterNode["label"]; - - if (nameValue.isUndefined()) { - QJsonDocument document(parameterNode); - qWarning() << QString("Parameter %1 has no name. Skipping.") - .arg(document.toJson().data()); - return; - } - - QLabel* label = new QLabel(nameValue.toString()); - if (!labelValue.isUndefined()) { - label->setText(labelValue.toString()); - } - layout->addWidget(label, row, 0, 1, 1); - - std::vector defaultValues; - if (parameterNode.contains("default")) { - QJsonValueRef defaultNode = parameterNode["default"]; - if (isType(defaultNode)) { - defaultValues.push_back(getAs(defaultNode)); - } else if (defaultNode.isArray()) { - QJsonArray defaultArray = defaultNode.toArray(); - for (QJsonObject::size_type i = 0; i < defaultArray.size(); ++i) { - defaultValues.push_back(getAs(defaultArray[i])); - } - } - } else if (parameterNode.contains("data-default")) { - if (!dataSource) { - return; - } - QJsonValueRef defaultNode = parameterNode["data-default"]; - int extent[6]; - dataSource->getExtent(extent); - if (defaultNode.toString() == "num-voxels-x") { - defaultValues.push_back(extent[1] - extent[0] + 1); - } else if (defaultNode.toString() == "num-voxels-y") { - defaultValues.push_back(extent[3] - extent[2] + 1); - } else if (defaultNode.toString() == "num-voxels-z") { - defaultValues.push_back(extent[5] - extent[4] + 1); - } - } - - std::vector minValues(defaultValues.size(), std::numeric_limits::lowest()); - if (parameterNode.contains("minimum")) { - QJsonValueRef minNode = parameterNode["minimum"]; - if (isType(minNode)) { - minValues[0] = getAs(minNode); - } else if (minNode.isArray()) { - QJsonArray minArray = minNode.toArray(); - for (QJsonObject::size_type i = 0; i < minArray.size(); ++i) { - minValues[i] = getAs(minArray[i]); - } - } - } - - std::vector maxValues(defaultValues.size(), std::numeric_limits::max()); - if (parameterNode.contains("maximum")) { - QJsonValueRef maxNode = parameterNode["maximum"]; - if (isType(maxNode)) { - maxValues[0] = getAs(maxNode); - } else if (maxNode.isArray()) { - QJsonArray maxArray = maxNode.toArray(); - for (QJsonObject::size_type i = 0; i < maxArray.size(); ++i) { - maxValues[i] = getAs(maxArray[i]); - } - } - } - - int precision = -1; - if (parameterNode.contains("precision")) { - QJsonValueRef precNode = parameterNode["precision"]; - if (isType(precNode)) { - precision = getAs(precNode); - } - } - T step = -1; - if (parameterNode.contains("step")) { - QJsonValueRef stepNode = parameterNode["step"]; - if (isType(stepNode)) { - step = getAs(stepNode); - } - } - - QHBoxLayout* horizontalLayout = new QHBoxLayout(); - horizontalLayout->setContentsMargins(0, 0, 0, 0); - QWidget* horizontalWidget = new QWidget; - horizontalWidget->setLayout(horizontalLayout); - label->setBuddy(horizontalWidget); - layout->addWidget(horizontalWidget, row, 1, 1, 1); - - for (size_t i = 0; i < defaultValues.size(); ++i) { - QString name = nameValue.toString(); - if (defaultValues.size() > 1) { - // Multi-element parameters are named with the pattern 'basename#XXX' - // where 'basename' is the name of the parameter and 'XXX' is the - // element number. - name.append("#%1"); - name = name.arg(i, 3, 10, QLatin1Char('0')); - } - - QWidget* spinBox = getNumericWidget(defaultValues[i], minValues[i], - maxValues[i], precision, step); - spinBox->setObjectName(name); - horizontalLayout->addWidget(spinBox); - } -} - -void addEnumerationWidget(QGridLayout* layout, int row, - QJsonObject& parameterNode) -{ - QJsonValueRef nameValue = parameterNode["name"]; - QJsonValueRef labelValue = parameterNode["label"]; - - if (nameValue.isUndefined()) { - QJsonDocument document(parameterNode); - qWarning() << QString("Parameter %1 has no name. Skipping.") - .arg(document.toJson().data()); - return; - } - - QLabel* label = new QLabel(nameValue.toString()); - if (!labelValue.isUndefined()) { - label->setText(labelValue.toString()); - } - layout->addWidget(label, row, 0, 1, 1); - - QComboBox* comboBox = new QComboBox(); - comboBox->setObjectName(nameValue.toString()); - label->setBuddy(comboBox); - QJsonValueRef optionsNode = parameterNode["options"]; - if (!optionsNode.isUndefined()) { - QJsonArray optionsArray = optionsNode.toArray(); - for (QJsonObject::size_type i = 0; i < optionsArray.size(); ++i) { - QJsonObject optionNode = optionsArray[i].toObject(); - QString optionName = optionNode.keys()[0]; - QJsonValueRef optionValueNode = optionNode[optionName]; - QVariant optionValue; - if (isType(optionValueNode)) { - // Convert to an int if possible - optionValue = optionValueNode.toInt(); - } else { - // Otherwise, let it be whatever type it is... - optionValue = optionValueNode.toVariant(); - } - comboBox->addItem(optionName, optionValue); - } - } - - // Set the default if present - QJsonValueRef defaultNode = parameterNode["default"]; - if (!defaultNode.isUndefined()) { - if (isType(defaultNode)) { - comboBox->setCurrentIndex(getAs(defaultNode)); - } else if (defaultNode.isString()) { - // Find the data that matches, and set it - int defaultIndex = comboBox->findData(getAs(defaultNode)); - if (defaultIndex >= 0) { - comboBox->setCurrentIndex(defaultIndex); - } - } - } - - layout->addWidget(comboBox, row, 1, 1, 1); -} - -void addXYZHeaderWidget(QGridLayout* layout, int row, const QJsonValue&) -{ - QHBoxLayout* horizontalLayout = new QHBoxLayout; - horizontalLayout->setContentsMargins(0, 0, 0, 0); - QWidget* horizontalWidget = new QWidget; - horizontalWidget->setLayout(horizontalLayout); - layout->addWidget(horizontalWidget, row, 1, 1, 1); - - QLabel* xLabel = new QLabel("X"); - xLabel->setAlignment(Qt::AlignCenter); - horizontalLayout->addWidget(xLabel); - QLabel* yLabel = new QLabel("Y"); - yLabel->setAlignment(Qt::AlignCenter); - horizontalLayout->addWidget(yLabel); - QLabel* zLabel = new QLabel("Z"); - zLabel->setAlignment(Qt::AlignCenter); - horizontalLayout->addWidget(zLabel); -} - -void addPathWidget(QGridLayout* layout, int row, QJsonObject& pathNode) -{ - QHBoxLayout* horizontalLayout = new QHBoxLayout; - horizontalLayout->setContentsMargins(0, 0, 0, 0); - QWidget* horizontalWidget = new QWidget; - horizontalWidget->setLayout(horizontalLayout); - layout->addWidget(horizontalWidget, row, 1, 1, 1); - - QJsonValueRef typeValue = pathNode["type"]; - if (typeValue.isUndefined()) { - QJsonDocument document(pathNode); - qWarning() << QString("Parameter %1 has no type. Skipping.") - .arg(document.toJson().data()); - return; - } - QString type = typeValue.toString(); - - QJsonValueRef nameValue = pathNode["name"]; - if (nameValue.isUndefined()) { - QJsonDocument document(pathNode); - qWarning() << QString("Parameter %1 has no name. Skipping.") - .arg(document.toJson().data()); - return; - } - - QJsonValueRef labelValue = pathNode["label"]; - QLabel* label = new QLabel(nameValue.toString()); - label->setBuddy(horizontalWidget); - if (!labelValue.isUndefined()) { - label->setText(labelValue.toString()); - } - layout->addWidget(label, row, 0, 1, 1); - - QLineEdit* pathField = new QLineEdit(); - // Tag the line edit with the type, so we can distinguish it from other line - // edit uses ( such as in a QSpinBox ) - pathField->setProperty("type", type); - pathField->setObjectName(nameValue.toString()); - pathField->setMinimumWidth(500); - - // Set default if present - QJsonValueRef defaultNode = pathNode["default"]; - if (!defaultNode.isUndefined() && defaultNode.isString()) { - auto defaultValue = getAs(defaultNode); - pathField->setText(defaultValue); - } - - horizontalLayout->addWidget(pathField); - auto filter = pathNode["filter"].toString(); - - QPushButton* browseButton = new QPushButton("Browse"); - horizontalLayout->addWidget(browseButton); - QObject::connect(browseButton, &QPushButton::clicked, [type, pathField, filter]() { - // Determine the directory we should open the file browser at. - QString browseDir; - if (!pathField->text().isEmpty()) { - QFileInfo currentValue = QFileInfo(pathField->text()); - auto dir = currentValue.dir(); - if (dir.exists()) { - browseDir = dir.absolutePath(); - } - } - - // Now open the appropriate dialog to browse for a file or directory. - QString path; - if (type == "file") { - path = QFileDialog::getOpenFileName(tomviz::mainWidget(), "Select File", - browseDir, filter); - } else if (type == "save_file") { - path = QFileDialog::getSaveFileName(tomviz::mainWidget(), "Save File Path", - browseDir, filter); - } else { - path = QFileDialog::getExistingDirectory(tomviz::mainWidget(), - "Select Directory", browseDir); - } - - // If a path was selected update the line edit. - if (!path.isNull()) { - pathField->setText(path); - } - }); -} - -void addStringWidget(QGridLayout* layout, int row, QJsonObject& pathNode) -{ - QHBoxLayout* horizontalLayout = new QHBoxLayout; - horizontalLayout->setContentsMargins(0, 0, 0, 0); - QWidget* horizontalWidget = new QWidget; - horizontalWidget->setLayout(horizontalLayout); - layout->addWidget(horizontalWidget, row, 1, 1, 1); - - QJsonValueRef typeValue = pathNode["type"]; - if (typeValue.isUndefined()) { - QJsonDocument document(pathNode); - qWarning() << QString("Parameter %1 has no type. Skipping.") - .arg(document.toJson().data()); - return; - } - QString type = typeValue.toString(); - - QJsonValueRef nameValue = pathNode["name"]; - if (nameValue.isUndefined()) { - QJsonDocument document(pathNode); - qWarning() << QString("Parameter %1 has no name. Skipping.") - .arg(document.toJson().data()); - return; - } - - QJsonValueRef labelValue = pathNode["label"]; - QLabel* label = new QLabel(nameValue.toString()); - if (!labelValue.isUndefined()) { - label->setText(labelValue.toString()); - } - layout->addWidget(label, row, 0, 1, 1); - - QLineEdit* stringField = new QLineEdit(); - stringField->setProperty("type", type); - // Tag the line edit with the type, so we can distinguish it from other line - // edit uses ( such as in a QSpinBox ) - stringField->setProperty("type", type); - stringField->setObjectName(nameValue.toString()); - stringField->setMinimumWidth(500); - label->setBuddy(stringField); - horizontalLayout->addWidget(stringField); - - QJsonValueRef defaultNode = pathNode["default"]; - if (!defaultNode.isUndefined() && defaultNode.isString()) { - auto defaultValue = getAs(defaultNode); - stringField->setText(defaultValue); - } -} - -void addDatasetWidget(QGridLayout* layout, int row, QJsonObject& parameterNode) -{ - QJsonValueRef nameValue = parameterNode["name"]; - QJsonValueRef labelValue = parameterNode["label"]; - auto defaultId = parameterNode.value("default").toString(); - - if (nameValue.isUndefined()) { - QJsonDocument document(parameterNode); - qWarning() << QString("Parameter %1 has no name. Skipping.") - .arg(document.toJson().data()); - return; - } - - QLabel* labelWidget = new QLabel(nameValue.toString()); - if (!labelValue.isUndefined()) { - labelWidget->setText(labelValue.toString()); - } - layout->addWidget(labelWidget, row, 0, 1, 1); - - QComboBox* comboBox = new QComboBox(); - comboBox->setObjectName(nameValue.toString()); - labelWidget->setBuddy(comboBox); - QStringList addedLabels; - auto dataSources = - tomviz::ModuleManager::instance().allDataSourcesDepthFirst(); - auto labels = tomviz::ModuleManager::createUniqueLabels(dataSources); - auto defaultIndex = -1; - for (int i = 0; i < dataSources.size(); ++i) { - auto* dataSource = dataSources[i]; - if (dataSource->id() == defaultId) { - defaultIndex = i; - } - - auto label = labels[i]; - - QVariant data; - data.setValue(dataSource); - comboBox->addItem(label, data); - addedLabels.append(label); - } - - if (defaultIndex != -1) { - comboBox->setCurrentIndex(defaultIndex); - } - - layout->addWidget(comboBox, row, 1, 1, 1); -} - -void addSelectScalarsWidget(QGridLayout* layout, int row, - QJsonObject& parameterNode, - DataSource* dataSource) -{ - QJsonValueRef nameValue = parameterNode["name"]; - QJsonValueRef labelValue = parameterNode["label"]; - - if (nameValue.isUndefined()) { - QJsonDocument document(parameterNode); - qWarning() << QString("Parameter %1 has no name. Skipping.") - .arg(document.toJson().data()); - return; - } - - QString name = nameValue.toString(); - - QLabel* label = new QLabel(name); - if (!labelValue.isUndefined()) { - label->setText(labelValue.toString()); - } - layout->addWidget(label, row, 0, 1, 1); - - // Container widget - QWidget* container = new QWidget(); - container->setObjectName(name); - container->setProperty("type", "select_scalars"); - label->setBuddy(container); - - QVBoxLayout* vLayout = new QVBoxLayout(); - vLayout->setContentsMargins(0, 0, 0, 0); - container->setLayout(vLayout); - - // "Apply to all scalars" checkbox - bool showApplyAll = parameterNode.value("show_apply_all").toBool(true); - QCheckBox* applyAllCheckBox = new QCheckBox("Apply to all scalars"); - applyAllCheckBox->setObjectName(name + "_apply_all"); - applyAllCheckBox->setChecked(showApplyAll); - applyAllCheckBox->setVisible(showApplyAll); - vLayout->addWidget(applyAllCheckBox); - - // Checkable combo box for individual scalar selection - QComboBox* comboBox = new QComboBox(); - comboBox->setObjectName(name + "_combo"); - QStandardItemModel* model = new QStandardItemModel(comboBox); - comboBox->setModel(model); - comboBox->setEnabled(!showApplyAll); - - if (dataSource) { - QStringList scalars = dataSource->listScalars(); - for (const QString& scalar : scalars) { - QStandardItem* item = new QStandardItem(scalar); - item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); - item->setData(Qt::Checked, Qt::CheckStateRole); - model->appendRow(item); - } - - // Restore previous selection from "default" if present - QJsonValueRef defaultNode = parameterNode["default"]; - if (!defaultNode.isUndefined() && defaultNode.isArray()) { - QJsonArray defaultArray = defaultNode.toArray(); - QSet selected; - for (const auto& v : defaultArray) { - selected.insert(v.toString()); - } - - bool allSelected = true; - for (int i = 0; i < model->rowCount(); ++i) { - bool isSelected = selected.contains(model->item(i)->text()); - model->item(i)->setData(isSelected ? Qt::Checked : Qt::Unchecked, - Qt::CheckStateRole); - if (!isSelected) { - allSelected = false; - } - } - applyAllCheckBox->setChecked(showApplyAll && allSelected); - comboBox->setEnabled(!applyAllCheckBox->isChecked()); - } - - // Auto-hide when only one scalar - if (scalars.size() <= 1) { - label->setVisible(false); - container->setVisible(false); - } - } - - vLayout->addWidget(comboBox); - - // Toggle combo box enabled state based on checkbox - QObject::connect(applyAllCheckBox, &QCheckBox::toggled, - [comboBox](bool checked) { - comboBox->setEnabled(!checked); - }); - - // Install event filter on combo box viewport to prevent popup from closing - // on item click, while still toggling the checkbox. Only toggle on release - // if a matching press was seen on the viewport — this ignores the orphaned - // release from the click that originally opened the popup. - class ComboEventFilter : public QObject - { - public: - ComboEventFilter(QComboBox* combo, QObject* parent) - : QObject(parent), m_combo(combo) {} - bool eventFilter(QObject* obj, QEvent* event) override - { - if (event->type() == QEvent::MouseButtonPress) { - m_pressedOnViewport = true; - return true; // Consume press to keep popup open - } - if (event->type() == QEvent::MouseButtonRelease) { - if (!m_pressedOnViewport) { - return true; // No matching press — consume without toggling - } - m_pressedOnViewport = false; - // Manually toggle the check state of the item under the cursor - auto* view = m_combo->view(); - auto index = view->indexAt( - static_cast(event)->pos()); - if (index.isValid()) { - auto* model = - qobject_cast(m_combo->model()); - if (model) { - auto* item = model->itemFromIndex(index); - if (item && (item->flags() & Qt::ItemIsUserCheckable)) { - auto state = item->checkState() == Qt::Checked - ? Qt::Unchecked : Qt::Checked; - item->setCheckState(state); - } - } - } - return true; // Consume the event to keep popup open - } - return QObject::eventFilter(obj, event); - } - private: - QComboBox* m_combo; - bool m_pressedOnViewport = false; - }; - - auto* filter = new ComboEventFilter(comboBox, comboBox); - comboBox->view()->viewport()->installEventFilter(filter); - - layout->addWidget(container, row, 1, 1, 1); -} - -static const QStringList PATH_TYPES = { "file", "save_file", "directory" }; - -} // end anonymous namespace - -namespace tomviz { - -InterfaceBuilder::InterfaceBuilder(QObject* parentObject, DataSource* ds) - : QObject(parentObject), m_dataSource(ds) -{} - -void InterfaceBuilder::setJSONDescription(const QString& description) -{ - setJSONDescription(QJsonDocument::fromJson(description.toLatin1())); -} - -void InterfaceBuilder::setJSONDescription(const QJsonDocument& description) -{ - if (!description.isObject()) { - qCritical() << "Failed to parse operator JSON"; - qCritical() << m_json; - m_json = QJsonDocument(); - } else { - m_json = description; - } -} - -QLayout* InterfaceBuilder::buildParameterInterface(QGridLayout* layout, - QJsonArray& parameters, - const QString& tag) const -{ - QJsonObject::size_type numParameters = parameters.size(); - for (QJsonObject::size_type i = 0; i < numParameters; ++i) { - QJsonValueRef parameterNode = parameters[i]; - QJsonObject parameterObject = parameterNode.toObject(); - - QString tagValue = parameterObject["tag"].toString(""); - if (tagValue != tag) { - // The tag doesn't match, skip over this one. - continue; - } - - QJsonValueRef typeValue = parameterObject["type"]; - - if (typeValue.isUndefined()) { - qWarning() << tr("Parameter has no type entry"); - continue; - } - - QString typeString = typeValue.toString(); - - // See if we have a parameter value that we need to set the default - // to. - QJsonValueRef nameValue = parameterObject["name"]; - if (!nameValue.isUndefined()) { - QString parameterName = nameValue.toString(); - if (m_parameterValues.contains(parameterName)) { - QVariant parameterValue = m_parameterValues[parameterName]; - if (parameterValue.canConvert()) { - // Save the id of the pointer so we can store it in a QJsonValue - auto* ds = parameterValue.value(); - parameterValue.setValue(ds->id()); - } - - parameterObject["default"] = QJsonValue::fromVariant(parameterValue); - } - } - - if (typeString == "bool") { - addBoolWidget(layout, i + 1, parameterObject); - } else if (typeString == "int") { - addNumericWidget(layout, i + 1, parameterObject, m_dataSource); - } else if (typeString == "double") { - addNumericWidget(layout, i + 1, parameterObject, m_dataSource); - } else if (typeString == "enumeration") { - addEnumerationWidget(layout, i + 1, parameterObject); - } else if (typeString == "xyz_header") { - addXYZHeaderWidget(layout, i + 1, parameterObject); - } else if (PATH_TYPES.contains(typeString)) { - addPathWidget(layout, i + 1, parameterObject); - } else if (typeString == "string") { - addStringWidget(layout, i + 1, parameterObject); - } else if (typeString == "dataset") { - addDatasetWidget(layout, i + 1, parameterObject); - } else if (typeString == "select_scalars") { - addSelectScalarsWidget(layout, i + 1, parameterObject, m_dataSource); - } - } - - setupEnableAndVisibleStates(layout->parentWidget(), parameters); - - return layout; -} - -void InterfaceBuilder::setupEnableAndVisibleStates( - const QObject* parent, - QJsonArray& parameters) const -{ - setupEnableStates(parent, parameters, true); - setupEnableStates(parent, parameters, false); -} - -QLayout* InterfaceBuilder::buildInterface() const -{ - QWidget* widget = new QWidget; - widget->setProperty("isRootInterfaceWidget", true); - - QVBoxLayout* verticalLayout = new QVBoxLayout; - verticalLayout->addWidget(widget); - verticalLayout->addStretch(); - - QGridLayout* layout = new QGridLayout; - widget->setLayout(layout); - - if (!m_json.isObject()) { - return layout; - } - QJsonObject root = m_json.object(); - - QLabel* descriptionLabel = new QLabel("No description provided in JSON"); - descriptionLabel->setWordWrap(true); - descriptionLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); - QJsonValueRef descriptionValue = root["description"]; - if (!descriptionValue.isUndefined()) { - descriptionLabel->setText(descriptionValue.toString()); - } - verticalLayout->insertWidget(0, descriptionLabel); - - // Get the label for the operator - QString operatorLabel; - QJsonValueRef labelNode = root["label"]; - if (!labelNode.isUndefined()) { - operatorLabel = QString(labelNode.toString()); - } - - // Get the parameters for the operator - QJsonValueRef parametersNode = root["parameters"]; - if (parametersNode.isUndefined()) { - return layout; - } - auto parameters = parametersNode.toArray(); - buildParameterInterface(layout, parameters); - - return verticalLayout; -} - -static bool setWidgetValue(QObject* o, const QVariant& v) -{ - // Returns true if the widget type was found, false otherwise. - - // Handle select_scalars container widget - if (auto w = qobject_cast(o)) { - if (w->property("type").toString() == "select_scalars") { - QStringList selected; - if (v.canConvert()) { - for (const auto& item : v.toList()) { - selected << item.toString(); - } - } else if (v.canConvert()) { - selected = v.toStringList(); - } - - auto* applyAllCB = w->findChild(w->objectName() + "_apply_all"); - auto* combo = w->findChild(w->objectName() + "_combo"); - if (!applyAllCB || !combo) { - return false; - } - - auto* model = qobject_cast(combo->model()); - if (!model) { - return false; - } - - // Check if all items are selected - bool allSelected = true; - for (int i = 0; i < model->rowCount(); ++i) { - if (!selected.contains(model->item(i)->text())) { - allSelected = false; - break; - } - } - - applyAllCB->setChecked(allSelected); - for (int i = 0; i < model->rowCount(); ++i) { - Qt::CheckState state = selected.contains(model->item(i)->text()) - ? Qt::Checked : Qt::Unchecked; - model->item(i)->setData(state, Qt::CheckStateRole); - } - return true; - } - } - - if (auto cb = qobject_cast(o)) { - cb->setChecked(v.toBool()); - } else if (auto sb = qobject_cast(o)) { - sb->setValue(v.toInt()); - } else if (auto dsb = qobject_cast(o)) { - dsb->setValue(v.toDouble()); - } else if (auto combo = qobject_cast(o)) { - // Check if the variant is a data source - if (v.canConvert()) { - // Find the item with this data, and set it - auto* ds = v.value(); - bool found = false; - for (int i = 0; i < combo->count(); ++i) { - if (combo->itemData(i).value() == ds) { - found = true; - combo->setCurrentIndex(i); - break; - } - } - if (!found) { - qDebug() << "Warning: could not find combo box for data source: " - << ds->label() << "(" << ds << ")"; - } - } else { - // Assume it is a string - combo->setCurrentText(v.toString()); - } - } else if (auto le = qobject_cast(o)) { - le->setText(v.toString()); - } else { - return false; - } - - return true; -} - -void InterfaceBuilder::setParameterValues(QMap values) -{ - m_parameterValues = values; -} - -void InterfaceBuilder::updateWidgetValues(const QObject* parent) -{ - static const QStringList skipTypes = { - "QLabel", - }; - - for (auto* child : parent->findChildren()) { - if (skipTypes.contains(child->metaObject()->className())) { - // Skip this type... - continue; - } - - const auto& name = child->objectName(); - if (!m_parameterValues.contains(name)) { - continue; - } - - if (!setWidgetValue(child, m_parameterValues[name])) { - qDebug() << "Failed to set value for child: " << child; - } - } -} - -QVariantMap InterfaceBuilder::parameterValues(const QObject* parent) -{ - QVariantMap map; - - // Handle select_scalars widgets first, and collect their internal widget - // names so we can skip them in the generic loops below. - QSet selectScalarsInternalNames; - QList allWidgets = parent->findChildren(); - for (auto* w : allWidgets) { - if (w->property("type").toString() != "select_scalars") { - continue; - } - QString name = w->objectName(); - auto* applyAllCB = w->findChild(name + "_apply_all"); - auto* combo = w->findChild(name + "_combo"); - if (!applyAllCB || !combo) { - continue; - } - - selectScalarsInternalNames.insert(applyAllCB->objectName()); - selectScalarsInternalNames.insert(combo->objectName()); - - auto* model = qobject_cast(combo->model()); - if (!model) { - continue; - } - - QVariantList selectedScalars; - if (applyAllCB->isChecked()) { - // All scalars selected - for (int i = 0; i < model->rowCount(); ++i) { - selectedScalars << model->item(i)->text(); - } - } else { - // Only checked scalars - for (int i = 0; i < model->rowCount(); ++i) { - if (model->item(i)->checkState() == Qt::Checked) { - selectedScalars << model->item(i)->text(); - } - } - } - map[name] = selectedScalars; - } - - // Iterate over all children, taking the value of the named widgets - // and stuffing them into the map. - QList checkBoxes = parent->findChildren(); - for (int i = 0; i < checkBoxes.size(); ++i) { - if (selectScalarsInternalNames.contains(checkBoxes[i]->objectName())) { - continue; - } - map[checkBoxes[i]->objectName()] = - (checkBoxes[i]->checkState() == Qt::Checked); - } - - QList spinBoxes = parent->findChildren(); - for (int i = 0; i < spinBoxes.size(); ++i) { - map[spinBoxes[i]->objectName()] = spinBoxes[i]->value(); - } - - QList doubleSpinBoxes = - parent->findChildren(); - for (int i = 0; i < doubleSpinBoxes.size(); ++i) { - map[doubleSpinBoxes[i]->objectName()] = doubleSpinBoxes[i]->value(); - } - - QList comboBoxes = parent->findChildren(); - for (int i = 0; i < comboBoxes.size(); ++i) { - if (selectScalarsInternalNames.contains(comboBoxes[i]->objectName())) { - continue; - } - int currentIndex = comboBoxes[i]->currentIndex(); - map[comboBoxes[i]->objectName()] = comboBoxes[i]->itemData(currentIndex); - } - - // Assemble multi-component properties into single properties in the map. - QMap::iterator iter = map.begin(); - while (iter != map.end()) { - QString name = iter.key(); - QVariant value = iter.value(); - int poundIndex = name.indexOf(tr("#")); - if (poundIndex >= 0) { - QString indexString = name.mid(poundIndex + 1); - - // Keep the part of the name to the left of the '#' - name = name.left(poundIndex); - - QList valueList; - QMap::iterator findIter = map.find(name); - if (findIter != map.end()) { - valueList = map[name].toList(); - } - - // The QMap keeps entries sorted by lexicographic order, so we - // can just append to the list and the elements will be inserted - // in the correct order. - valueList.append(value); - map[name] = valueList; - - // Delete the individual component map entry. Doing so increments the - // iterator. - iter = map.erase(iter); - } else { - // Single-element parameter, nothing to do - ++iter; - } - } - - // QLineEdit's ( currently 'file', 'save_file', and 'directory' types ). - QList lineEdits = parent->findChildren(); - for (int i = 0; i < lineEdits.size(); ++i) { - auto lineEdit = lineEdits[i]; - QVariant type = lineEdit->property("type"); - bool canConvertTypeToString = QMetaType::canConvert(type.metaType(), QMetaType(QMetaType::QString)); - if (canConvertTypeToString && PATH_TYPES.contains(type.toString())) { - map[lineEdit->objectName()] = lineEdit->text(); - } else if (canConvertTypeToString && type.toString() == "string") { - map[lineEdit->objectName()] = lineEdit->text(); - } - } - - return map; -} - -QWidget* findRootInterfaceWidget(const QWidget* widget) -{ - auto* parent = widget->parent(); - while (parent and not parent->property("isRootInterfaceWidget").toBool()) { - parent = parent->parent(); - } - - if (parent and parent->property("isRootInterfaceWidget").toBool()) { - return qobject_cast(parent); - } - - return nullptr; -} - -QLabel* findLabelForWidget(const QWidget* widget) -{ - // We use the buddy system to keep track of which label is for - // which widget. - auto* parent = findRootInterfaceWidget(widget); - if (!parent) { - return nullptr; - } - - for (auto* child : parent->findChildren()) { - if (child->buddy() == widget) { - return child; - } - } - - return nullptr; -} - -void setWidgetProperty(QWidget* widget, const char* property, QVariant value) -{ - if (isWidgetNumeric(widget) || isWidgetType(widget)) { - // These types actually want the parent widget instead, because there - // is some parent widget holding everything (spinboxes, path button, etc.). - widget = widget->parentWidget(); - if (!widget) { - // This hopefully should't happen. - return; - } - } - - // First, set the property on the widget - widget->setProperty(property, value); - - // Next, see if we can find the label corresponding to this widget, and - // set the property there as well. - auto* label = findLabelForWidget(widget); - if (label) { - label->setProperty(property, value); - } -} - -template -bool compareGeneric(T value, T ref, const QString& comparator) -{ - // The generic one, for bools and strings, can only do `==` and `!=`. - if (comparator == "==") { - return value == ref; - } else if (comparator == "!=") { - return value != ref; - } - - return false; -} - -template -bool compareNumbers(T value, T ref, const QString& comparator) -{ - // This is for ints and floats. We can do inequality comparisons. - if (comparator == "==") { - return value == ref; - } else if (comparator == "!=") { - return value != ref; - } else if (comparator == ">") { - return value > ref; - } else if (comparator == "<") { - return value < ref; - } else if (comparator == ">=") { - return value >= ref; - } else if (comparator == "<=") { - return value <= ref; - } - - return false; -} - -template -bool compare(const T* widget, const QVariant& compareValue, - const QString& comparator) -{ - auto value = widgetValue(widget); - if constexpr (std::is_same_v || std::is_same_v) { - QString ref = compareValue.toString(); - if (ref.startsWith("'") || ref.startsWith("\"")) { - // Remove the first and last characters - ref = ref.mid(1, ref.length() - 2); - } - return compareGeneric(value, ref, comparator); - } else if constexpr (std::is_same_v) { - return compareGeneric(value, compareValue.toBool(), comparator); - } else if constexpr (std::is_same_v) { - return compareNumbers(value, compareValue.toInt(), comparator); - } else if constexpr (std::is_same_v) { - return compareNumbers(value, compareValue.toDouble(), comparator); - } - - return false; -} - -// Represents a single condition clause like "algorithm == 'mlem'" -struct EnableCondition -{ - QWidget* refWidget = nullptr; - QString comparator; - QVariant compareValue; -}; - -// Evaluate a single condition by delegating to the typed compare() function -static bool evaluateCondition(const EnableCondition& cond) -{ - auto* w = cond.refWidget; - if (isWidgetType(w)) { - return compare(qobject_cast(w), cond.compareValue, cond.comparator); - } else if (isWidgetType(w)) { - return compare(qobject_cast(w), cond.compareValue, cond.comparator); - } else if (isWidgetType(w)) { - return compare(qobject_cast(w), cond.compareValue, cond.comparator); - } else if (isWidgetType(w)) { - return compare(qobject_cast(w), cond.compareValue, cond.comparator); - } else if (isWidgetType(w)) { - return compare(qobject_cast(w), cond.compareValue, cond.comparator); - } - return false; -} - -// Evaluate a compound expression: list of condition groups joined by "or", -// where each group is a list of conditions joined by "and". -// Result = (g0[0] && g0[1] && ...) || (g1[0] && g1[1] && ...) || ... -static bool evaluateCompound( - const QList>& orGroups) -{ - for (auto& andGroup : orGroups) { - bool groupResult = true; - for (auto& cond : andGroup) { - if (!evaluateCondition(cond)) { - groupResult = false; - break; - } - } - if (groupResult) { - return true; - } - } - return false; -} - -static void connectWidgetChanged(QWidget* refWidget, QWidget* target, - std::function func) -{ - if (isWidgetType(refWidget)) { - target->connect(qobject_cast(refWidget), - changedSignal(), target, func); - } else if (isWidgetType(refWidget)) { - target->connect(qobject_cast(refWidget), - changedSignal(), target, func); - } else if (isWidgetType(refWidget)) { - target->connect(qobject_cast(refWidget), - changedSignal(), target, func); - } else if (isWidgetType(refWidget)) { - target->connect(qobject_cast(refWidget), - changedSignal(), target, func); - } else if (isWidgetType(refWidget)) { - target->connect(qobject_cast(refWidget), - changedSignal(), target, func); - } else { - qCritical() << "Unhandled widget type for enable/visible trigger:" - << refWidget->objectName(); - } -} - -void InterfaceBuilder::setupEnableStates(const QObject* parent, - QJsonArray& parameters, - bool visible) const -{ - static const QStringList validComparators = { - "==", "!=", ">", ">=", "<", "<=" - }; - - QJsonObject::size_type numParameters = parameters.size(); - for (QJsonObject::size_type i = 0; i < numParameters; ++i) { - QJsonValueRef parameterNode = parameters[i]; - QJsonObject parameterObject = parameterNode.toObject(); - - QString text = visible ? "visible_if" : "enable_if"; - QString enableIfValue = parameterObject[text].toString(""); - if (enableIfValue.isEmpty()) { - continue; - } - - QString widgetName = parameterObject["name"].toString(""); - if (widgetName.isEmpty()) { - qCritical() << text << "parameters must have a name. Ignoring..."; - continue; - } - auto* widget = parent->findChild(widgetName); - if (!widget) { - qCritical() << "Failed to find widget with name:" << widgetName; - continue; - } - - // Split on " or " first, then each piece on " and ". - // Precedence: "and" binds tighter than "or". - auto orParts = enableIfValue.simplified().split(" or ", - Qt::KeepEmptyParts, - Qt::CaseInsensitive); - - QList> orGroups; - bool parseError = false; - - for (auto& orPart : orParts) { - auto andParts = orPart.simplified().split(" and ", - Qt::KeepEmptyParts, - Qt::CaseInsensitive); - QList andGroup; - for (auto& clause : andParts) { - auto tokens = clause.simplified().split(" "); - if (tokens.size() != 3) { - qCritical() << "Invalid" << text << "clause:" << clause - << "in expression:" << enableIfValue; - parseError = true; - break; - } - - auto refWidgetName = tokens[0]; - auto comparator = tokens[1]; - auto compareValue = tokens[2]; - - auto* refWidget = parent->findChild(refWidgetName); - if (!refWidget) { - qCritical() << "Invalid widget name" << refWidgetName << "in" - << text << "string:" << enableIfValue; - parseError = true; - break; - } - - if (!validComparators.contains(comparator)) { - qCritical() << "Invalid comparator" << comparator << "in" - << text << "string:" << enableIfValue; - parseError = true; - break; - } - - EnableCondition cond; - cond.refWidget = refWidget; - cond.comparator = comparator; - cond.compareValue = compareValue; - andGroup.append(cond); - } - - if (parseError) { - break; - } - orGroups.append(andGroup); - } - - if (parseError) { - continue; - } - - const char* property = visible ? "visible" : "enabled"; - - // Build the evaluation callback - auto evalFunc = [orGroups, widget, property]() { - bool result = evaluateCompound(orGroups); - setWidgetProperty(widget, property, result); - }; - - // Connect every referenced widget's changed signal to re-evaluate - QSet connectedWidgets; - for (auto& andGroup : orGroups) { - for (auto& cond : andGroup) { - if (connectedWidgets.contains(cond.refWidget)) { - continue; - } - connectedWidgets.insert(cond.refWidget); - connectWidgetChanged(cond.refWidget, widget, evalFunc); - } - } - - // Evaluate once for the initial state - evalFunc(); - } -} - -} // namespace tomviz diff --git a/tomviz/InterfaceBuilder.h b/tomviz/InterfaceBuilder.h deleted file mode 100644 index 0d3c026b8..000000000 --- a/tomviz/InterfaceBuilder.h +++ /dev/null @@ -1,60 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizInterfaceBuilder_h -#define tomvizInterfaceBuilder_h - -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -class DataSource; - -/// The InterfaceBuilder creates a Qt widget containing controls defined -/// by a JSON description. -class InterfaceBuilder : public QObject -{ - Q_OBJECT - -public: - InterfaceBuilder(QObject* parent = nullptr, DataSource* ds = nullptr); - - /// Set the JSON description - void setJSONDescription(const QString& description); - void setJSONDescription(const QJsonDocument& description); - - /// Build the interface, returning it in a QWidget. - QLayout* buildInterface() const; - QLayout* buildParameterInterface(QGridLayout* layout, QJsonArray& parameters, - const QString& tag = "") const; - - /// Set the parameter values - void setParameterValues(QMap values); - - static QMap parameterValues(const QObject* parent); - - void updateWidgetValues(const QObject* parent); - - void setupEnableAndVisibleStates(const QObject* parent, - QJsonArray& parameters) const; - void setupEnableStates(const QObject* parent, QJsonArray& parameters, - bool visible) const; - - QWidget* findWidgetByName(const QObject* parent, const QString& name) const; - -private: - Q_DISABLE_COPY(InterfaceBuilder) - - QJsonDocument m_json; - QMap m_parameterValues; - DataSource* m_dataSource; -}; - -} // namespace tomviz - -#endif diff --git a/tomviz/LoadDataReaction.cxx b/tomviz/LoadDataReaction.cxx index 840a2b88d..d67deeee4 100644 --- a/tomviz/LoadDataReaction.cxx +++ b/tomviz/LoadDataReaction.cxx @@ -13,8 +13,6 @@ #include "ImageStackDialog.h" #include "ImageStackModel.h" #include "LoadStackReaction.h" -#include "legacy/Pipeline.h" -#include "legacy/PipelineManager.h" #include "PythonReader.h" #include "PythonUtilities.h" #include "RAWFileReaderDialog.h" diff --git a/tomviz/LoadStackReaction.cxx b/tomviz/LoadStackReaction.cxx index 8f626abb8..39be98957 100644 --- a/tomviz/LoadStackReaction.cxx +++ b/tomviz/LoadStackReaction.cxx @@ -4,12 +4,12 @@ #include "LoadStackReaction.h" #include "ActiveObjects.h" -#include "legacy/DataSource.h" #include "ImageStackDialog.h" #include "LoadDataReaction.h" #include "Utilities.h" #include "pipeline/Pipeline.h" +#include "pipeline/PortType.h" #include "pipeline/PortUtils.h" #include "pipeline/SourceNode.h" #include "pipeline/data/VolumeData.h" @@ -70,9 +70,9 @@ pipeline::SourceNode* LoadStackReaction::execStackDialog( return nullptr; } - DataSource::DataSourceType stackType = dialog.getStackType(); + pipeline::PortType stackType = dialog.getStackType(); bool imageViewerMode = dialog.getImageViewerMode(); - if (stackType == DataSource::DataSourceType::TiltSeries) { + if (stackType == pipeline::PortType::TiltSeries) { // Build tilt angles from the stack summary QMap angles; int j = 0; diff --git a/tomviz/MainWindow.cxx b/tomviz/MainWindow.cxx index e82abed1f..cd0e2cc41 100644 --- a/tomviz/MainWindow.cxx +++ b/tomviz/MainWindow.cxx @@ -42,8 +42,6 @@ #include "LoadPaletteReaction.h" #include "LoadStackReaction.h" #include "LoadTimeSeriesReaction.h" -#include "legacy/modules/ModuleManager.h" -#include "legacy/modules/ModuleMenu.h" #include "PipelineModuleMenu.h" #include "pipeline/Pipeline.h" #include "pipeline/PipelineExecutor.h" @@ -66,10 +64,6 @@ #include "MoleculeProperties.h" #include "CentralWidget.h" #include "OperatorSearchDialog.h" -#include "PassiveAcquisitionWidget.h" -#include "legacy/Pipeline.h" -#include "legacy/PipelineManager.h" -#include "legacy/PipelineProxy.h" #include "ProgressDialogManager.h" #include "PtychoRunner.h" #include "PyXRFRunner.h" @@ -77,7 +71,6 @@ #include "PythonUtilities.h" #include "RecentFilesMenu.h" #include "ReconstructionReaction.h" -#include "RegexGroupSubstitution.h" #include "ResetReaction.h" #include "SaveDataReaction.h" #include "SaveLoadStateReaction.h" @@ -88,12 +81,9 @@ #include "SetTiltAnglesReaction.h" #include "Utilities.h" #include "ViewMenuManager.h" -#include "legacy/modules/VolumeManager.h" #include "WelcomeDialog.h" #include "tomvizConfig.h" -#include "legacy/PipelineModel.h" - #include #include #include @@ -145,16 +135,6 @@ MainWindow* MainWindow::instance() MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) : QMainWindow(parent, flags), m_ui(new Ui::MainWindow) { - VolumeManager::instance(); - connect(&ModuleManager::instance(), &ModuleManager::enablePythonConsole, this, - &MainWindow::setEnabledPythonConsole); - - connect(&ModuleManager::instance(), &ModuleManager::mouseOverVoxel, this, - &MainWindow::onMouseOverVoxel); - - connect(&ModuleManager::instance(), - &ModuleManager::mostRecentStateFileChanged, this, - &MainWindow::updateSaveStateEnableState); // Update back light azimuth default on view. connect(pqApplicationCore::instance()->getServerManagerModel(), @@ -194,13 +174,6 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) // shell. pqPythonShell::setPreamble(QStringList()); - connect(m_ui->pythonConsole, &pqPythonShell::executing, - [this](bool executing) { - if (!executing) { - this->syncPythonToApp(); - } - }); - // Hide these dock widgets when tomviz is first opened. If they are later // opened and remain open while tomviz is shut down, their visibility and // geometry state will be saved out to the settings file. The dock widgets @@ -659,9 +632,6 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) // Populate the menu with templates findPipelineTemplates(); - // Register our factories for Python wrapping. - PipelineProxyFactory::registerWithFactory(); - // Build Tomography menu // ################################################################ QAction* setVolumeDataTypeAction = @@ -748,9 +718,10 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) // Set up reactions for Tomography Menu //################################################################# - new SetDataTypeReaction(setVolumeDataTypeAction, this, DataSource::Volume); - new SetDataTypeReaction(setTiltDataTypeAction, this, DataSource::TiltSeries); - // new SetDataTypeReaction(setFibDataTypeAction, this, DataSource::FIB); + new SetDataTypeReaction(setVolumeDataTypeAction, this, + pipeline::PortType::Volume); + new SetDataTypeReaction(setTiltDataTypeAction, this, + pipeline::PortType::TiltSeries); new SetTiltAnglesReaction(setTiltAnglesAction, this); new AddPythonTransformReaction( @@ -984,7 +955,6 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) // Add the acquisition client experimentally. m_ui->actionAcquisition->setEnabled(false); - m_ui->actionPassiveAcquisition->setEnabled(false); connect(m_ui->actionAcquisition, &QAction::triggered, this, [this]() { openDialog(&m_acquisitionWidget); }); @@ -993,10 +963,6 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) openDialog(&m_animationHelperDialog); }); - connect(m_ui->actionPassiveAcquisition, &QAction::triggered, this, [this]() { - openDialog(&m_passiveAcquisitionDialog); - }); - // Prepopulate the previously seen python readers/writers // This operation is fast since it fetches the readers description // from the settings, without really invoking python @@ -1007,7 +973,6 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) auto operators = initPython(); m_ui->actionAcquisition->setEnabled(true); - m_ui->actionPassiveAcquisition->setEnabled(true); registerCustomOperators(operators); auto dataBroker = new DataBroker(this); @@ -1065,7 +1030,6 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) MainWindow::~MainWindow() { - ModuleManager::instance().reset(); QString autosaveFile = getAutosaveFile(); if (QFile::exists(autosaveFile) && !QFile::remove(autosaveFile)) { std::cerr << "Failed to remove autosave file." << std::endl; @@ -1076,7 +1040,6 @@ std::vector MainWindow::initPython() { Python::initialize(); Connection::registerType(); - RegexGroupSubstitution::registerType(); auto operators = findCustomOperators(); FileFormatManager::instance().registerPythonReaders(); FileFormatManager::instance().registerPythonWriters(); @@ -1216,7 +1179,8 @@ void MainWindow::dropEvent(QDropEvent* e) void MainWindow::closeEvent(QCloseEvent* e) { - if (ModuleManager::instance().hasRunningOperators()) { + auto* activePipeline = ActiveObjects::instance().pipeline(); + if (activePipeline && activePipeline->isExecuting()) { QMessageBox::StandardButton response = QMessageBox::question(this, "Close tomviz?", "You have transforms that are not completed " @@ -1227,7 +1191,7 @@ void MainWindow::closeEvent(QCloseEvent* e) e->ignore(); return; } - } else if (ModuleManager::instance().hasDataSources()) { + } else if (activePipeline && !activePipeline->nodes().isEmpty()) { QMessageBox::StandardButton response = QMessageBox::question(this, "Close?", "Are you sure you want to exit?"); if (response == QMessageBox::No) { @@ -1238,10 +1202,9 @@ void MainWindow::closeEvent(QCloseEvent* e) // This is a little hackish, but we must ensure all PV proxy unregister calls // happen early enough in application destruction that the ParaView proxy // management code can still run without segfaulting. - PipelineManager::instance().removeAllPipelines(); - ModuleManager::instance().removeAllModules(); - ModuleManager::instance().removeAllDataSources(); - ModuleManager::instance().removeAllMoleculeSources(); + if (activePipeline) { + activePipeline->clear(); + } e->accept(); } @@ -1302,7 +1265,16 @@ void MainWindow::autosave() QString MainWindow::mostRecentStateFile() const { - return ModuleManager::instance().mostRecentStateFile(); + return m_mostRecentStateFile; +} + +void MainWindow::setMostRecentStateFile(const QString& fileName) +{ + if (m_mostRecentStateFile == fileName) { + return; + } + m_mostRecentStateFile = fileName; + updateSaveStateEnableState(); } void MainWindow::updateSaveStateEnableState() @@ -1537,26 +1509,6 @@ void MainWindow::onMouseOverVoxel(const vtkVector3i& ijk, double v) 5000); } -void MainWindow::syncPythonToApp() -{ - Python python; - auto tomvizState = python.import("tomviz.state"); - if (!tomvizState.isValid()) { - qCritical() << "Failed to import tomviz.state"; - return; - } - - auto sync = tomvizState.findFunction("sync"); - if (!sync.isValid()) { - qCritical() << "Unable to locate sync."; - return; - } - - Python::Tuple args(0); - Python::Dict kwargs; - sync.call(args, kwargs); -} - void MainWindow::findPipelineTemplates() { m_pipelineTemplates->clear(); @@ -1653,9 +1605,13 @@ void MainWindow::initPipeline() connect(p, &pipeline::Pipeline::nodeAdded, this, &MainWindow::onNodeSelected); - // Per-node rescale of existing color maps. Uses cached VTK objects - // (not SM proxy creation), so safe while the worker thread is still - // running subsequent nodes. + // Per-node rescale of existing color maps. rescaleColorMap() pushes + // SM proxy state through the ParaView session, which is not + // thread-safe against the worker thread's node execution. The + // ThreadedExecutor parks its worker at a per-node barrier until this + // handler returns (see ExecutionWorker::run), so no operator runs + // concurrently with the rescale below — this previously crashed with + // a SIGBUS inside vtkSMProxy::UpdateVTKObjects. connect(p->executor(), &pipeline::PipelineExecutor::nodeExecutionFinished, this, [](pipeline::Node* node, bool success) { if (!success) { diff --git a/tomviz/MainWindow.h b/tomviz/MainWindow.h index ad7498eb7..989dffcd1 100644 --- a/tomviz/MainWindow.h +++ b/tomviz/MainWindow.h @@ -22,7 +22,6 @@ namespace tomviz { class AboutDialog; class DataSource; -class MoleculeSource; class Module; struct OperatorDescription; class OperatorSearchDialog; @@ -52,6 +51,8 @@ class MainWindow : public QMainWindow pipeline::Pipeline* pipeline() const; + void setMostRecentStateFile(const QString& fileName); + protected: void showEvent(QShowEvent* event) override; void closeEvent(QCloseEvent* event) override; @@ -103,7 +104,6 @@ private slots: static std::vector findCustomOperators(); void registerCustomOperators(std::vector operators); static std::vector initPython(); - void syncPythonToApp(); void updateSaveStateEnableState(); QString mostRecentStateFile() const; @@ -138,11 +138,12 @@ private slots: QMetaObject::Connection m_editingChangedConn; QPointer m_dynamicPropertiesWidget; + QString m_mostRecentStateFile; + // Lazily loaded dialogs QWidget* m_aboutDialog = nullptr; QWidget* m_acquisitionWidget = nullptr; QWidget* m_animationHelperDialog = nullptr; - QWidget* m_passiveAcquisitionDialog = nullptr; template void openDialog(QWidget**); diff --git a/tomviz/MainWindow.ui b/tomviz/MainWindow.ui index 42e329b33..2ce398c18 100644 --- a/tomviz/MainWindow.ui +++ b/tomviz/MainWindow.ui @@ -83,7 +83,6 @@ - @@ -264,27 +263,6 @@ 0 - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 100 - 30 - - - - @@ -327,14 +305,6 @@ Ctrl+Q - - - &Passive Acquisition - - - Passive Acquisition - - &Save Data @@ -483,12 +453,6 @@ QToolBar
pqVCRToolbar.h
- - tomviz::MoleculePropertiesPanel - QWidget -
MoleculePropertiesPanel.h
- 1 -
pqOutputWidget QWidget diff --git a/tomviz/MergeImagesDialog.cxx b/tomviz/MergeImagesDialog.cxx deleted file mode 100644 index 561aedf33..000000000 --- a/tomviz/MergeImagesDialog.cxx +++ /dev/null @@ -1,25 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "MergeImagesDialog.h" -#include "ui_MergeImagesDialog.h" - -namespace tomviz { - -MergeImagesDialog::MergeImagesDialog(QWidget* parent) - : QDialog(parent), m_ui(new Ui::MergeImagesDialog) -{ - m_ui->setupUi(this); - m_ui->MergeImageArraysRadioButton->setChecked(true); - m_ui->MergeImageArraysRadioButton->setChecked(false); - m_ui->MergeArrayComponentsWidget->hide(); -} - -MergeImagesDialog::~MergeImagesDialog() {} - -MergeImagesDialog::MergeMode MergeImagesDialog::getMode() -{ - return (m_ui->MergeImageArraysRadioButton->isChecked() ? Arrays : Components); -} - -} // namespace tomviz diff --git a/tomviz/MergeImagesDialog.h b/tomviz/MergeImagesDialog.h deleted file mode 100644 index 3bafcec91..000000000 --- a/tomviz/MergeImagesDialog.h +++ /dev/null @@ -1,38 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizMergeImagesDialog_h -#define tomvizMergeImagesDialog_h - -#include -#include - -namespace Ui { -class MergeImagesDialog; -} - -namespace tomviz { - -class MergeImagesDialog : public QDialog -{ - Q_OBJECT - -public: - MergeImagesDialog(QWidget* parent = nullptr); - ~MergeImagesDialog() override; - - enum MergeMode : int - { - Arrays, - Components - }; - - MergeMode getMode(); - -private: - Q_DISABLE_COPY(MergeImagesDialog) - QScopedPointer m_ui; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/MergeImagesDialog.ui b/tomviz/MergeImagesDialog.ui deleted file mode 100644 index 81482d850..000000000 --- a/tomviz/MergeImagesDialog.ui +++ /dev/null @@ -1,202 +0,0 @@ - - - MergeImagesDialog - - - - 0 - 0 - 400 - 268 - - - - Dialog - - - - - - - 0 - 0 - - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 75 - true - - - - - - - Merge image arrays - - - true - - - - - - - Merge all arrays from the selected images into a new image, but keep the arrays separate. - - - true - - - - - - - - 75 - true - - - - - - - Merge array components - - - - - - - Merge arrays from the selected images into a new image, but merge all components into one array. - - - true - - - - - - - - - - - 0 - 0 - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - MergeImagesDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - MergeImagesDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - MergeImageArraysRadioButton - clicked() - MergeArrayComponentsWidget - hide() - - - 199 - 22 - - - 199 - 314 - - - - - MergeArrayComponentsRadioButton - clicked() - MergeArrayComponentsWidget - show() - - - 199 - 78 - - - 199 - 314 - - - - - diff --git a/tomviz/MergeImagesReaction.cxx b/tomviz/MergeImagesReaction.cxx deleted file mode 100644 index da435f529..000000000 --- a/tomviz/MergeImagesReaction.cxx +++ /dev/null @@ -1,199 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "MergeImagesReaction.h" - -#include "ActiveObjects.h" -#include "legacy/DataSource.h" -#include "LoadDataReaction.h" -#include "MergeImagesDialog.h" - -#include "pipeline/PortData.h" -#include "pipeline/PortType.h" -#include "pipeline/SourceNode.h" -#include "pipeline/data/VolumeData.h" - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace tomviz { - -MergeImagesReaction::MergeImagesReaction(QAction* parentObject) - : pqReaction(parentObject) -{ - updateEnableState(); -} - -void MergeImagesReaction::onTriggered() -{ - // Post a dialog box to select what kind of merging to do, either merging - // arrays or merging components in a single array. - MergeImagesDialog dialog; - - // TODO - build list widget of arrays and their inputs in the dialog, - // allow for reordering - - int result = dialog.exec(); - if (result == QDialog::Rejected) { - return; - } - - DataSource* newSource = nullptr; - if (dialog.getMode() == MergeImagesDialog::Arrays) { - newSource = mergeArrays(); - } else { - newSource = mergeComponents(); - } - - if (newSource) { - // Create a SourceNode from the merged DataSource - auto* source = new pipeline::SourceNode(); - source->setLabel(newSource->label()); - source->addOutput("volume", pipeline::PortType::ImageData); - vtkSmartPointer img = newSource->imageData(); - auto vol = std::make_shared(img); - vol->setLabel(newSource->label()); - source->setOutputData( - "volume", - pipeline::PortData(vol, pipeline::PortType::ImageData)); - LoadDataReaction::sourceNodeAdded(source); - delete newSource; // Clean up the temporary DataSource - } -} - -void MergeImagesReaction::updateDataSources(QSet sources) -{ - m_dataSources = sources; - updateEnableState(); -} - -void MergeImagesReaction::updateEnableState() -{ - bool enabled = m_dataSources.size() > 1; - - // Check for compatibility of the DataSource extents. Ignore overlap in - // physical space for now. - if (enabled) { - QList sourceList = m_dataSources.values(); - auto info = sourceList[0]->proxy()->GetDataInformation(); - int refExtent[6]; - info->GetExtent(refExtent); - - // Check against other DataSource extents - for (int i = 0; i < m_dataSources.size(); ++i) { - info = sourceList[i]->proxy()->GetDataInformation(); - int thisExtent[6]; - info->GetExtent(thisExtent); - enabled = enabled && std::equal(refExtent, refExtent + 6, thisExtent); - } - } - - parentAction()->setEnabled(enabled); -} - -DataSource* MergeImagesReaction::mergeArrays() -{ - if (m_dataSources.size() < 1) { - return nullptr; - } - - QList sourceList = m_dataSources.values(); - - // Eventually, we'll offer the option to merge components in a single array. - // For now, we will simply append the point data arrays. - vtkSMSessionProxyManager* pxm = ActiveObjects::instance().proxyManager(); - vtkSMSourceProxy* filter = vtkSMSourceProxy::SafeDownCast( - pxm->NewProxy("filters", "AppendAttributes")); - Q_ASSERT(filter); - - for (int i = 0; i < sourceList.size(); ++i) { - vtkSMPropertyHelper(filter, "Input").Add(sourceList[i]->proxy(), 0); - } - - filter->UpdateVTKObjects(); - filter->UpdatePipeline(); - - DataSource* newSource = new DataSource(filter); - QString mergedFilename(QFileInfo(sourceList[0]->fileName()).baseName()); - for (int i = 1; i < sourceList.size(); ++i) { - mergedFilename.append(" + "); - mergedFilename.append(QFileInfo(sourceList[i]->fileName()).baseName()); - } - newSource->setFileName(mergedFilename); - filter->Delete(); - - return newSource; -} - -DataSource* MergeImagesReaction::mergeComponents() -{ - if (m_dataSources.size() < 1) { - return nullptr; - } - - QList sourceList = m_dataSources.values(); - QStringList sourceLabels; - - vtkSMSessionProxyManager* pxm = ActiveObjects::instance().proxyManager(); - vtkSMSourceProxy* filter = vtkSMSourceProxy::SafeDownCast( - pxm->NewProxy("filters", "PythonCalculator")); - Q_ASSERT(filter); - - std::stringstream expression; - expression << "np.transpose(np.vstack(("; - - for (int i = 0; i < sourceList.size(); ++i) { - vtkSMPropertyHelper(filter, "Input").Add(sourceList[i]->proxy(), 0); - - auto info = sourceList[0]->proxy()->GetDataInformation(); - auto pointData = info->GetPointDataInformation(); - for (int j = 0; j < pointData->GetNumberOfArrays(); ++j) { - auto arrayInfo = pointData->GetArrayInformation(j); - std::string arrayName(arrayInfo->GetName()); - expression << "inputs[" << i << "].PointData['" << arrayName << "']"; - if (i < sourceList.size() - 1 || j < pointData->GetNumberOfArrays() - 1) { - expression << ", "; - } - } - - sourceLabels.append(sourceList[i]->label()); - } - - // Arrange columns - expression << ")))"; - - std::cout << "expression: " << expression.str() << std::endl; - - // Set the Python expression for arranging the components in the output - vtkSMPropertyHelper(filter, "ArrayAssociation").Set(0); - vtkSMPropertyHelper(filter, "CopyArrays").Set(0); - vtkSMPropertyHelper(filter, "Expression").Set(expression.str().c_str()); - vtkSMPropertyHelper(filter, "ArrayName").Set("Merged"); - - filter->UpdateVTKObjects(); - filter->UpdatePipeline(); - - DataSource* newSource = new DataSource(filter); - newSource->setFileName("Merged Image"); - filter->Delete(); - - // Give the components names based off the labels of the data sources - // that were used to generate them. - newSource->setComponentNames(sourceLabels); - return newSource; -} - -} // namespace tomviz diff --git a/tomviz/MergeImagesReaction.h b/tomviz/MergeImagesReaction.h deleted file mode 100644 index 5f23bb59a..000000000 --- a/tomviz/MergeImagesReaction.h +++ /dev/null @@ -1,39 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizMergeImagesReaction_h -#define tomvizMergeImagesReaction_h - -#include - -#include - -namespace tomviz { - -class DataSource; - -class MergeImagesReaction : pqReaction -{ - Q_OBJECT - -public: - MergeImagesReaction(QAction* action); - -public slots: - void updateDataSources(QSet); - -protected: - void onTriggered() override; - void updateEnableState() override; - - DataSource* mergeArrays(); - DataSource* mergeComponents(); - -private: - Q_DISABLE_COPY(MergeImagesReaction) - - QSet m_dataSources; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/MoleculePropertiesPanel.cxx b/tomviz/MoleculePropertiesPanel.cxx deleted file mode 100644 index 68c9626af..000000000 --- a/tomviz/MoleculePropertiesPanel.cxx +++ /dev/null @@ -1,68 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "MoleculePropertiesPanel.h" - -#include "ActiveObjects.h" -#include "MoleculeProperties.h" -#include "MoleculeSource.h" - -#include - -#include -#include -#include - -namespace tomviz { - -MoleculePropertiesPanel::MoleculePropertiesPanel(QWidget* parent) - : QWidget(parent) -{ - - m_layout = new QVBoxLayout(); - QWidget* separator = pqProxyWidget::newGroupLabelWidget("Filename", this); - m_label = new QLineEdit(); - m_label->setAutoFillBackground(true); - m_label->setFrame(false); - m_label->setReadOnly(true); - m_layout->addWidget(separator); - m_layout->addWidget(m_label); - m_layout->addStretch(); - this->setLayout(m_layout); - - // TODO: migrate to new pipeline - // Old code connected to ActiveObjects::moleculeSourceChanged - update(); -} - -MoleculePropertiesPanel::~MoleculePropertiesPanel() -{ -} - -void MoleculePropertiesPanel::setMoleculeSource(MoleculeSource* source) -{ - m_currentMoleculeSource = source; - update(); -} - -void MoleculePropertiesPanel::update() -{ - if (m_moleculeProperties) { - m_layout->removeWidget(m_moleculeProperties); - delete m_moleculeProperties; - m_moleculeProperties = nullptr; - } - - if (m_currentMoleculeSource) { - auto fileName = m_currentMoleculeSource->fileName(); - m_label->setText(fileName); - auto molecule = m_currentMoleculeSource->molecule(); - m_moleculeProperties = new MoleculeProperties(molecule); - m_layout->insertWidget(m_layout->count() - 1, m_moleculeProperties); - - } else { - m_label->setText(QString()); - } -} - -} // tomviz diff --git a/tomviz/MoleculePropertiesPanel.h b/tomviz/MoleculePropertiesPanel.h deleted file mode 100644 index a10c31f30..000000000 --- a/tomviz/MoleculePropertiesPanel.h +++ /dev/null @@ -1,42 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizMoleculePropertiesPanel_h -#define tomvizMoleculePropertiesPanel_h - -#include - -#include - -class QVBoxLayout; -class QLineEdit; - -namespace tomviz { - -class MoleculeSource; -class MoleculeProperties; - -class MoleculePropertiesPanel : public QWidget -{ - Q_OBJECT - -public: - explicit MoleculePropertiesPanel(QWidget* parent = nullptr); - ~MoleculePropertiesPanel() override; - -private slots: - void setMoleculeSource(MoleculeSource*); - -private: - Q_DISABLE_COPY(MoleculePropertiesPanel) - - void update(); - - QPointer m_currentMoleculeSource; - QVBoxLayout* m_layout; - QLineEdit* m_label; - MoleculeProperties* m_moleculeProperties = nullptr; -}; -} - -#endif diff --git a/tomviz/MoleculeSource.cxx b/tomviz/MoleculeSource.cxx deleted file mode 100644 index cb8eefaf7..000000000 --- a/tomviz/MoleculeSource.cxx +++ /dev/null @@ -1,102 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "MoleculeSource.h" - -#include "legacy/modules/ModuleFactory.h" -#include "legacy/modules/ModuleManager.h" -#include "legacy/Pipeline.h" - -#include -#include - -#include - -namespace tomviz { - -MoleculeSource::MoleculeSource(vtkMolecule* molecule, QObject* parent) - : QObject(parent), m_molecule(vtkSmartPointer::Take(molecule)) -{ -} - -MoleculeSource::~MoleculeSource() = default; - -QJsonObject MoleculeSource::serialize() const -{ - QJsonObject json = m_json; - // Serialize the modules... - auto modules = ModuleManager::instance().findModulesGeneric(this, nullptr); - QJsonArray jModules; - foreach (Module* module, modules) { - QJsonObject jModule = module->serialize(); - jModule["type"] = ModuleFactory::moduleType(module); - jModule["viewId"] = static_cast(module->view()->GetGlobalID()); - - jModules.append(jModule); - } - if (!jModules.isEmpty()) { - json["modules"] = jModules; - } - - return json; -} - -bool MoleculeSource::deserialize(const QJsonObject& state) -{ - // Check for modules on the data source first. - if (state.contains("modules") && state["modules"].isArray()) { - auto moduleArray = state["modules"].toArray(); - for (int i = 0; i < moduleArray.size(); ++i) { - auto moduleObj = moduleArray[i].toObject(); - auto viewId = moduleObj["viewId"].toInt(); - auto viewProxy = ModuleManager::instance().lookupView(viewId); - auto type = moduleObj["type"].toString(); - auto m = - ModuleManager::instance().createAndAddModule(type, this, viewProxy); - m->deserialize(moduleObj); - } - } - return true; -} - -void MoleculeSource::setFileName(const QString fileName) -{ - auto reader = m_json.value("reader").toObject(QJsonObject()); - QJsonValue file(fileName); - reader["fileName"] = file; - m_json["reader"] = reader; -} - -/// Returns the name of the file used to load the data source. -QString MoleculeSource::fileName() const -{ - auto reader = m_json.value("reader").toObject(QJsonObject()); - QString file; - if (reader.contains("fileName")) { - file = reader["fileName"].toString(); - } - return file; -} - -/// Set the label for the data source. -void MoleculeSource::setLabel(const QString& label) -{ - m_json["label"] = label; -} - -/// Returns the name of the filename used from the originalDataSource. -QString MoleculeSource::label() const -{ - if (m_json.contains("label")) { - return m_json["label"].toString(); - } else { - return QFileInfo(fileName()).baseName(); - } -} - -vtkMolecule* MoleculeSource::molecule() const -{ - return m_molecule.GetPointer(); -} - -} // namespace tomviz diff --git a/tomviz/MoleculeSource.h b/tomviz/MoleculeSource.h deleted file mode 100644 index 0ba7d9ef1..000000000 --- a/tomviz/MoleculeSource.h +++ /dev/null @@ -1,52 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizMoleculeSource_h -#define tomvizMoleculeSource_h - -#include - -#include - -#include - -class vtkMolecule; - -namespace tomviz { -class Pipeline; - -class MoleculeSource : public QObject -{ - Q_OBJECT - -public: - MoleculeSource(vtkMolecule* molecule, QObject* parent = nullptr); - ~MoleculeSource() override; - - /// Save the state out. - QJsonObject serialize() const; - bool deserialize(const QJsonObject& state); - - /// Set the file name. - void setFileName(QString label); - - /// Returns the file used to load the atoms. - QString fileName() const; - - /// Set the label for the data source. - void setLabel(const QString& label); - - /// Returns the name of the filename used from the originalDataSource. - QString label() const; - - /// Returns a pointer to the vtkMolecule - vtkMolecule* molecule() const; - -private: - QJsonObject m_json; - vtkSmartPointer m_molecule; -}; - -} // namespace tomviz - -#endif diff --git a/tomviz/PythonUtilities.cxx b/tomviz/PythonUtilities.cxx index 378b30e5b..496af6b8e 100644 --- a/tomviz/PythonUtilities.cxx +++ b/tomviz/PythonUtilities.cxx @@ -8,9 +8,6 @@ #include "vtkPython.h" // must be first #pragma pop_macro("slots") -#include "legacy/core/DataSourceBase.h" - -#include "legacy/DataSource.h" #include "Logger.h" #include @@ -24,6 +21,9 @@ #include #pragma pop_macro("slots") +#include +#include + namespace py = pybind11; namespace tomviz { @@ -90,13 +90,6 @@ Python::Object::Object(const Variant& value) m_smartPyObject = new vtkSmartPyObject(toPyObject(value)); } -Python::Object::Object(const DataSourceBase& source) -{ - // The vtkSmartPyObject will take ownership of the PyObject* - py::object obj = py::cast(source, py::return_value_policy::reference); - m_smartPyObject = new vtkSmartPyObject(obj.release().ptr()); -} - Python::Object::Object(PyObject* obj) { m_smartPyObject = new vtkSmartPyObject(obj); @@ -606,31 +599,6 @@ void Python::prependPythonPath(std::string dir) vtkPythonInterpreter::PrependPythonPath(dir.c_str()); } -Python::Object Python::createDataset(vtkObjectBase* data, - const DataSource& source) -{ - Python python; - auto module = python.import("tomviz.internal_dataset"); - if (!module.isValid()) { - Logger::critical("Failed to import tomviz.internal_dataset module."); - } - - auto createDatasetFunc = module.findFunction("create_dataset"); - if (!createDatasetFunc.isValid()) { - Logger::critical("Unable to locate create_dataset."); - return Python::Object(); - } - - auto dataObj = Python::VTK::GetObjectFromPointer(data); - auto dataSourceObj = Python::Object(*source.pythonProxy()); - - Python::Tuple args(2); - args.set(0, dataObj); - args.set(1, dataSourceObj); - - return createDatasetFunc.call(args); -} - std::vector findCustomOperators(const QString& path) { std::vector operators; diff --git a/tomviz/PythonUtilities.h b/tomviz/PythonUtilities.h index 7fe0a7349..af0018fd7 100644 --- a/tomviz/PythonUtilities.h +++ b/tomviz/PythonUtilities.h @@ -29,9 +29,6 @@ class capsule; namespace tomviz { -class DataSource; -class DataSourceBase; - class Python { @@ -60,7 +57,6 @@ class Python Object(const QList& intList); Object(const QList& floatList); Object(const Variant& value); - Object(const DataSourceBase& source); Object(PyObject* obj); Object& operator=(const Object& other); @@ -192,9 +188,6 @@ class Python /// vtkPythonPythonInterpreter::PrependPythonPath(...) to do the work. static void prependPythonPath(std::string dir); - /// Create an internal Dataset object for operators to use - static Object createDataset(vtkObjectBase* data, const DataSource& source); - private: vtkPythonScopeGilEnsurer* m_ensurer = nullptr; }; diff --git a/tomviz/ResetReaction.cxx b/tomviz/ResetReaction.cxx index 872d8020d..6cb22447e 100644 --- a/tomviz/ResetReaction.cxx +++ b/tomviz/ResetReaction.cxx @@ -5,7 +5,6 @@ #include "ActiveObjects.h" #include "HistogramManager.h" -#include "legacy/modules/ModuleManager.h" #include "pipeline/Pipeline.h" #include "Utilities.h" @@ -35,7 +34,6 @@ void ResetReaction::reset() return; } } - ModuleManager::instance().reset(); if (pipeline) { pipeline->clear(); } diff --git a/tomviz/SaveLoadStateReaction.cxx b/tomviz/SaveLoadStateReaction.cxx index e26cacfed..f42c84d4a 100644 --- a/tomviz/SaveLoadStateReaction.cxx +++ b/tomviz/SaveLoadStateReaction.cxx @@ -4,7 +4,7 @@ #include "SaveLoadStateReaction.h" #include "ActiveObjects.h" -#include "legacy/modules/ModuleManager.h" +#include "MainWindow.h" #include "pipeline/LegacyStateLoader.h" #include "pipeline/Pipeline.h" #include "pipeline/PipelineStateIO.h" @@ -75,7 +75,7 @@ bool SaveLoadStateReaction::saveState() if (success) { // Only set the most recent state file if the user picked a file // to save via a file dialog, and the save was successful. - ModuleManager::instance().setMostRecentStateFile(filename); + MainWindow::instance()->setMostRecentStateFile(filename); } return success; } @@ -127,7 +127,7 @@ bool SaveLoadStateReaction::loadState(const QString& filename) RecentFilesMenu::pushStateFile(filename); // Set the most recent state file if we successfully loaded a // state, whether it was done programmatically or via file dialog - ModuleManager::instance().setMostRecentStateFile(filename); + MainWindow::instance()->setMostRecentStateFile(filename); } return success; diff --git a/tomviz/SaveWebReaction.cxx b/tomviz/SaveWebReaction.cxx index 2f978a254..61ee5b040 100644 --- a/tomviz/SaveWebReaction.cxx +++ b/tomviz/SaveWebReaction.cxx @@ -4,7 +4,6 @@ #include "SaveWebReaction.h" #include "ActiveObjects.h" -#include "legacy/modules/ModuleManager.h" #include "pqActiveObjects.h" #include "pqCoreUtilities.h" diff --git a/tomviz/SetDataTypeReaction.cxx b/tomviz/SetDataTypeReaction.cxx index b388dfcde..c038a04db 100644 --- a/tomviz/SetDataTypeReaction.cxx +++ b/tomviz/SetDataTypeReaction.cxx @@ -16,7 +16,7 @@ namespace tomviz { SetDataTypeReaction::SetDataTypeReaction(QAction* action, QMainWindow* mw, - DataSource::DataSourceType t) + pipeline::PortType t) : pqReaction(action), m_mainWindow(mw), m_type(t) { connect(&ActiveObjects::instance(), &ActiveObjects::activeNodeChanged, this, @@ -29,21 +29,16 @@ SetDataTypeReaction::SetDataTypeReaction(QAction* action, QMainWindow* mw, } void SetDataTypeReaction::setDataType(QMainWindow* mw, DataSource*, - DataSource::DataSourceType t) + pipeline::PortType t) { Q_UNUSED(mw); - if (t == DataSource::TiltSeries) { + if (t == pipeline::PortType::TiltSeries) { auto* transform = new pipeline::SetTiltAnglesTransform(); insertTransformIntoPipeline(transform); } else { auto* transform = new pipeline::ConvertToVolumeTransform(); - if (t == DataSource::Volume) { - transform->setOutputType(pipeline::PortType::Volume); - transform->setOutputLabel("Mark as Volume"); - } else if (t == DataSource::FIB) { - transform->setOutputType(pipeline::PortType::Volume); - transform->setOutputLabel("Mark as FIB"); - } + transform->setOutputType(pipeline::PortType::Volume); + transform->setOutputLabel("Mark as Volume"); insertTransformIntoPipeline(transform); } } @@ -59,14 +54,12 @@ void SetDataTypeReaction::updateEnableState() parentAction()->setEnabled(ao.activeTipOutputPort() != nullptr); } -void SetDataTypeReaction::setWidgetText(DataSource::DataSourceType t) +void SetDataTypeReaction::setWidgetText(pipeline::PortType t) { - if (t == DataSource::Volume) { + if (t == pipeline::PortType::Volume) { parentAction()->setText("Mark Data As Volume"); - } else if (t == DataSource::TiltSeries) { + } else if (t == pipeline::PortType::TiltSeries) { parentAction()->setText("Mark Data As Tilt Series"); - } else if (t == DataSource::FIB) { - parentAction()->setText("Mark Data As Focused Ion Beam (FIB)"); } else { assert("Unknown data source type" && false); } diff --git a/tomviz/SetDataTypeReaction.h b/tomviz/SetDataTypeReaction.h index 5fe96c45a..08e5ce836 100644 --- a/tomviz/SetDataTypeReaction.h +++ b/tomviz/SetDataTypeReaction.h @@ -6,7 +6,7 @@ #include -#include "legacy/DataSource.h" +#include "pipeline/PortType.h" class QMainWindow; @@ -20,10 +20,10 @@ class SetDataTypeReaction : public pqReaction public: SetDataTypeReaction(QAction* action, QMainWindow* mw, - DataSource::DataSourceType t = DataSource::Volume); + pipeline::PortType t = pipeline::PortType::Volume); static void setDataType(QMainWindow* mw, DataSource* source = nullptr, - DataSource::DataSourceType t = DataSource::Volume); + pipeline::PortType t = pipeline::PortType::Volume); protected: /// Called when the action is triggered. @@ -32,9 +32,9 @@ class SetDataTypeReaction : public pqReaction private: QMainWindow* m_mainWindow; - DataSource::DataSourceType m_type; + pipeline::PortType m_type; - void setWidgetText(DataSource::DataSourceType t); + void setWidgetText(pipeline::PortType t); Q_DISABLE_COPY(SetDataTypeReaction) }; diff --git a/tomviz/SliceViewDialog.cxx b/tomviz/SliceViewDialog.cxx deleted file mode 100644 index 7316c5cc2..000000000 --- a/tomviz/SliceViewDialog.cxx +++ /dev/null @@ -1,137 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "SliceViewDialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "QVTKGLWidget.h" -#include "Utilities.h" - -namespace tomviz { - -SliceViewDialog::SliceViewDialog(QWidget* parent) : QDialog(parent) -{ - // Pick a reasonable size. It is very tiny otherwise. - resize(500, 500); - auto* vLayout = new QVBoxLayout(this); - - // Set the margins to all be 0 - vLayout->setContentsMargins(0, 0, 0, 0); - - m_glWidget = new QVTKGLWidget(this); - vLayout->addWidget(m_glWidget); - - m_slice->SetMapper(m_mapper); - m_renderer->AddViewProp(m_slice); - - m_glWidget->renderWindow()->AddRenderer(m_renderer); - - vtkNew interatorStyle; - interatorStyle->SetRenderOnMouseMove(true); - m_glWidget->interactor()->SetInteractorStyle(interatorStyle); - - // Add in the radio buttons - m_darkButton = new QRadioButton(this); - m_whiteButton = new QRadioButton(this); - - auto* buttonLayout = new QHBoxLayout; - vLayout->addLayout(buttonLayout); - - buttonLayout->addWidget(m_darkButton); - buttonLayout->addWidget(m_whiteButton); - buttonLayout->addStretch(1); - - m_darkButton->setText("Dark"); - m_whiteButton->setText("White"); - - setupConnections(); -} - -void SliceViewDialog::setupConnections() -{ - connect(m_darkButton, &QRadioButton::clicked, this, - &SliceViewDialog::switchToDark); - connect(m_whiteButton, &QRadioButton::clicked, this, - &SliceViewDialog::switchToWhite); -} - -SliceViewDialog::~SliceViewDialog() = default; - -void SliceViewDialog::setActiveImageData(vtkImageData* image) -{ - m_mapper->SetInputData(image); - setSliceNumber(0); - updateLUTRange(); - - // Set up the renderer to show the slice in parallel projection - // It also zooms the renderer so the entire slice is visible - tomviz::setupRenderer(m_renderer, m_mapper); - m_glWidget->renderWindow()->Render(); -} - -void SliceViewDialog::setSliceNumber(int slice) -{ - m_mapper->SetSliceNumber(slice); - m_mapper->Update(); -} - -void SliceViewDialog::setLookupTable(vtkScalarsToColors* lut) -{ - m_lut = lut; - m_slice->GetProperty()->SetLookupTable(lut); -} - -void SliceViewDialog::updateLUTRange() -{ - auto* image = m_mapper->GetInput(); - auto* ctf = vtkColorTransferFunction::SafeDownCast(m_lut.Get()); - - if (!image || !ctf) { - return; - } - - // Make a deep copy to put on the image property, so we can modify it - auto* lut = ctf->NewInstance(); - lut->DeepCopy(ctf); - m_slice->GetProperty()->SetLookupTable(lut); - // Decrement the reference count - lut->Delete(); - - // Rescale the points - double* range = image->GetScalarRange(); - rescaleLut(lut, range[0], range[1]); -} - -void SliceViewDialog::switchToDark() -{ - m_darkButton->setChecked(true); - m_whiteButton->setChecked(false); - - setActiveImageData(m_darkImage); -} - -void SliceViewDialog::switchToWhite() -{ - m_whiteButton->setChecked(true); - m_darkButton->setChecked(false); - - setActiveImageData(m_whiteImage); -} - -} // namespace tomviz diff --git a/tomviz/SliceViewDialog.h b/tomviz/SliceViewDialog.h deleted file mode 100644 index 01653e016..000000000 --- a/tomviz/SliceViewDialog.h +++ /dev/null @@ -1,63 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizSliceViewDialog_h -#define tomvizSliceViewDialog_h - -#include - -#include - -#include -#include -#include - -class QRadioButton; - -class vtkImageData; -class vtkImageSlice; -class vtkImageSliceMapper; -class vtkRenderer; -class vtkScalarsToColors; - -namespace tomviz { - -class QVTKGLWidget; - -class SliceViewDialog : public QDialog -{ - Q_OBJECT -public: - SliceViewDialog(QWidget* parent = nullptr); - ~SliceViewDialog(); - - void setActiveImageData(vtkImageData* image); - void setSliceNumber(int slice); - void setLookupTable(vtkScalarsToColors* lut); - - void setDarkImage(vtkImageData* image) { m_darkImage = image; } - void setWhiteImage(vtkImageData* image) { m_whiteImage = image; } - - void switchToDark(); - void switchToWhite(); - -private: - void setupConnections(); - void updateLUTRange(); - - vtkWeakPointer m_darkImage; - vtkWeakPointer m_whiteImage; - - QPointer m_glWidget; - QPointer m_darkButton; - QPointer m_whiteButton; - - vtkNew m_slice; - vtkNew m_mapper; - vtkNew m_renderer; - - vtkSmartPointer m_lut; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/TimeSeriesLabel.cxx b/tomviz/TimeSeriesLabel.cxx index 530bcaade..f67a181ae 100644 --- a/tomviz/TimeSeriesLabel.cxx +++ b/tomviz/TimeSeriesLabel.cxx @@ -4,10 +4,12 @@ #include "TimeSeriesLabel.h" #include "ActiveObjects.h" -#include "legacy/DataSource.h" -#include "TimeSeriesStep.h" #include "Utilities.h" +#include "pipeline/OutputPort.h" +#include "pipeline/PortData.h" +#include "pipeline/data/VolumeData.h" + #include #include @@ -31,7 +33,6 @@ class TimeSeriesLabel::Internal : public QObject vtkNew textRepresentation; vtkNew textWidget; - QPointer activeDataSource; QPointer activeView; Internal(QObject* p) : QObject(p) @@ -51,11 +52,8 @@ class TimeSeriesLabel::Internal : public QObject connect(&activeObjects(), QOverload::of(&ActiveObjects::viewChanged), this, &Internal::viewChanged); - connect(&activeObjects(), &ActiveObjects::activeNodeChanged, this, - [this]() { - // TODO: extract DataSource from active node/port - dataSourceActivated(nullptr); - }); + connect(&activeObjects(), &ActiveObjects::activeTipOutputPortChanged, this, + &Internal::activeDataChanged); connect(&activeObjects(), &ActiveObjects::showTimeSeriesLabelChanged, this, &Internal::updateVisibility); } @@ -81,42 +79,40 @@ class TimeSeriesLabel::Internal : public QObject render(); } - void dataSourceActivated(DataSource* ds) + // The VolumeData on the active tip output port, or null. + pipeline::VolumeDataPtr activeVolumeData() { - if (ds == activeDataSource) { - return; + auto* port = activeObjects().activeTipOutputPort(); + if (!port || !port->hasData()) { + return nullptr; } - - if (activeDataSource) { - disconnect(activeDataSource); - } - - if (ds) { - connect(ds, &DataSource::timeStepsModified, this, - &Internal::timeStepsModified); - connect(ds, &DataSource::timeStepChanged, this, - &Internal::timeStepChanged); + try { + return port->data().value(); + } catch (const std::bad_any_cast&) { + return nullptr; } - - activeDataSource = ds; - updateVisibility(); - timeStepChanged(); } - void timeStepsModified() + void activeDataChanged() { - // In case there wasn't a time series before... updateVisibility(); - timeStepChanged(); + updateLabel(); } - void timeStepChanged() + void updateLabel() { - if (!activeDataSource || !activeDataSource->hasTimeSteps()) { + auto vol = activeVolumeData(); + if (!vol || !vol->hasTimeSteps()) { + return; + } + + auto steps = vol->timeSteps(); + int index = vol->currentTimeStepIndex(); + if (index < 0 || index >= static_cast(steps.size())) { return; } - auto label = activeDataSource->currentTimeSeriesStep().label; + auto label = steps[index].label; if (label == textActor->GetInput()) { // No changes needed return; @@ -128,10 +124,12 @@ class TimeSeriesLabel::Internal : public QObject void updateVisibility() { - // If we have an interactor and the settings indicate it should + // Show if the setting is enabled, we have an interactor, and the active + // data is a time series. bool show = activeObjects().showTimeSeriesLabel(); bool hasInteractor = textWidget->GetInteractor() != nullptr; - bool hasTimeSteps = activeDataSource && activeDataSource->hasTimeSteps(); + auto vol = activeVolumeData(); + bool hasTimeSteps = vol && vol->hasTimeSteps(); bool visible = show && hasInteractor && hasTimeSteps; diff --git a/tomviz/Utilities.cxx b/tomviz/Utilities.cxx index 39d68e569..808dd4577 100644 --- a/tomviz/Utilities.cxx +++ b/tomviz/Utilities.cxx @@ -4,7 +4,6 @@ #include "Utilities.h" #include "ActiveObjects.h" -#include "legacy/DataSource.h" #include "tomvizConfig.h" #include @@ -62,6 +61,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -584,14 +586,13 @@ vtkPVArrayInformation* scalarArrayInformation(vtkSMSourceProxy* proxy) : nullptr; } -bool rescaleColorMap(vtkSMProxy* colorMap, DataSource* dataSource) +bool rescaleColorMap(vtkSMProxy* colorMap, vtkSMSourceProxy* dataProxy) { // rescale the color/opacity maps for the data source. vtkSMProxy* cmap = colorMap; vtkSMProxy* omap = vtkSMPropertyHelper(cmap, "ScalarOpacityFunction").GetAsProxy(); - vtkPVArrayInformation* ainfo = - tomviz::scalarArrayInformation(dataSource->proxy()); + vtkPVArrayInformation* ainfo = tomviz::scalarArrayInformation(dataProxy); if (ainfo != nullptr && vtkSMPropertyHelper(cmap, "AutomaticRescaleRangeMode").GetAsInt() != vtkSMTransferFunctionManager::NEVER) { @@ -1504,13 +1505,6 @@ void addPlaceholderNodes(vtkColorTransferFunction* lut, const double range[2]) } } -void addPlaceholderNodes(vtkColorTransferFunction* lut, DataSource* ds) -{ - double range[2]; - ds->getRange(range); - addPlaceholderNodes(lut, range); -} - void removePlaceholderNodes(vtkColorTransferFunction* lut) { // Remove all nodes on the ends that match their neighboring nodes @@ -1574,13 +1568,6 @@ void addPlaceholderNodes(vtkPiecewiseFunction* opacity, const double range[2]) } } -void addPlaceholderNodes(vtkPiecewiseFunction* opacity, DataSource* ds) -{ - double range[2]; - ds->getRange(range); - addPlaceholderNodes(opacity, range); -} - void removePlaceholderNodes(vtkPiecewiseFunction* opacity) { // Remove all nodes on the ends that match their neighboring nodes @@ -1713,13 +1700,6 @@ void removePointsOutOfRange(vtkColorTransferFunction* lut, lut->AddRGBPoint(range[1], endColor[0], endColor[1], endColor[2]); } -void removePointsOutOfRange(vtkColorTransferFunction* lut, DataSource* ds) -{ - double range[2]; - ds->getRange(range); - removePointsOutOfRange(lut, range); -} - void removePointsOutOfRange(vtkPiecewiseFunction* opacity, const double range[2]) { @@ -1748,13 +1728,6 @@ void removePointsOutOfRange(vtkPiecewiseFunction* opacity, opacity->AddPoint(range[1], endY); } -void removePointsOutOfRange(vtkPiecewiseFunction* opacity, DataSource* ds) -{ - double range[2]; - ds->getRange(range); - removePointsOutOfRange(opacity, range); -} - bool loadPlugin(QString path) { if (!path.startsWith("/")) { diff --git a/tomviz/Utilities.h b/tomviz/Utilities.h index fdb3fca0b..ccfa7b747 100644 --- a/tomviz/Utilities.h +++ b/tomviz/Utilities.h @@ -53,8 +53,6 @@ struct Attributes static const char* FILENAME; }; -class DataSource; - //=========================================================================== // Functions for converting from pqProxy to vtkSMProxy and vice-versa. //=========================================================================== @@ -163,7 +161,7 @@ vtkPVArrayInformation* scalarArrayInformation(vtkSMSourceProxy* proxy); /// Rescales the colorMap (and associated opacityMap) using the transformed data /// range from the data source. This will respect the "LockScalarRange" property /// on the colorMap i.e. if user locked the scalar range, it won't be rescaled. -bool rescaleColorMap(vtkSMProxy* colorMap, DataSource* dataSource); +bool rescaleColorMap(vtkSMProxy* colorMap, vtkSMSourceProxy* dataProxy); // Given the root of a file and an extension, reades the file fileName + // extension and returns the content in a QString. @@ -299,11 +297,9 @@ QString getSizeNearestThousand(T num, bool labelAsBytes = false) } void addPlaceholderNodes(vtkColorTransferFunction* lut, const double range[2]); -void addPlaceholderNodes(vtkColorTransferFunction* lut, DataSource* ds); void removePlaceholderNodes(vtkColorTransferFunction* lut); void addPlaceholderNodes(vtkPiecewiseFunction* opacity, const double range[2]); -void addPlaceholderNodes(vtkPiecewiseFunction* opacity, DataSource* ds); void removePlaceholderNodes(vtkPiecewiseFunction* opacity); double rescale(double val, double oldMin, double oldMax, double newMin, @@ -312,10 +308,8 @@ void rescaleNodes(vtkColorTransferFunction* lut, double newMin, double newMax); void rescaleNodes(vtkPiecewiseFunction* opacity, double newMin, double newMax); void removePointsOutOfRange(vtkColorTransferFunction* lut, const double range[2]); -void removePointsOutOfRange(vtkColorTransferFunction* lut, DataSource* ds); void removePointsOutOfRange(vtkPiecewiseFunction* opacity, const double range[2]); -void removePointsOutOfRange(vtkPiecewiseFunction* opacity, DataSource* ds); // Load a plugin by path bool loadPlugin(QString path); diff --git a/tomviz/ViewMenuManager.cxx b/tomviz/ViewMenuManager.cxx index dc133cdad..59e8c8d79 100644 --- a/tomviz/ViewMenuManager.cxx +++ b/tomviz/ViewMenuManager.cxx @@ -33,7 +33,6 @@ #include "ActiveObjects.h" #include "CameraReaction.h" -#include "legacy/DataSource.h" #include "pipeline/InputPort.h" #include "pipeline/Link.h" #include "pipeline/OutputPort.h" @@ -44,7 +43,6 @@ #include "pipeline/data/VolumeData.h" #include "pipeline/sinks/LegacyModuleSink.h" #include "pipeline/sinks/SliceSink.h" -#include "SliceViewDialog.h" #include "Utilities.h" namespace tomviz { @@ -84,11 +82,6 @@ ViewMenuManager::ViewMenuManager(QMainWindow* mainWindow, QMenu* menu) &ActiveObjects::viewChanged), this, &ViewMenuManager::onViewChanged); - connect(&ActiveObjects::instance(), &ActiveObjects::activeNodeChanged, this, - [this]() { - // TODO: extract DataSource from active node/port - updateDataSource(nullptr); - }); connect(&ActiveObjects::instance(), &ActiveObjects::setImageViewerMode, this, &ViewMenuManager::setImageViewerMode); @@ -132,13 +125,6 @@ ViewMenuManager::ViewMenuManager(QMainWindow* mainWindow, QMenu* menu) connect(m_imageViewerModeAction, &QAction::triggered, this, &ViewMenuManager::setImageViewerMode); - // FIXME: staged for removal - m_showDarkWhiteDataAction = Menu->addAction("Show Dark/White Data"); - m_showDarkWhiteDataAction->setEnabled(false); - m_showDarkWhiteDataAction->setVisible(false); - connect(m_showDarkWhiteDataAction, &QAction::triggered, this, - &ViewMenuManager::showDarkWhiteData); - m_previousImageViewerSettings.reset(new PreviousImageViewerSettings); if (hasLookingGlassPlugin()) { @@ -539,41 +525,6 @@ void ViewMenuManager::restoreImageViewerSettings() view->StillRender(); } -void ViewMenuManager::updateDataSource(DataSource* s) -{ - m_dataSource = s; - updateDataSourceEnableStates(); -} - -void ViewMenuManager::updateDataSourceEnableStates() -{ - // Currently, both white and dark are required to use this - // We can change this in the future if needed... - m_showDarkWhiteDataAction->setEnabled( - m_dataSource && m_dataSource->darkData() && m_dataSource->whiteData()); -} - -void ViewMenuManager::showDarkWhiteData() -{ - if (!m_dataSource || !m_dataSource->darkData() || - !m_dataSource->whiteData()) { - return; - } - - if (!m_sliceViewDialog) { - m_sliceViewDialog.reset(new SliceViewDialog); - } - - auto* lut = vtkColorTransferFunction::SafeDownCast( - m_dataSource->colorMap()->GetClientSideObject()); - - m_sliceViewDialog->setLookupTable(lut); - m_sliceViewDialog->setDarkImage(m_dataSource->darkData()); - m_sliceViewDialog->setWhiteImage(m_dataSource->whiteData()); - m_sliceViewDialog->switchToDark(); - - m_sliceViewDialog->exec(); -} void ViewMenuManager::setupLookingGlassPlaceholder(QMainWindow* mainWindow) { diff --git a/tomviz/ViewMenuManager.h b/tomviz/ViewMenuManager.h index ae92ae95c..e4e73dc7d 100644 --- a/tomviz/ViewMenuManager.h +++ b/tomviz/ViewMenuManager.h @@ -17,9 +17,7 @@ class vtkSMViewProxy; namespace tomviz { -class DataSource; class PreviousImageViewerSettings; -class SliceViewDialog; namespace pipeline { class SliceSink; @@ -55,15 +53,10 @@ private slots: void setShowOrientationAxes(bool show); void setImageViewerMode(bool b); - void showDarkWhiteData(); - private: void setScaleLegendStyle(ScaleLegendStyle); void setScaleLegendVisibility(bool); - void updateDataSource(DataSource* s); - void updateDataSourceEnableStates(); - void render(); void restoreImageViewerSettings(); @@ -75,12 +68,9 @@ private slots: QPointer m_showCenterAxesAction; QPointer m_showOrientationAxesAction; QPointer m_imageViewerModeAction; - QPointer m_showDarkWhiteDataAction; QScopedPointer m_previousImageViewerSettings; - QScopedPointer m_sliceViewDialog; - DataSource* m_dataSource = nullptr; vtkSMViewProxy* m_view; unsigned long m_viewObserverId; bool m_imageViewerMode = false; diff --git a/tomviz/WelcomeDialog.cxx b/tomviz/WelcomeDialog.cxx index 8b8ea6230..84331a89a 100644 --- a/tomviz/WelcomeDialog.cxx +++ b/tomviz/WelcomeDialog.cxx @@ -6,7 +6,6 @@ #include "ActiveObjects.h" #include "MainWindow.h" -#include "legacy/modules/ModuleManager.h" #include #include diff --git a/tomviz/acquisition/AdvancedFormatWidget.cxx b/tomviz/acquisition/AdvancedFormatWidget.cxx deleted file mode 100644 index 3cb9080d9..000000000 --- a/tomviz/acquisition/AdvancedFormatWidget.cxx +++ /dev/null @@ -1,120 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "AdvancedFormatWidget.h" - -#include "ui_AdvancedFormatWidget.h" -#include - -namespace tomviz { - -AdvancedFormatWidget::AdvancedFormatWidget(QWidget* parent) - : QWidget(parent), m_ui(new Ui::AdvancedFormatWidget) -{ - m_ui->setupUi(this); - - // Default file name regex - m_fileNameRegex = QString(".*_([n,p]{1}[\\d,\\.]+)degree.*\\.dm3"); - m_ui->fileNameRegexLineEdit->setText(m_fileNameRegex); - - connect(m_ui->fileNameRegexLineEdit, &QLineEdit::textChanged, - [this](QString pattern) { - setEnabledRegexGroupsWidget(!pattern.isEmpty()); - m_fileNameRegex = pattern; - emit regexChanged(pattern); - }); - setEnabledRegexGroupsWidget(!m_ui->fileNameRegexLineEdit->text().isEmpty()); - - connect(m_ui->regexGroupsWidget, &RegexGroupsWidget::groupsChanged, [this]() { - emit regexChanged(m_fileNameRegex); - setEnabledRegexGroupsSubstitutionsWidget( - !m_ui->regexGroupsWidget->regexGroups().isEmpty()); - }); - setEnabledRegexGroupsSubstitutionsWidget( - !m_ui->regexGroupsWidget->regexGroups().isEmpty()); - - // Setup regex error label - auto palette = m_regexErrorLabel.palette(); - palette.setColor(m_regexErrorLabel.foregroundRole(), Qt::red); - m_regexErrorLabel.setPalette(palette); - - connect(m_ui->fileNameRegexLineEdit, &QLineEdit::textChanged, [this]() { - m_ui->gridLayout->removeWidget(&m_regexErrorLabel); - m_regexErrorLabel.setText(""); - }); -} - -AdvancedFormatWidget::~AdvancedFormatWidget() = default; - -void AdvancedFormatWidget::setEnabledRegexGroupsWidget(bool enabled) -{ - m_ui->regexGroupsLabel->setEnabled(enabled); - m_ui->regexGroupsWidget->setEnabled(enabled); -} - -void AdvancedFormatWidget::setEnabledRegexGroupsSubstitutionsWidget( - bool enabled) -{ - m_ui->regexGroupsSubstitutionsLabel->setEnabled(enabled); - m_ui->regexGroupsSubstitutionsWidget->setEnabled(enabled); -} - -MatchInfo AdvancedFormatWidget::matchFileName(QString fileName) const -{ - QRegExp regex = QRegExp(m_fileNameRegex); - bool match = regex.exactMatch(fileName); - MatchInfo result; - result.matched = match; - QList groups; - QStringList namedGroups = m_ui->regexGroupsWidget->regexGroups(); - - if (namedGroups.size() == 0) { - groups << CapGroup(QString("Full match"), regex.cap(0)); - } else { - for (int i = 0; i < namedGroups.size(); ++i) { - groups << CapGroup(namedGroups[i], regex.cap(i + 1)); - } - } - - result.groups = groups; - return result; -} - -QJsonArray AdvancedFormatWidget::getRegexGroups() const -{ - auto regexGroups = m_ui->regexGroupsWidget->regexGroups(); - return QJsonArray::fromStringList(regexGroups); -} - -QJsonObject AdvancedFormatWidget::getRegexSubsitutions() const -{ - auto regexSubstitutions = - m_ui->regexGroupsSubstitutionsWidget->substitutions(); - QJsonObject substitutions; - foreach (RegexGroupSubstitution sub, regexSubstitutions) { - QJsonArray regexToSubs; - if (substitutions.contains(sub.groupName())) { - regexToSubs = substitutions.value(sub.groupName()).toArray(); - } - QJsonObject mapping; - mapping[sub.regex()] = sub.substitution(); - regexToSubs.append(mapping); - substitutions[sub.groupName()] = regexToSubs; - } - return substitutions; -} - -QString AdvancedFormatWidget::getRegex() const -{ - return m_fileNameRegex; -} - -QString AdvancedFormatWidget::getPythonRegex() const -{ - QString regex = m_fileNameRegex; - // Let's not replace it if it isn't needed - regex.replace(QString(".*?"), QString(".*")); - regex.replace(QString(".*"), QString(".*?")); - return regex; -} -} diff --git a/tomviz/acquisition/AdvancedFormatWidget.h b/tomviz/acquisition/AdvancedFormatWidget.h deleted file mode 100644 index 904e1d3aa..000000000 --- a/tomviz/acquisition/AdvancedFormatWidget.h +++ /dev/null @@ -1,54 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizAdvancedFormatWidget_h -#define tomvizAdvancedFormatWidget_h - -#include - -#include "MatchInfo.h" - -#include -#include -#include -#include - -namespace Ui { -class AdvancedFormatWidget; -} - -namespace tomviz { -class AdvancedFormatWidget : public QWidget -{ - Q_OBJECT - -public: - AdvancedFormatWidget(QWidget* parent = nullptr); - ~AdvancedFormatWidget() override; - - QString getRegex() const; - QString getPythonRegex() const; - MatchInfo matchFileName(QString) const; - QJsonArray getRegexGroups() const; - QJsonObject getRegexSubsitutions() const; - -public slots: - -private slots: - -signals: - void regexChanged(QString); - -private: - QScopedPointer m_ui; - - QLabel m_regexErrorLabel; - - QString m_fileNameRegex; - - void setEnabledRegexGroupsWidget(bool enabled); - void setEnabledRegexGroupsSubstitutionsWidget(bool enabled); -}; -} - -#endif diff --git a/tomviz/acquisition/AdvancedFormatWidget.ui b/tomviz/acquisition/AdvancedFormatWidget.ui deleted file mode 100644 index 9992a4a61..000000000 --- a/tomviz/acquisition/AdvancedFormatWidget.ui +++ /dev/null @@ -1,62 +0,0 @@ - - - AdvancedFormatWidget - - - - - - File Name Regex - - - - - - - - - - - Regex Group Names - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - - Group Substitutions - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - - tomviz::RegexGroupsWidget - QWidget -
RegexGroupsWidget.h
- 1 -
- - tomviz::RegexGroupsSubstitutionsWidget - QWidget -
RegexGroupsSubstitutionsWidget.h
- 1 -
-
- - -
diff --git a/tomviz/acquisition/BasicFormatWidget.cxx b/tomviz/acquisition/BasicFormatWidget.cxx deleted file mode 100644 index 0c7f79530..000000000 --- a/tomviz/acquisition/BasicFormatWidget.cxx +++ /dev/null @@ -1,386 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "BasicFormatWidget.h" - -#include "ui_BasicFormatWidget.h" - -#include -#include - -namespace tomviz { - -BasicFormatWidget::BasicFormatWidget(QWidget* parent) - : QWidget(parent), m_ui(new Ui::BasicFormatWidget), - m_defaultFormatOrder(makeDefaultFormatOrder()), - m_defaultExtensionOrder(makeDefaultExtensionOrder()), - m_defaultFileNames(makeDefaultFileNames()), - m_defaultFormatLabels(makeDefaultFormatLabels()), - m_defaultExtensionLabels(makeDefaultExtensionLabels()), - m_defaultRegexParams(makeDefaultRegexParams()) -{ - m_ui->setupUi(this); - - connect(m_ui->fileFormatCombo, static_cast( - &QComboBox::currentIndexChanged), - this, &BasicFormatWidget::onFormatChanged); - - connect(m_ui->fileExtensionCombo, static_cast( - &QComboBox::currentIndexChanged), - this, &BasicFormatWidget::onExtensionChanged); - - setupFileFormatCombo(); - setupFileExtensionCombo(); - setupRegexDisplayLine(); - - connect(m_ui->customFormatWidget, &CustomFormatWidget::fieldsChanged, this, - &BasicFormatWidget::customFileRegex); -} - -BasicFormatWidget::~BasicFormatWidget() = default; - -void BasicFormatWidget::setupFileFormatCombo() -{ - foreach (auto format, m_defaultFormatOrder) { - m_ui->fileFormatCombo->addItem(m_defaultFormatLabels[format]); - } -} - -void BasicFormatWidget::setupFileExtensionCombo() -{ - foreach (auto format, m_defaultExtensionOrder) { - m_ui->fileExtensionCombo->addItem(m_defaultExtensionLabels[format]); - } -} - -void BasicFormatWidget::onFormatChanged(int index) -{ - if (index >= m_defaultFormatOrder.size() || index < 0) { - return; - } - - m_format = m_defaultFormatOrder[index]; - - switch (m_format) { - case RegexFormat::Custom: - m_ui->customGroupBox->setVisible(true); - m_ui->customGroupBox->setEnabled(true); - m_ui->fileExtensionCombo->setEnabled(false); - break; - default: - m_ui->customGroupBox->setVisible(true); - m_ui->customGroupBox->setEnabled(false); - m_ui->fileExtensionCombo->setEnabled(true); - } - updateRegex(); - emit fileFormatChanged(); -} - -void BasicFormatWidget::onExtensionChanged(int index) -{ - if (index >= m_defaultExtensionOrder.size() || index < 0) { - return; - } - m_extension = m_defaultExtensionOrder[index]; - updateRegex(); - emit fileFormatChanged(); -} - -void BasicFormatWidget::updateRegex() -{ - QStringList params = - m_defaultRegexParams[std::make_pair(m_format, m_extension)]; - if (params.size() != 5) { - qCritical() << "Default Regex Parameters size not equal to 5"; - return; - } - buildFileRegex(params[0], params[1], params[2], params[3], params[4]); - - m_ui->customFormatWidget->setFields(params); -} - -void BasicFormatWidget::buildFileRegex(QString prefix, QString negChar, - QString posChar, QString suffix, - QString extension) -{ - // Greediness differences between Qt regex engine in the client, - // and the python regex engine on the server, - // require adjustments to the pattern - if (prefix.trimmed() == "") { - prefix = QString("*"); - } - QString pythonPrefix = prefix; - prefix.replace(QString("*"), QString(".*")); - pythonPrefix.replace(QString("*"), QString(".*?")); - - if (suffix.trimmed() == "") { - suffix = QString("*"); - } - QString pythonSuffix = suffix; - suffix.replace(QString("*"), QString(".*")); - pythonSuffix.replace(QString("*"), QString(".*?")); - - if (extension.trimmed() == "" || extension.trimmed() == "*") { - extension = QString(".+"); - } - if (negChar.trimmed() == "") { - negChar = QString("n"); - } - if (posChar.trimmed() == "") { - posChar = QString("p"); - } - - m_fileNameRegex = QString("^%1((%2|%3)(\\d+(\\.\\d+)?))%4(\\.%5)$") - .arg(prefix) - .arg(QRegExp::escape(negChar)) - .arg(QRegExp::escape(posChar)) - .arg(suffix) - .arg(extension); - - m_pythonFileNameRegex = QString("^%1((%2|%3)(\\d+(\\.\\d+)?))%4(\\.%5)$") - .arg(pythonPrefix) - .arg(QRegExp::escape(negChar)) - .arg(QRegExp::escape(posChar)) - .arg(pythonSuffix) - .arg(extension); - m_negChar = negChar; - m_posChar = posChar; - - m_ui->regexDisplay->setText(m_fileNameRegex); - emit regexChanged(m_fileNameRegex); -} - -void BasicFormatWidget::customFileRegex() -{ - QStringList fields = m_ui->customFormatWidget->getFields(); - if (fields.size() != 5) { - qCritical() << "Field Regex Parameters size not equal to 5"; - return; - } - buildFileRegex(fields[0], fields[1], fields[2], fields[3], fields[4]); -} - -MatchInfo BasicFormatWidget::matchFileName(QString fileName) const -{ - QString prefix; - QString suffix; - QString sign; - QString numStr; - QString ext; - double num; - bool valid = false; - QRegExp regex = QRegExp(m_fileNameRegex); - bool match = regex.exactMatch(fileName); - - if (match) { - valid = true; - - sign = regex.cap(2); - numStr = regex.cap(3); - ext = regex.cap(5); - - int start = 0; - int stop = 0; - stop = regex.pos(1); - prefix = fileName.left(stop); - start = regex.pos(1) + regex.cap(1).size(); - stop = regex.pos(5); - suffix = fileName.mid(start, stop - start); - - if (sign == m_posChar) { - sign = "+"; - } else if (sign == m_negChar) { - sign = "-"; - } - num = (sign + numStr).toFloat(); - - // Special case: only 0 can be missing a positive/negative identifier - if (sign.trimmed() == "" && num != 0) { - valid = false; - } - } - - if (!valid) { - prefix = ""; - suffix = ""; - sign = ""; - numStr = ""; - ext = ""; - } - - MatchInfo result; - result.matched = valid; - QList groups; - groups << CapGroup("Prefix", prefix); - groups << CapGroup("Angle", sign + numStr); - groups << CapGroup("Suffix", suffix); - groups << CapGroup("Ext", ext); - result.groups = groups; - return result; -} - -QString BasicFormatWidget::getRegex() const -{ - return m_fileNameRegex; -} - -QString BasicFormatWidget::getPythonRegex() const -{ - return m_pythonFileNameRegex; -} - -QJsonArray BasicFormatWidget::getRegexGroups() const -{ - return QJsonArray::fromStringList(QStringList(QString("angle"))); -} - -QJsonObject BasicFormatWidget::getRegexSubsitutions() const -{ - QJsonObject substitutions; - QJsonArray regexToSubs; - QJsonObject sub0; - sub0[QRegExp::escape(m_posChar)] = "+"; - QJsonObject sub1; - sub1[QRegExp::escape(m_negChar)] = "-"; - regexToSubs.append(sub0); - regexToSubs.append(sub1); - substitutions["angle"] = regexToSubs; - return substitutions; -} - -bool BasicFormatWidget::isDefaultFilename(const QString& fileName) const -{ - foreach (auto format, m_defaultFormatOrder) { - foreach (auto extension, m_defaultExtensionOrder) { - if (fileName == m_defaultFileNames[std::make_pair(format, extension)]) { - return true; - } - } - } - return false; -} - -QString BasicFormatWidget::getDefaultFilename() const -{ - return m_defaultFileNames[std::make_pair(m_format, m_extension)]; -} - -void BasicFormatWidget::setupRegexDisplayLine() -{ - QLineEdit* display = m_ui->regexDisplay; - display->setReadOnly(true); - QPalette pal = display->palette(); - QColor disabledBg = pal.color(QPalette::Disabled, QPalette::Base); - pal.setColor(QPalette::Active, QPalette::Base, disabledBg); - pal.setColor(QPalette::Inactive, QPalette::Base, disabledBg); - display->setPalette(pal); -} - -QMap, QString> -BasicFormatWidget::makeDefaultFileNames() const -{ - QMap, QString> defaultFilenames; - QString fileBase; - QString fileExt; - foreach (auto format, m_defaultFormatOrder) { - foreach (auto extension, m_defaultExtensionOrder) { - fileBase = QString(""); - fileExt = QString(""); - if (format == RegexFormat::NegativePositive) { - fileBase = QString("Prefix_n12.3_Suffix"); - } else if (format == RegexFormat::PlusMinus) { - fileBase = QString("Prefix_+12.3_Suffix"); - } - - if (format != RegexFormat::Custom) { - if (extension == RegexExtension::dm3) { - fileExt = QString(".dm3"); - } else if (extension == RegexExtension::tiff) { - fileExt = QString(".tiff"); - } - } - - defaultFilenames[std::make_pair(format, extension)] = - QString("%1%2").arg(fileBase).arg(fileExt); - } - } - return defaultFilenames; -} - -QMap BasicFormatWidget::makeDefaultFormatLabels() const -{ - QMap defaultLabels; - defaultLabels[RegexFormat::NegativePositive] = - QString("[n|p]"); - defaultLabels[RegexFormat::PlusMinus] = - QString("[-|+]"); - defaultLabels[RegexFormat::Custom] = QString("Custom"); - - return defaultLabels; -} - -QMap BasicFormatWidget::makeDefaultExtensionLabels() - const -{ - QMap defaultLabels; - defaultLabels[RegexExtension::dm3] = QString(".dm3"); - defaultLabels[RegexExtension::tiff] = QString(".tiff"); - - return defaultLabels; -} - -QMap, QStringList> -BasicFormatWidget::makeDefaultRegexParams() const -{ - QMap, QStringList> defaultRegexParams; - foreach (auto format, m_defaultFormatOrder) { - foreach (auto extension, m_defaultExtensionOrder) { - QStringList params; - - if (format == RegexFormat::NegativePositive) { - params << "*" - << "n" - << "p" - << "*"; - } else if (format == RegexFormat::PlusMinus) { - params << "*" - << "-" - << "+" - << "*"; - } else if (format == RegexFormat::Custom) { - params << "*" - << "n" - << "p" - << "*"; - } - - if (format == RegexFormat::Custom) { - params << "*"; - } else if (extension == RegexExtension::dm3) { - params << "dm3"; - } else if (extension == RegexExtension::tiff) { - params << "tif[f]?"; - } - - defaultRegexParams[std::make_pair(format, extension)] = params; - } - } - - return defaultRegexParams; -} - -QList BasicFormatWidget::makeDefaultFormatOrder() const -{ - QList defaultOrder; - defaultOrder << RegexFormat::NegativePositive << RegexFormat::PlusMinus - << RegexFormat::Custom; - return defaultOrder; -} - -QList BasicFormatWidget::makeDefaultExtensionOrder() const -{ - QList defaultOrder; - defaultOrder << RegexExtension::dm3 << RegexExtension::tiff; - return defaultOrder; -} -} diff --git a/tomviz/acquisition/BasicFormatWidget.h b/tomviz/acquisition/BasicFormatWidget.h deleted file mode 100644 index 30ed493ee..000000000 --- a/tomviz/acquisition/BasicFormatWidget.h +++ /dev/null @@ -1,101 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizBasicFormatWidget_h -#define tomvizBasicFormatWidget_h - -#include - -#include "MatchInfo.h" - -#include -#include -#include -#include -#include -#include - -namespace Ui { -class BasicFormatWidget; -} - -namespace tomviz { - -enum class RegexFormat -{ - NegativePositive, - PlusMinus, - Custom -}; - -enum class RegexExtension -{ - tiff, - dm3 -}; - -class BasicFormatWidget : public QWidget -{ - Q_OBJECT - -public: - BasicFormatWidget(QWidget* parent = nullptr); - ~BasicFormatWidget() override; - - QString getRegex() const; - QString getPythonRegex() const; - MatchInfo matchFileName(QString) const; - QJsonArray getRegexGroups() const; - QJsonObject getRegexSubsitutions() const; - bool isDefaultFilename(const QString& fileName) const; - QString getDefaultFilename() const; - -private slots: - void onFormatChanged(int); - void onExtensionChanged(int); - -signals: - void fileFormatChanged(); - void regexChanged(QString); - -private: - QScopedPointer m_ui; - - QString m_fileNameRegex; - QString m_negChar; - QString m_posChar; - QString m_pythonFileNameRegex; - QString m_testFileName; - RegexFormat m_format = RegexFormat::NegativePositive; - RegexExtension m_extension = RegexExtension::dm3; - - void setupFileFormatCombo(); - void setupFileExtensionCombo(); - void setupRegexDisplayLine(); - - QList makeDefaultFormatOrder() const; - QList makeDefaultExtensionOrder() const; - QMap, QString> makeDefaultFileNames() - const; - QMap makeDefaultFormatLabels() const; - QMap makeDefaultExtensionLabels() const; - QMap, QStringList> - makeDefaultRegexParams() const; - - const QList m_defaultFormatOrder; - const QList m_defaultExtensionOrder; - const QMap, QString> - m_defaultFileNames; - const QMap m_defaultFormatLabels; - const QMap m_defaultExtensionLabels; - const QMap, QStringList> - m_defaultRegexParams; - - void updateRegex(); - void buildFileRegex(QString, QString, QString, QString, QString); - void customFileRegex(); - void validateFileNameRegex(); -}; -} - -#endif diff --git a/tomviz/acquisition/BasicFormatWidget.ui b/tomviz/acquisition/BasicFormatWidget.ui deleted file mode 100644 index 7ed6f760c..000000000 --- a/tomviz/acquisition/BasicFormatWidget.ui +++ /dev/null @@ -1,92 +0,0 @@ - - - BasicFormatWidget - - - - 0 - 0 - 296 - 166 - - - - - - - File Name Format - - - - - - - - - true - - - - 0 - 0 - - - - - - - - true - - - - - - - - - Resulting Regex - - - - - - - - - - Custom format - - - - - - - - - - - - Qt::Vertical - - - - 0 - 30 - - - - - - - - - tomviz::CustomFormatWidget - QWidget -
CustomFormatWidget.h
- 1 -
-
- - -
diff --git a/tomviz/acquisition/ConnectionsWidget.cxx b/tomviz/acquisition/ConnectionsWidget.cxx index 789604685..8a6c2ded5 100644 --- a/tomviz/acquisition/ConnectionsWidget.cxx +++ b/tomviz/acquisition/ConnectionsWidget.cxx @@ -7,7 +7,6 @@ #include "ActiveObjects.h" #include "MainWindow.h" -#include "legacy/modules/ModuleManager.h" #include #include diff --git a/tomviz/acquisition/CustomFormatWidget.cxx b/tomviz/acquisition/CustomFormatWidget.cxx deleted file mode 100644 index 04869d2d8..000000000 --- a/tomviz/acquisition/CustomFormatWidget.cxx +++ /dev/null @@ -1,98 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "CustomFormatWidget.h" - -#include "ui_CustomFormatWidget.h" - -namespace tomviz { - -CustomFormatWidget::CustomFormatWidget(QWidget* parent) - : QWidget(parent), m_ui(new Ui::CustomFormatWidget) -{ - m_ui->setupUi(this); - m_ui->prefixEdit->setText(m_prefix); - m_ui->suffixEdit->setText(m_suffix); - m_ui->extensionEdit->setText(m_ext); - m_ui->negativeEdit->setText(m_negChar); - m_ui->positiveEdit->setText(m_posChar); - - connect(m_ui->prefixEdit, &QLineEdit::textEdited, this, - &CustomFormatWidget::onPrefixChanged); - connect(m_ui->suffixEdit, &QLineEdit::textEdited, this, - &CustomFormatWidget::onSuffixChanged); - connect(m_ui->extensionEdit, &QLineEdit::textEdited, this, - &CustomFormatWidget::onExtensionChanged); - connect(m_ui->negativeEdit, &QLineEdit::textEdited, this, - &CustomFormatWidget::onNegChanged); - connect(m_ui->positiveEdit, &QLineEdit::textEdited, this, - &CustomFormatWidget::onPosChanged); -} - -CustomFormatWidget::~CustomFormatWidget() = default; - -void CustomFormatWidget::onPrefixChanged(QString val) -{ - m_prefix = val; - emit fieldsChanged(); -} - -void CustomFormatWidget::onNegChanged(QString val) -{ - m_negChar = val; - emit fieldsChanged(); -} - -void CustomFormatWidget::onPosChanged(QString val) -{ - m_posChar = val; - emit fieldsChanged(); -} - -void CustomFormatWidget::onSuffixChanged(QString val) -{ - m_suffix = val; - emit fieldsChanged(); -} - -void CustomFormatWidget::onExtensionChanged(QString val) -{ - m_ext = val; - emit fieldsChanged(); -} - -QStringList CustomFormatWidget::getFields() const -{ - QStringList fields; - fields << m_prefix << m_negChar << m_posChar << m_suffix << m_ext; - return fields; -} - -void CustomFormatWidget::setFields(const QStringList& fields) -{ - if (fields.size() != 5) { - return; - } - m_prefix = fields[0]; - m_negChar = fields[1]; - m_posChar = fields[2]; - m_suffix = fields[3]; - m_ext = fields[4]; - - m_ui->prefixEdit->setText(m_prefix); - m_ui->suffixEdit->setText(m_suffix); - m_ui->extensionEdit->setText(m_ext); - m_ui->negativeEdit->setText(m_negChar); - m_ui->positiveEdit->setText(m_posChar); -} - -void CustomFormatWidget::setAllowEdit(bool allow) -{ - m_ui->prefixEdit->setEnabled(allow); - m_ui->suffixEdit->setEnabled(allow); - m_ui->extensionEdit->setEnabled(allow); - m_ui->negativeEdit->setEnabled(allow); - m_ui->positiveEdit->setEnabled(allow); -} - -} // namespace tomviz diff --git a/tomviz/acquisition/CustomFormatWidget.h b/tomviz/acquisition/CustomFormatWidget.h deleted file mode 100644 index 7805f6708..000000000 --- a/tomviz/acquisition/CustomFormatWidget.h +++ /dev/null @@ -1,48 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizCustomFormatWidget_h -#define tomvizCustomFormatWidget_h - -#include - -#include - -namespace Ui { -class CustomFormatWidget; -} - -namespace tomviz { - -class CustomFormatWidget : public QWidget -{ - Q_OBJECT - -public: - CustomFormatWidget(QWidget* parent = nullptr); - ~CustomFormatWidget() override; - QStringList getFields() const; - void setFields(const QStringList&); - void setAllowEdit(bool); - -signals: - void fieldsChanged(); - -private slots: - void onPrefixChanged(QString prefix); - void onNegChanged(QString prefix); - void onPosChanged(QString prefix); - void onSuffixChanged(QString prefix); - void onExtensionChanged(QString prefix); - -private: - QScopedPointer m_ui; - QString m_prefix = "*"; - QString m_suffix = "*"; - QString m_ext = "*"; - QString m_negChar = "n"; - QString m_posChar = "p"; -}; -} - -#endif diff --git a/tomviz/acquisition/CustomFormatWidget.ui b/tomviz/acquisition/CustomFormatWidget.ui deleted file mode 100644 index 8eaedd027..000000000 --- a/tomviz/acquisition/CustomFormatWidget.ui +++ /dev/null @@ -1,187 +0,0 @@ - - - CustomFormatWidget - - - - - - - 0 - 0 - - - - - 70 - 0 - - - - File prefix (* catches all) - - - - - - - QLayout::SetMaximumSize - - - - - [ - - - - - - - - 0 - 0 - - - - - 20 - 0 - - - - - 50 - 16777215 - - - - Negative angle character identifier - - - - - - - | - - - - - - - - 0 - 0 - - - - - 20 - 0 - - - - - 50 - 16777215 - - - - Positive angle character identifier - - - - - - - ] - - - - - - - 123.4 - - - - - - - - - - 0 - 0 - - - - - 70 - 0 - - - - File suffix (* catches all) - - - - - - - . - - - - - - - - 0 - 0 - - - - - 80 - 16777215 - - - - File extension (* catches all) - - - - - - - Prefix - - - - - - - Angle - - - - - - - Suffix - - - - - - - Ext - - - - - - - - diff --git a/tomviz/acquisition/MatchInfo.h b/tomviz/acquisition/MatchInfo.h deleted file mode 100644 index 5b588b9fd..000000000 --- a/tomviz/acquisition/MatchInfo.h +++ /dev/null @@ -1,28 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizMatchInfo_h -#define tomvizMatchInfo_h - -#include - -namespace tomviz { - -struct CapGroup -{ - CapGroup(const QString& name_, const QString& text) - : name(name_), capturedText(text) - { - } - QString name; - QString capturedText; -}; - -struct MatchInfo -{ - bool matched; - QList groups; -}; -} - -#endif diff --git a/tomviz/acquisition/PassiveAcquisitionWidget.cxx b/tomviz/acquisition/PassiveAcquisitionWidget.cxx deleted file mode 100644 index 0f1587c65..000000000 --- a/tomviz/acquisition/PassiveAcquisitionWidget.cxx +++ /dev/null @@ -1,533 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "PassiveAcquisitionWidget.h" - -#include "ui_PassiveAcquisitionWidget.h" - -#include "AcquisitionClient.h" -#include "ActiveObjects.h" -#include "ConnectionDialog.h" -#include "InterfaceBuilder.h" -#include "StartServerDialog.h" - -#include "legacy/DataSource.h" -#include "legacy/modules/ModuleManager.h" -#include "legacy/Pipeline.h" -#include "legacy/PipelineManager.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -const char* PASSIVE_ADAPTER = - "tomviz.acquisition.vendors.passive.PassiveWatchSource"; - -PassiveAcquisitionWidget::PassiveAcquisitionWidget(QWidget* parent) - : QDialog(parent), m_ui(new Ui::PassiveAcquisitionWidget), - m_client(new AcquisitionClient("http://localhost:8080/acquisition", this)), - m_connectParamsWidget(new QWidget), m_watchTimer(new QTimer) -{ - m_ui->setupUi(this); - - // Default to home directory - QStringList locations = - QStandardPaths::standardLocations(QStandardPaths::HomeLocation); - m_ui->watchPathLineEdit->setText(locations[0]); - - setupTestTable(); - - readSettings(); - - connect(m_ui->watchPathLineEdit, &QLineEdit::textChanged, this, - &PassiveAcquisitionWidget::checkEnableWatchButton); - connect(m_ui->connectionsWidget, &ConnectionsWidget::selectionChanged, this, - &PassiveAcquisitionWidget::checkEnableWatchButton); - - connect(m_ui->formatTabWidget, &QTabWidget::currentChanged, this, - &PassiveAcquisitionWidget::formatTabChanged); - - connect(m_ui->testFileFormatEdit, &QLineEdit::textChanged, this, - &PassiveAcquisitionWidget::testFileNameChanged); - - connect(m_ui->basicTab, &BasicFormatWidget::regexChanged, this, - &PassiveAcquisitionWidget::onRegexChanged); - - connect(m_ui->basicTab, &BasicFormatWidget::fileFormatChanged, this, - &PassiveAcquisitionWidget::onBasicFormatChanged); - - connect(m_ui->advancedTab, &AdvancedFormatWidget::regexChanged, this, - &PassiveAcquisitionWidget::onRegexChanged); - - connect(m_ui->watchButton, &QPushButton::clicked, [this]() { - m_retryCount = 5; - connectToServer(); - }); - - connect(m_ui->stopWatchingButton, &QPushButton::clicked, this, - &PassiveAcquisitionWidget::stopWatching); - - checkEnableWatchButton(); - - // Connect signal to clean up any servers we start. - auto app = QCoreApplication::instance(); - connect(app, &QApplication::aboutToQuit, [this]() { - if (m_serverProcess != nullptr) { - // First disconnect the error signal as we are about pull the rug from - // under - // the process! - disconnect(m_serverProcess, &QProcess::errorOccurred, nullptr, nullptr); - m_serverProcess->terminate(); - } - }); - - // Initialize the fileName line edit with a default example - onBasicFormatChanged(); -} - -PassiveAcquisitionWidget::~PassiveAcquisitionWidget() = default; - -void PassiveAcquisitionWidget::closeEvent(QCloseEvent* event) -{ - writeSettings(); - event->accept(); -} - -void PassiveAcquisitionWidget::readSettings() -{ - auto settings = pqApplicationCore::instance()->settings(); - if (!settings->contains("acquisition/watchPath")) { - return; - } - settings->beginGroup("acquisition"); - setGeometry(settings->value("passive.geometry").toRect()); - - auto watchPath = settings->value("watchPath").toString(); - if (!watchPath.isEmpty()) { - m_ui->watchPathLineEdit->setText(watchPath); - } - - settings->endGroup(); -} - -void PassiveAcquisitionWidget::writeSettings() -{ - auto settings = pqApplicationCore::instance()->settings(); - settings->beginGroup("acquisition"); - settings->setValue("passive.geometry", geometry()); - settings->setValue("watchPath", m_ui->watchPathLineEdit->text()); - settings->endGroup(); -} - -void PassiveAcquisitionWidget::connectToServer(bool startServer) -{ - if (m_retryCount == 0) { - displayError("Retry count excceed trying to connect to server."); - return; - } - - m_client->setUrl(url()); - auto request = m_client->connect(connectParams()); - connect(request, &AcquisitionClientRequest::finished, [this]() { - // Now check that we are connected to server that has the right adapter - // loaded. - auto describeRequest = m_client->describe(); - connect(describeRequest, &AcquisitionClientRequest::error, this, - &PassiveAcquisitionWidget::onError); - connect( - describeRequest, &AcquisitionClientRequest::finished, - [this](const QJsonValue& result) { - if (!result.isObject()) { - onError("Invalid response to describe request:", result); - return; - } - - if (result.toObject()["name"] != PASSIVE_ADAPTER) { - onError( - "The server is not running the passive acquisition " - "adapter, please restart the server with the correct adapter.", - QJsonValue()); - return; - } - - // Now we can start watching. - watchSource(); - }); - }); - - connect(request, &AcquisitionClientRequest::error, - [startServer, this](const QString& errorMessage, - const QJsonValue& errorData) { - auto connection = m_ui->connectionsWidget->selectedConnection(); - // If we are getting a connection refused error and we are trying to - // connect - // to localhost, try to start the server. - if (startServer && - errorData.toInt() == QNetworkReply::ConnectionRefusedError && - connection->hostName() == "localhost") { - startLocalServer(); - } else { - onError(errorMessage, errorData); - } - }); -} - -void PassiveAcquisitionWidget::imageReady(QString mimeType, QByteArray result, - float angle, bool hasAngle) -{ - if (mimeType != "image/tiff") { - qDebug() << "image/tiff is the only supported mime type right now.\n" - << mimeType << "\n"; - return; - } - - QDir dir(QDir::homePath() + "/tomviz-data"); - if (!dir.exists()) { - dir.mkpath(dir.path()); - } - - QString path = "/tomviz_"; - - if (angle > 0.0) { - path.append('+'); - } - path.append(QString::number(angle, 'g', 2)); - path.append(".tiff"); - - QFile file(dir.path() + path); - if (!file.open(QIODevice::WriteOnly)) { - qWarning() << "Failed to open file for writing:" << file.fileName(); - return; - } - file.write(result); - qDebug() << "Data file:" << file.fileName(); - file.close(); - - vtkNew reader; - reader->SetFileName(file.fileName().toLatin1()); - reader->Update(); - m_imageData = reader->GetOutput(); - - // If we haven't added it, add our live data source to the pipeline. - if (!m_dataSource) { - DataSource::DataSourceType t = - hasAngle ? DataSource::TiltSeries : DataSource::Volume; - m_dataSource = new DataSource(m_imageData, t); - m_dataSource->setLabel("Live!"); - auto pipeline = new Pipeline(m_dataSource); - PipelineManager::instance().addPipeline(pipeline); - ModuleManager::instance().addDataSource(m_dataSource); - pipeline->addDefaultModules(m_dataSource); - } else { - m_dataSource->appendSlice(m_imageData); - } - - if (m_dataSource->type() == DataSource::TiltSeries) { - auto tiltAngles = m_dataSource->getTiltAngles(); - tiltAngles << angle; - m_dataSource->setTiltAngles(tiltAngles); - } -} - -void PassiveAcquisitionWidget::onError(const QString& errorMessage, - const QJsonValue& errorData) -{ - auto message = errorMessage; - if (!errorData.toString().isEmpty()) { - message = QString("%1\n%2").arg(message).arg(errorData.toString()); - } - - stopWatching(); - displayError(message); -} - -void PassiveAcquisitionWidget::displayError(const QString& errorMessage) -{ - QMessageBox::warning(this, "Acquisition Error", errorMessage, - QMessageBox::Ok); -} - -QString PassiveAcquisitionWidget::url() const -{ - auto connection = m_ui->connectionsWidget->selectedConnection(); - - return QString("http://%1:%2/acquisition") - .arg(connection->hostName()) - .arg(connection->port()); -} - -void PassiveAcquisitionWidget::watchSource() -{ - m_ui->watchButton->setEnabled(false); - m_ui->stopWatchingButton->setEnabled(true); - connect(m_watchTimer, &QTimer::timeout, this, - [this]() { - auto request = m_client->stem_acquire(); - connect(request, &AcquisitionClientImageRequest::finished, - [this](const QString mimeType, const QByteArray& result, - const QJsonObject& meta) { - if (!result.isNull()) { - float angle = 0; - bool hasAngle = false; - if (meta.contains("angle")) { - angle = meta["angle"].toString().toFloat(); - hasAngle = true; - } - imageReady(mimeType, result, angle, hasAngle); - } - }); - connect(request, &AcquisitionClientRequest::error, this, - &PassiveAcquisitionWidget::onError); - }, - Qt::UniqueConnection); - m_watchTimer->start(1000); -} - -QJsonObject PassiveAcquisitionWidget::connectParams() -{ - /* - Let's write some json in C++ !! - - Structure: - - connectParams = { - "path": "/directory/to/be/watched/", - "fileNameRegex": "^.*((n|p)?(\d+(\.\d+)?)).*(\.tif[f]?)$", - "fileNameRegexGroups": [ - "angle" - ], - "groupRegexSubstitutions": { - "angle": [ - {"n": "-"}, - {"p": "+"}, - ] - } - } - */ - - QJsonObject connectParams{ - { "path", m_ui->watchPathLineEdit->text() }, - }; - - QString regex; - QJsonArray groups; - QJsonObject substitutions; - - int tabIndex = m_ui->formatTabWidget->currentIndex(); - if (tabIndex == 0) { - regex = m_ui->basicTab->getPythonRegex(); - groups = m_ui->basicTab->getRegexGroups(); - substitutions = m_ui->basicTab->getRegexSubsitutions(); - } else { - regex = m_ui->advancedTab->getPythonRegex(); - groups = m_ui->advancedTab->getRegexGroups(); - substitutions = m_ui->advancedTab->getRegexSubsitutions(); - } - - connectParams["fileNameRegex"] = regex; - connectParams["fileNameRegexGroups"] = groups; - connectParams["groupRegexSubstitutions"] = substitutions; - - return connectParams; -} - -void PassiveAcquisitionWidget::startLocalServer() -{ - StartServerDialog dialog; - auto r = dialog.exec(); - if (r != QDialog::Accepted) { - return; - } - auto pythonExecutablePath = dialog.pythonExecutablePath(); - - QStringList arguments; - arguments << "-m" - << "tomviz" - << "-a" << PASSIVE_ADAPTER << "-r"; - - m_serverProcess = new QProcess(this); - m_serverProcess->setProgram(pythonExecutablePath); - m_serverProcess->setArguments(arguments); - connect(m_serverProcess, &QProcess::errorOccurred, - [this](QProcess::ProcessError error) { - Q_UNUSED(error); - auto message = QString("Error starting local acquisition: '%1'") - .arg(m_serverProcess->errorString()); - - QMessageBox::warning(this, "Server Start Error", message, - QMessageBox::Ok); - }); - - connect(m_serverProcess, &QProcess::started, [this]() { - // Now try to connect and watch. Note we are not asking for server to be - // started if the connection fails, this is to prevent us getting into a - // connect loop. - QTimer::singleShot(200, [this]() { - --m_retryCount; - connectToServer(false); - }); - }); - - connect( - m_serverProcess, static_cast(&QProcess::finished), - [](int exitCode, QProcess::ExitStatus exitStatus) { - qWarning() << QString( - "The acquisition server has exited with exit code: %1 %2") - .arg(exitCode, exitStatus); - }); - - connect(m_serverProcess, &QProcess::readyReadStandardError, - [this]() { qWarning() << m_serverProcess->readAllStandardError(); }); - - connect(m_serverProcess, &QProcess::readyReadStandardOutput, - [this]() { qInfo() << m_serverProcess->readAllStandardOutput(); }); - - qInfo() << QString("Starting server with following command: %1 %2") - .arg(m_serverProcess->program()) - .arg(m_serverProcess->arguments().join(" ")); - QStringList locations = - QStandardPaths::standardLocations(QStandardPaths::HomeLocation); - qInfo() - << QString( - "Server logs are written to the following path: %1%2.tomviz%2logs%2") - .arg(locations[0]) - .arg(QDir::separator()); - - m_serverProcess->start(); -} - -void PassiveAcquisitionWidget::checkEnableWatchButton() -{ - auto path = m_ui->watchPathLineEdit->text(); - m_ui->watchButton->setEnabled(!path.isEmpty() && - m_ui->connectionsWidget->selectedConnection() != - nullptr); -} - -void PassiveAcquisitionWidget::stopWatching() -{ - m_watchTimer->stop(); - m_ui->stopWatchingButton->setEnabled(false); - m_ui->watchButton->setEnabled(true); -} - -void PassiveAcquisitionWidget::formatTabChanged(int tab) -{ - if (tab == 0) { - onBasicFormatChanged(); - } - validateTestFileName(); -} - -void PassiveAcquisitionWidget::onRegexChanged(QString) -{ - validateTestFileName(); -} - -void PassiveAcquisitionWidget::onBasicFormatChanged() -{ - if (m_ui->basicTab->isDefaultFilename(m_testFileName)) { - m_testFileName = m_ui->basicTab->getDefaultFilename(); - m_ui->testFileFormatEdit->setText(m_testFileName); - } -} - -void PassiveAcquisitionWidget::validateTestFileName() -{ - int tabIndex = m_ui->formatTabWidget->currentIndex(); - MatchInfo result; - - if (tabIndex == 0) { - result = m_ui->basicTab->matchFileName(m_testFileName); - } else { - result = m_ui->advancedTab->matchFileName(m_testFileName); - } - if (result.matched) { - m_ui->testFileFormatEdit->setStyleSheet("background-color : #C8E6C9;"); - } else { - m_ui->testFileFormatEdit->setStyleSheet("background-color : #FFCCBC;"); - } - if (m_testFileName.isEmpty()) { - m_ui->testFileFormatEdit->setStyleSheet(""); - } - - QStringList tableHeaders; - m_ui->testTableWidget->setColumnCount(result.groups.size()); - for (int i = 0; i < result.groups.size(); ++i) { - tableHeaders << result.groups[i].name; - m_ui->testTableWidget->setItem( - 0, i, new QTableWidgetItem(result.groups[i].capturedText)); - } - m_ui->testTableWidget->setHorizontalHeaderLabels(tableHeaders); - resizeTestTable(); -} - -void PassiveAcquisitionWidget::testFileNameChanged(QString fileName) -{ - m_testFileName = fileName; - validateTestFileName(); -} - -void PassiveAcquisitionWidget::setupTestTable() -{ - m_ui->testTableWidget->setRowCount(1); - m_ui->testTableWidget->setColumnCount(1); - QStringList tableHeaders; - m_ui->testTableWidget->verticalHeader()->setVisible(false); - m_ui->testTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_ui->testTableWidget->setSelectionMode(QAbstractItemView::NoSelection); - - m_ui->testTableWidget->setVisible(true); - m_ui->testTablePlaceholder->setVisible(false); - resizeTestTable(); - validateTestFileName(); -} - -void PassiveAcquisitionWidget::resizeTestTable() -{ - m_ui->testTableWidget->resizeColumnsToContents(); - m_ui->testTableWidget->horizontalHeader()->setSectionResizeMode( - 0, QHeaderView::Stretch); - int horizontalHeaderHeight = - m_ui->testTableWidget->horizontalHeader()->height(); - int rowTotalHeight = m_ui->testTableWidget->verticalHeader()->sectionSize(0); - int vSize = horizontalHeaderHeight + rowTotalHeight; - - m_ui->testTableWidget->setMinimumHeight(vSize); - m_ui->testTableWidget->setMaximumHeight(vSize); - m_ui->testTablePlaceholder->setMinimumHeight(vSize); - m_ui->testTablePlaceholder->setMaximumHeight(vSize); -} - -} // namespace tomviz diff --git a/tomviz/acquisition/PassiveAcquisitionWidget.h b/tomviz/acquisition/PassiveAcquisitionWidget.h deleted file mode 100644 index d6eee0959..000000000 --- a/tomviz/acquisition/PassiveAcquisitionWidget.h +++ /dev/null @@ -1,107 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPassiveAcquisitionWidget_h -#define tomvizPassiveAcquisitionWidget_h - -#include - -#include "MatchInfo.h" - -#include -#include -#include -#include - -#include -#include - -class vtkImageData; -class vtkImageSlice; -class vtkImageSliceMapper; -class vtkInteractorStyleRubberBand2D; -class vtkInteractorStyleRubberBandZoom; -class vtkRenderer; -class vtkScalarsToColors; -class QProcess; - -namespace Ui { -class PassiveAcquisitionWidget; -} - -namespace tomviz { - -class AcquisitionClient; -class DataSource; - -class PassiveAcquisitionWidget : public QDialog -{ - Q_OBJECT - -public: - PassiveAcquisitionWidget(QWidget* parent = nullptr); - ~PassiveAcquisitionWidget() override; - -protected: - void closeEvent(QCloseEvent* event) override; - - void readSettings(); - void writeSettings(); - -private slots: - void connectToServer(bool startServer = true); - - void imageReady(QString mimeType, QByteArray result, float angle = 0, - bool hasAngle = false); - - void onError(const QString& errorMessage, const QJsonValue& errorData); - void watchSource(); - - void formatTabChanged(int index); - void testFileNameChanged(QString); - void onBasicFormatChanged(); - - void onRegexChanged(QString); - -signals: - void connectParameterDescription(QJsonValue params); - -private: - QScopedPointer m_ui; - QScopedPointer m_client; - - QString m_testFileName; - - vtkNew m_renderer; - vtkNew m_defaultInteractorStyle; - vtkSmartPointer m_imageData; - vtkNew m_imageSlice; - vtkNew m_imageSliceMapper; - vtkSmartPointer m_lut; - - DataSource* m_dataSource = nullptr; - - QString m_units = "unknown"; - double m_calX = 0.0; - double m_calY = 0.0; - QPointer m_connectParamsWidget; - QPointer m_watchTimer; - int m_retryCount = 5; - QProcess* m_serverProcess = nullptr; - - QString url() const; - void introspectSource(); - QJsonObject connectParams(); - QVariantMap settings(); - void checkEnableWatchButton(); - void startLocalServer(); - void displayError(const QString& errorMessage); - void stopWatching(); - void validateTestFileName(); - - void setupTestTable(); - void resizeTestTable(); -}; -} // namespace tomviz - -#endif // tomvizPassiveAcquisitionWidget_h diff --git a/tomviz/acquisition/PassiveAcquisitionWidget.ui b/tomviz/acquisition/PassiveAcquisitionWidget.ui deleted file mode 100644 index 63014c12a..000000000 --- a/tomviz/acquisition/PassiveAcquisitionWidget.ui +++ /dev/null @@ -1,173 +0,0 @@ - - - PassiveAcquisitionWidget - - - Passive Data Acquisition - - - - - - Server Connection (required) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - 0 - 0 - - - - - - - - Qt::Vertical - - - - 0 - 30 - - - - - - - - Watch Path (required) - - - - - - - - - - - Basic - - - - - Advanced - - - - - - - - Qt::Vertical - - - - 0 - 30 - - - - - - - - Sample filename - - - - - - - Paste a sample filename - - - - - - - - - - - - - Qt::Vertical - - - - 0 - 30 - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - false - - - Stop Watching - - - - - - - false - - - Watch - - - - - - - - - - tomviz::ConnectionsWidget - QWidget -
ConnectionsWidget.h
- 1 -
- - tomviz::BasicFormatWidget - QWidget -
BasicFormatWidget.h
- 1 -
- - tomviz::AdvancedFormatWidget - QWidget -
AdvancedFormatWidget.h
- 1 -
-
- - -
diff --git a/tomviz/acquisition/RegexGroupDialog.cxx b/tomviz/acquisition/RegexGroupDialog.cxx deleted file mode 100644 index b20c055e5..000000000 --- a/tomviz/acquisition/RegexGroupDialog.cxx +++ /dev/null @@ -1,23 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "RegexGroupDialog.h" - -#include "ui_RegexGroupDialog.h" - -namespace tomviz { - -RegexGroupDialog::RegexGroupDialog(const QString name, QWidget* parent) - : QDialog(parent), m_ui(new Ui::RegexGroupDialog) -{ - m_ui->setupUi(this); - m_ui->nameLineEdit->setText(name); -} - -RegexGroupDialog::~RegexGroupDialog() = default; - -QString RegexGroupDialog::name() -{ - return m_ui->nameLineEdit->text(); -} -} // namespace tomviz diff --git a/tomviz/acquisition/RegexGroupDialog.h b/tomviz/acquisition/RegexGroupDialog.h deleted file mode 100644 index c7391f86b..000000000 --- a/tomviz/acquisition/RegexGroupDialog.h +++ /dev/null @@ -1,32 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizRegexGroupDialog_h -#define tomvizRegexGroupDialog_h - -#include - -#include - -namespace Ui { -class RegexGroupDialog; -} - -namespace tomviz { - -class RegexGroupDialog : public QDialog -{ - Q_OBJECT - -public: - explicit RegexGroupDialog(const QString name = "", QWidget* parent = nullptr); - ~RegexGroupDialog() override; - - QString name(); - -private: - QScopedPointer m_ui; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/acquisition/RegexGroupDialog.ui b/tomviz/acquisition/RegexGroupDialog.ui deleted file mode 100644 index 832e4f4b3..000000000 --- a/tomviz/acquisition/RegexGroupDialog.ui +++ /dev/null @@ -1,93 +0,0 @@ - - - RegexGroupDialog - - - - 0 - 0 - 384 - 134 - - - - New Regex Group Name - - - - - 30 - 90 - 341 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 9 - 9 - 361 - 71 - - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - Group Name - - - - - - - - - - - - - buttonBox - accepted() - RegexGroupDialog - accept() - - - 200 - 255 - - - 191 - 147 - - - - - buttonBox - rejected() - RegexGroupDialog - reject() - - - 200 - 255 - - - 191 - 147 - - - - - diff --git a/tomviz/acquisition/RegexGroupSubstitution.cxx b/tomviz/acquisition/RegexGroupSubstitution.cxx deleted file mode 100644 index d333116e0..000000000 --- a/tomviz/acquisition/RegexGroupSubstitution.cxx +++ /dev/null @@ -1,46 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "RegexGroupSubstitution.h" - -#include - -namespace tomviz { - -RegexGroupSubstitution::RegexGroupSubstitution() = default; - -RegexGroupSubstitution::RegexGroupSubstitution(const QString& groupName, - const QString& regex, - const QString& substitution) - : m_groupName(groupName), m_regex(regex), m_substitution(substitution) -{} - -RegexGroupSubstitution::~RegexGroupSubstitution() = default; - -void RegexGroupSubstitution::registerType() -{ - - static bool registered = false; - if (!registered) { - registered = true; - } -} - -QDataStream& operator<<(QDataStream& out, const RegexGroupSubstitution& conn) -{ - out << conn.groupName() << conn.regex() << conn.substitution(); - - return out; -} - -QDataStream& operator>>(QDataStream& in, RegexGroupSubstitution& conn) -{ - QString groupName, regex, substitution; - in >> groupName >> regex >> substitution; - conn.setGroupName(groupName); - conn.setRegex(regex); - conn.setSubstitution(substitution); - - return in; -} -} // namespace tomviz diff --git a/tomviz/acquisition/RegexGroupSubstitution.h b/tomviz/acquisition/RegexGroupSubstitution.h deleted file mode 100644 index c183e1395..000000000 --- a/tomviz/acquisition/RegexGroupSubstitution.h +++ /dev/null @@ -1,41 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizRegexGroupSubstitution_h -#define tomvizRegexGroupSubstitution_h - -#include -#include - -namespace tomviz { - -class RegexGroupSubstitution -{ -public: - RegexGroupSubstitution(); - RegexGroupSubstitution(const QString& groupName, const QString& regex, - const QString& substitution); - ~RegexGroupSubstitution(); - - QString groupName() const { return m_groupName; } - void setGroupName(const QString& groupName) { m_groupName = groupName; } - QString regex() const { return m_regex; } - void setRegex(const QString& regex) { m_regex = regex; } - QString substitution() const { return m_substitution; } - void setSubstitution(const QString& substitution) - { - m_substitution = substitution; - } - - static void registerType(); - -private: - QString m_groupName; - QString m_regex; - QString m_substitution; -}; -} // namespace tomviz - -Q_DECLARE_METATYPE(tomviz::RegexGroupSubstitution) - -#endif diff --git a/tomviz/acquisition/RegexGroupSubstitutionDialog.cxx b/tomviz/acquisition/RegexGroupSubstitutionDialog.cxx deleted file mode 100644 index 8e10794b2..000000000 --- a/tomviz/acquisition/RegexGroupSubstitutionDialog.cxx +++ /dev/null @@ -1,64 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "RegexGroupSubstitutionDialog.h" - -#include "ui_RegexGroupSubstitutionDialog.h" - -#include -#include - -namespace tomviz { - -RegexGroupSubstitutionDialog::RegexGroupSubstitutionDialog( - const QString groupName, const QString regex, const QString substitution, - QWidget* parent) - : QDialog(parent), m_ui(new Ui::RegexGroupSubstitutionDialog) -{ - m_ui->setupUi(this); - - m_ui->groupNameLineEdit->setText(groupName); - m_ui->regexLineEdit->setText(regex); - m_ui->substitutionLineEdit->setText(substitution); - - auto palette = m_regexErrorLabel.palette(); - palette.setColor(m_regexErrorLabel.foregroundRole(), Qt::red); - m_regexErrorLabel.setPalette(palette); - - connect(m_ui->regexLineEdit, &QLineEdit::textChanged, [this]() { - m_ui->formLayout->removeWidget(&m_regexErrorLabel); - m_regexErrorLabel.setText(""); - }); -} - -RegexGroupSubstitutionDialog::~RegexGroupSubstitutionDialog() = default; - -QString RegexGroupSubstitutionDialog::groupName() -{ - return m_ui->groupNameLineEdit->text(); -} - -QString RegexGroupSubstitutionDialog::regex() -{ - return m_ui->regexLineEdit->text(); -} - -QString RegexGroupSubstitutionDialog::substitution() -{ - return m_ui->substitutionLineEdit->text(); -} - -void RegexGroupSubstitutionDialog::done(int r) -{ - if (QDialog::Accepted == r) { - QRegExp regExp(m_ui->regexLineEdit->text()); - if (!regExp.isValid()) { - m_regexErrorLabel.setText(regExp.errorString()); - m_ui->formLayout->insertRow(2, "", &m_regexErrorLabel); - return; - } - } - - QDialog::done(r); -} -} // namespace tomviz diff --git a/tomviz/acquisition/RegexGroupSubstitutionDialog.h b/tomviz/acquisition/RegexGroupSubstitutionDialog.h deleted file mode 100644 index 5ccf270bd..000000000 --- a/tomviz/acquisition/RegexGroupSubstitutionDialog.h +++ /dev/null @@ -1,42 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizRegexGroupSubstitutionDialog_h -#define tomvizRegexGroupSubstitutionDialog_h - -#include - -#include -#include - -namespace Ui { -class RegexGroupSubstitutionDialog; -} - -namespace tomviz { - -class RegexGroupSubstitutionDialog : public QDialog -{ - Q_OBJECT - -public: - explicit RegexGroupSubstitutionDialog(const QString groupName = "", - const QString regex = "", - const QString substitution = "", - QWidget* parent = nullptr); - ~RegexGroupSubstitutionDialog() override; - - QString groupName(); - QString regex(); - QString substitution(); - -public slots: - void done(int r) override; - -private: - QScopedPointer m_ui; - QLabel m_regexErrorLabel; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/acquisition/RegexGroupSubstitutionDialog.ui b/tomviz/acquisition/RegexGroupSubstitutionDialog.ui deleted file mode 100644 index 3e1e9e083..000000000 --- a/tomviz/acquisition/RegexGroupSubstitutionDialog.ui +++ /dev/null @@ -1,113 +0,0 @@ - - - RegexGroupSubstitutionDialog - - - - 0 - 0 - 384 - 201 - - - - New Substitution - - - - - 30 - 160 - 341 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 9 - 9 - 361 - 111 - - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - Group Name - - - - - - - - - - Regex - - - - - - - - - - Substitution: - - - - - - - - - - - - - buttonBox - accepted() - RegexGroupSubstitutionDialog - accept() - - - 200 - 255 - - - 191 - 147 - - - - - buttonBox - rejected() - RegexGroupSubstitutionDialog - reject() - - - 200 - 255 - - - 191 - 147 - - - - - diff --git a/tomviz/acquisition/RegexGroupsSubstitutionsWidget.cxx b/tomviz/acquisition/RegexGroupsSubstitutionsWidget.cxx deleted file mode 100644 index 92ff19382..000000000 --- a/tomviz/acquisition/RegexGroupsSubstitutionsWidget.cxx +++ /dev/null @@ -1,188 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "RegexGroupsSubstitutionsWidget.h" -#include "RegexGroupSubstitutionDialog.h" -#include "ui_RegexGroupsSubstitutionsWidget.h" - -#include "ActiveObjects.h" -#include "MainWindow.h" -#include "legacy/modules/ModuleManager.h" - -#include -#include - -#include -#include - -#include - -namespace tomviz { - -RegexGroupsSubstitutionsWidget::RegexGroupsSubstitutionsWidget(QWidget* parent) - : QWidget(parent), m_ui(new Ui::RegexGroupsSubstitutionsWidget) -{ - m_ui->setupUi(this); - autoResizeTable(); - - readSettings(); - - foreach (const RegexGroupSubstitution& substitution, m_substitutions) { - addRegexGroupSubstitution(substitution); - } - - // New - connect(m_ui->newSubstitutionButton, &QPushButton::clicked, [this]() { - RegexGroupSubstitutionDialog dialog; - auto r = dialog.exec(); - if (r != QDialog::Accepted) { - return; - } - - RegexGroupSubstitution newSubstitution(dialog.groupName(), dialog.regex(), - dialog.substitution()); - - m_substitutions.append(newSubstitution); - sortRegexGroupSubstitutions(); - addRegexGroupSubstitution(newSubstitution); - - writeSettings(); - }); - - // Edit - connect(m_ui->regexGroupsSubstitutionsWidget, - &QTableWidget::itemDoubleClicked, [this](QTableWidgetItem* item) { - auto row = m_ui->regexGroupsSubstitutionsWidget->row(item); - editRegexGroupSubstitution(row); - writeSettings(); - }); - - // Delete - connect(m_ui->regexGroupsSubstitutionsWidget, - &QWidget::customContextMenuRequested, [this](const QPoint& pos) { - auto globalPos = - this->m_ui->regexGroupsSubstitutionsWidget->mapToGlobal(pos); - QMenu contextMenu; - contextMenu.addAction("Delete", this, [this, &pos]() { - auto item = - this->m_ui->regexGroupsSubstitutionsWidget->itemAt(pos); - auto row = this->m_ui->regexGroupsSubstitutionsWidget->row(item); - this->m_ui->regexGroupsSubstitutionsWidget->removeRow(row); - this->m_substitutions.removeAt(row); - this->writeSettings(); - autoResizeTable(); - }); - - // Show context menu at handling position - contextMenu.exec(globalPos); - }); -} - -RegexGroupsSubstitutionsWidget::~RegexGroupsSubstitutionsWidget() = default; - -void RegexGroupsSubstitutionsWidget::readSettings() -{ - auto settings = pqApplicationCore::instance()->settings(); - settings->beginGroup("acquisition"); - auto substitutions = settings->value("regexGroupsSubstitutions").toList(); - // If setting doesn't exist use the defaults - if (!settings->contains("regexGroupsSubstitutions")) { - QVariant pos; - pos.setValue(RegexGroupSubstitution("angle", "n", "-")); - substitutions.append(pos); - QVariant neg; - neg.setValue(RegexGroupSubstitution("angle", "p", "+")); - substitutions.append(neg); - } - foreach (const QVariant conn, substitutions) { - m_substitutions.append(conn.value()); - } - settings->endGroup(); -} - -void RegexGroupsSubstitutionsWidget::writeSettings() -{ - auto settings = pqApplicationCore::instance()->settings(); - settings->beginGroup("acquisition"); - - QVariantList substitutions; - foreach (const RegexGroupSubstitution& substitution, m_substitutions) { - QVariant variant; - variant.setValue(substitution); - substitutions.append(variant); - } - settings->setValue("regexGroupsSubstitutions", substitutions); - settings->endGroup(); -} - -void RegexGroupsSubstitutionsWidget::sortRegexGroupSubstitutions() -{ - std::sort(m_substitutions.begin(), m_substitutions.end(), - [](RegexGroupSubstitution a, RegexGroupSubstitution b) { - return a.groupName() < b.groupName(); - }); -} - -void RegexGroupsSubstitutionsWidget::editRegexGroupSubstitution(int row) -{ - RegexGroupSubstitution substitution = m_substitutions[row]; - RegexGroupSubstitutionDialog dialog(substitution.groupName(), - substitution.regex(), - substitution.substitution()); - dialog.exec(); - RegexGroupSubstitution newSubstitution(dialog.groupName(), dialog.regex(), - dialog.substitution()); - - m_substitutions[row] = newSubstitution; - setRegexGroupSubstitution(row, newSubstitution); -} - -void RegexGroupsSubstitutionsWidget::addRegexGroupSubstitution( - RegexGroupSubstitution substitution) -{ - auto row = m_ui->regexGroupsSubstitutionsWidget->rowCount(); - m_ui->regexGroupsSubstitutionsWidget->insertRow(row); - setRegexGroupSubstitution(row, substitution); - autoResizeTable(); -} - -void RegexGroupsSubstitutionsWidget::setRegexGroupSubstitution( - int row, RegexGroupSubstitution substitution) -{ - QTableWidgetItem* groupNameItem = - new QTableWidgetItem(substitution.groupName()); - QTableWidgetItem* regexItem = new QTableWidgetItem(substitution.regex()); - QTableWidgetItem* substitutionItem = - new QTableWidgetItem(substitution.substitution()); - m_ui->regexGroupsSubstitutionsWidget->setItem(row, 0, groupNameItem); - m_ui->regexGroupsSubstitutionsWidget->setItem(row, 1, regexItem); - m_ui->regexGroupsSubstitutionsWidget->setItem(row, 2, substitutionItem); -} - -QList RegexGroupsSubstitutionsWidget::substitutions() -{ - return m_substitutions; -} - -void RegexGroupsSubstitutionsWidget::autoResizeTable() -{ - // Auto resize the size when adding/deleting entries. - // Keep the size between is between 0 and 2 rows. - m_ui->regexGroupsSubstitutionsWidget->resizeColumnsToContents(); - m_ui->regexGroupsSubstitutionsWidget->horizontalHeader() - ->setSectionResizeMode(0, QHeaderView::Stretch); - int vSize = - m_ui->regexGroupsSubstitutionsWidget->horizontalHeader()->height(); - for (int i = 0; i < m_substitutions.size(); ++i) { - vSize += - m_ui->regexGroupsSubstitutionsWidget->verticalHeader()->sectionSize(i); - if (i >= 1) { - break; - } - } - vSize += 2; - m_ui->regexGroupsSubstitutionsWidget->setMinimumHeight(vSize); - m_ui->regexGroupsSubstitutionsWidget->setMaximumHeight(vSize); -} - -} // namespace tomviz diff --git a/tomviz/acquisition/RegexGroupsSubstitutionsWidget.h b/tomviz/acquisition/RegexGroupsSubstitutionsWidget.h deleted file mode 100644 index acf210eaf..000000000 --- a/tomviz/acquisition/RegexGroupsSubstitutionsWidget.h +++ /dev/null @@ -1,46 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizRegexGroupsSubstitutionsWidget_h -#define tomvizRegexGroupsSubstitutionsWidget_h - -#include - -#include -#include -#include - -#include "RegexGroupSubstitution.h" - -namespace Ui { -class RegexGroupsSubstitutionsWidget; -} - -namespace tomviz { - -class RegexGroupsSubstitutionsWidget : public QWidget -{ - Q_OBJECT - -public: - RegexGroupsSubstitutionsWidget(QWidget* parent); - ~RegexGroupsSubstitutionsWidget() override; - - QList substitutions(); - -private: - QScopedPointer m_ui; - QList m_substitutions; - - void readSettings(); - void writeSettings(); - void setRegexGroupSubstitutions(const QVariantList& substitutions); - void sortRegexGroupSubstitutions(); - void editRegexGroupSubstitution(int row); - void addRegexGroupSubstitution(RegexGroupSubstitution substitution); - void setRegexGroupSubstitution(int row, RegexGroupSubstitution substitution); - void autoResizeTable(); -}; -} // namespace tomviz - -#endif diff --git a/tomviz/acquisition/RegexGroupsSubstitutionsWidget.ui b/tomviz/acquisition/RegexGroupsSubstitutionsWidget.ui deleted file mode 100644 index b9fc33f79..000000000 --- a/tomviz/acquisition/RegexGroupsSubstitutionsWidget.ui +++ /dev/null @@ -1,134 +0,0 @@ - - - RegexGroupsSubstitutionsWidget - - - - 0 - 0 - 537 - 282 - - - - Qt::DefaultContextMenu - - - Form - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::CustomContextMenu - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - 3 - - - true - - - false - - - - Group Name - - - - - Regex - - - - - Substitution - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - New Substitution - - - - - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - diff --git a/tomviz/acquisition/RegexGroupsWidget.cxx b/tomviz/acquisition/RegexGroupsWidget.cxx deleted file mode 100644 index b7d63c9ac..000000000 --- a/tomviz/acquisition/RegexGroupsWidget.cxx +++ /dev/null @@ -1,105 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "RegexGroupsWidget.h" - -#include "ui_RegexGroupsWidget.h" - -#include "ActiveObjects.h" -#include "MainWindow.h" -#include "legacy/modules/ModuleManager.h" -#include "RegexGroupDialog.h" - -#include -#include - -#include -#include - -#include - -namespace tomviz { - -RegexGroupsWidget::RegexGroupsWidget(QWidget* parent) - : QWidget(parent), m_ui(new Ui::RegexGroupsWidget) -{ - m_ui->setupUi(this); - - readSettings(); - - // New - connect(m_ui->newRegexGroupButton, &QPushButton::clicked, [this]() { - RegexGroupDialog dialog; - auto r = dialog.exec(); - if (r != QDialog::Accepted || dialog.name().isEmpty()) { - return; - } - - if (m_ui->regexGroupsWidget->findItems(dialog.name(), Qt::MatchExactly) - .isEmpty()) { - m_ui->regexGroupsWidget->addItem(dialog.name()); - } - - writeSettings(); - emit groupsChanged(); - }); - - // Delete - connect(m_ui->regexGroupsWidget, &QWidget::customContextMenuRequested, - [this](const QPoint& pos) { - auto globalPos = m_ui->regexGroupsWidget->mapToGlobal(pos); - QMenu contextMenu; - contextMenu.addAction("Delete", this, [this, &pos]() { - auto item = m_ui->regexGroupsWidget->itemAt(pos); - delete item; - writeSettings(); - emit groupsChanged(); - }); - - // Show context menu at handling position - contextMenu.exec(globalPos); - }); -} - -RegexGroupsWidget::~RegexGroupsWidget() = default; - -void RegexGroupsWidget::readSettings() -{ - auto settings = pqApplicationCore::instance()->settings(); - settings->beginGroup("acquisition"); - auto groups = settings->value("regexGroupNames").toStringList(); - // Add the default - if (!settings->contains("regexGroupNames")) { - groups.append("angle"); - } - foreach (const QString& group, groups) { - m_ui->regexGroupsWidget->addItem(group); - } - settings->endGroup(); -} - -void RegexGroupsWidget::writeSettings() -{ - auto settings = pqApplicationCore::instance()->settings(); - settings->beginGroup("acquisition"); - - QStringList groups; - for (int i = 0; i < m_ui->regexGroupsWidget->count(); i++) { - QListWidgetItem* group = m_ui->regexGroupsWidget->item(i); - groups.append(group->text()); - } - settings->setValue("regexGroupNames", groups); - settings->endGroup(); -} - -QStringList RegexGroupsWidget::regexGroups() -{ - QStringList groups; - for (int i = 0; i < m_ui->regexGroupsWidget->count(); i++) { - QListWidgetItem* group = m_ui->regexGroupsWidget->item(i); - groups.append(group->text()); - } - - return groups; -} -} // namespace tomviz diff --git a/tomviz/acquisition/RegexGroupsWidget.h b/tomviz/acquisition/RegexGroupsWidget.h deleted file mode 100644 index da2f2885d..000000000 --- a/tomviz/acquisition/RegexGroupsWidget.h +++ /dev/null @@ -1,39 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizRegexGroupsWidget_h -#define tomvizRegexGroupsWidget_h - -#include - -#include -#include - -namespace Ui { -class RegexGroupsWidget; -} - -namespace tomviz { - -class RegexGroupsWidget : public QWidget -{ - Q_OBJECT - -public: - RegexGroupsWidget(QWidget* parent); - ~RegexGroupsWidget() override; - - QStringList regexGroups(); - -signals: - void groupsChanged(); - -private: - QScopedPointer m_ui; - - void readSettings(); - void writeSettings(); -}; -} // namespace tomviz - -#endif diff --git a/tomviz/acquisition/RegexGroupsWidget.ui b/tomviz/acquisition/RegexGroupsWidget.ui deleted file mode 100644 index fca0b3b93..000000000 --- a/tomviz/acquisition/RegexGroupsWidget.ui +++ /dev/null @@ -1,101 +0,0 @@ - - - RegexGroupsWidget - - - - 0 - 0 - 604 - 250 - - - - - 0 - 0 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Qt::CustomContextMenu - - - false - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - New Regex Group - - - - - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - diff --git a/tomviz/acquisition/StartServerDialog.cxx b/tomviz/acquisition/StartServerDialog.cxx deleted file mode 100644 index 63d8801d7..000000000 --- a/tomviz/acquisition/StartServerDialog.cxx +++ /dev/null @@ -1,75 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "StartServerDialog.h" -#include "ui_StartServerDialog.h" - -#include -#include - -#include -#include - -const char* PYTHON_PATH_DEFAULT = "Enter Python path ..."; - -namespace tomviz { - -StartServerDialog::StartServerDialog(QWidget* parent) - : QDialog(parent), m_ui(new Ui::StartServerDialog) -{ - m_ui->setupUi(this); - - connect(m_ui->pythonPathLineEdit, &QLineEdit::textChanged, [this]() { - auto currentText = m_ui->pythonPathLineEdit->text(); - auto valid = !currentText.isEmpty() && currentText != PYTHON_PATH_DEFAULT; - m_ui->startButton->setEnabled(valid); - if (valid) { - setPythonExecutablePath(currentText); - } - }); - - readSettings(); - - connect(m_ui->browseButton, &QPushButton::clicked, [this]() { - QFileInfo info(m_pythonExecutablePath); - auto pythonExecutablePath = QFileDialog::getOpenFileName( - this, "Select Python Executable", info.dir().path()); - - if (!pythonExecutablePath.isEmpty()) { - setPythonExecutablePath(pythonExecutablePath); - writeSettings(); - } - }); - - connect(m_ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject); - connect(m_ui->startButton, &QPushButton::clicked, this, &QDialog::accept); -} - -StartServerDialog::~StartServerDialog() = default; - -void StartServerDialog::readSettings() -{ - auto settings = pqApplicationCore::instance()->settings(); - settings->beginGroup("acquisition"); - if (!settings->contains("pythonExecutablePath")) { - setPythonExecutablePath(PYTHON_PATH_DEFAULT); - } else { - setPythonExecutablePath(settings->value("pythonExecutablePath").toString()); - } - settings->endGroup(); -} - -void StartServerDialog::writeSettings() -{ - auto settings = pqApplicationCore::instance()->settings(); - settings->beginGroup("acquisition"); - settings->setValue("pythonExecutablePath", m_pythonExecutablePath); - settings->endGroup(); -} - -void StartServerDialog::setPythonExecutablePath(const QString& path) -{ - m_pythonExecutablePath = path; - m_ui->pythonPathLineEdit->setText(path); -} -} // namespace tomviz diff --git a/tomviz/acquisition/StartServerDialog.h b/tomviz/acquisition/StartServerDialog.h deleted file mode 100644 index 1f2bfb85b..000000000 --- a/tomviz/acquisition/StartServerDialog.h +++ /dev/null @@ -1,36 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizStartServerDialog_h -#define tomvizStartServerDialog_h - -#include - -#include - -namespace Ui { -class StartServerDialog; -} - -namespace tomviz { - -class StartServerDialog : public QDialog -{ - Q_OBJECT - -public: - explicit StartServerDialog(QWidget* parent = nullptr); - ~StartServerDialog() override; - QString pythonExecutablePath() { return m_pythonExecutablePath; } - -private: - QScopedPointer m_ui; - QString m_pythonExecutablePath; - - void readSettings(); - void writeSettings(); - void setPythonExecutablePath(const QString& path); -}; -} // namespace tomviz - -#endif diff --git a/tomviz/acquisition/StartServerDialog.ui b/tomviz/acquisition/StartServerDialog.ui deleted file mode 100644 index 7004ce028..000000000 --- a/tomviz/acquisition/StartServerDialog.ui +++ /dev/null @@ -1,108 +0,0 @@ - - - StartServerDialog - - - - 0 - 0 - 384 - 149 - - - - - 0 - 0 - - - - Start Server? - - - - - - The acquisition server is not running locally. To start that server please provide the path to the python environment that has the acquisition server installed. - - - false - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - false - - - Start - - - - - - - - - - - Enter Python Path ... - - - - - - - Browse - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - diff --git a/tomviz/core/CMakeLists.txt b/tomviz/core/CMakeLists.txt index 13ff189af..677254845 100644 --- a/tomviz/core/CMakeLists.txt +++ b/tomviz/core/CMakeLists.txt @@ -1,7 +1,6 @@ include(GenerateExportHeader) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../legacy/core) -add_library(tomvizcore SHARED PythonFactory.cxx Variant.cxx) +add_library(tomvizcore SHARED Variant.cxx) target_compile_definitions(tomvizcore PRIVATE IS_TOMVIZ_CORE_BUILD) generate_export_header(tomvizcore) target_link_libraries(tomvizcore PRIVATE ${PYTHON_LIBRARIES}) diff --git a/tomviz/core/PythonFactory.cxx b/tomviz/core/PythonFactory.cxx deleted file mode 100644 index 7638a27be..000000000 --- a/tomviz/core/PythonFactory.cxx +++ /dev/null @@ -1,36 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "PythonFactory.h" - -#include "OperatorProxyBase.h" -#include "PipelineProxyBase.h" - -namespace tomviz { - -PythonFactory::~PythonFactory() -{ - delete m_operatorFactory; -} - -PythonFactory& PythonFactory::instance() -{ - static PythonFactory theInstance; - return theInstance; -} - -OperatorProxyBase* PythonFactory::createOperatorProxy(void* o) -{ - if (m_operatorFactory) - return m_operatorFactory->create(o); - return nullptr; -} - -PipelineProxyBase* PythonFactory::createPipelineProxy() -{ - if (m_pipelineFactory) - return m_pipelineFactory->create(); - return nullptr; -} - -} // namespace tomviz \ No newline at end of file diff --git a/tomviz/core/PythonFactory.h b/tomviz/core/PythonFactory.h deleted file mode 100644 index 06ae451c5..000000000 --- a/tomviz/core/PythonFactory.h +++ /dev/null @@ -1,52 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPythonFactory_h -#define tomvizPythonFactory_h - -#include "tomvizcore_export.h" - -namespace tomviz { - -class OperatorProxyBase; -class OperatorProxyBaseFactory; -class PipelineProxyBase; -class PipelineProxyBaseFactory; - -class TOMVIZCORE_EXPORT PythonFactory -{ -public: - /// Returns reference to the singleton instance. - static PythonFactory& instance(); - - /// Get an instance of the OperatorProxy class for this runtime. - OperatorProxyBase* createOperatorProxy(void* o); - - /// Set the proxy factory for the singleton. - void setOperatorProxyFactory(OperatorProxyBaseFactory* factory) - { - m_operatorFactory = factory; - } - - /// Get an instance of the PipelineProxy class for this runtime. - PipelineProxyBase* createPipelineProxy(); - - /// Set the proxy factory for the singleton. - void setPipelineProxyFactory(PipelineProxyBaseFactory* factory) - { - m_pipelineFactory = factory; - } - -private: - PythonFactory() = default; - ~PythonFactory(); - PythonFactory(const PythonFactory&) = delete; - PythonFactory& operator=(const PythonFactory&) = delete; - - OperatorProxyBaseFactory* m_operatorFactory = nullptr; - PipelineProxyBaseFactory* m_pipelineFactory = nullptr; -}; - -} // namespace tomviz - -#endif \ No newline at end of file diff --git a/tomviz/legacy/DataSource.cxx b/tomviz/legacy/DataSource.cxx deleted file mode 100644 index 2f83dfb1d..000000000 --- a/tomviz/legacy/DataSource.cxx +++ /dev/null @@ -1,1958 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "DataSource.h" - -#include "core/DataSourceBase.h" - -#include "ActiveObjects.h" -#include "ColorMap.h" -#include "DataExchangeFormat.h" -#include "EmdFormat.h" -#include "GenericHDF5Format.h" -#include "modules/ModuleFactory.h" -#include "modules/ModuleManager.h" -#include "operators/Operator.h" -#include "operators/OperatorFactory.h" -#include "Pipeline.h" -#include "TimeSeriesStep.h" -#include "Utilities.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace { -void createOrResizeTiltAnglesArray(vtkDataObject* data) -{ - auto fd = data->GetFieldData(); - auto* imageData = vtkImageData::SafeDownCast(data); - if (!imageData) { - return; - } - int* extent = imageData->GetExtent(); - int numTiltAngles = extent[5] - extent[4] + 1; - if (!fd->HasArray("tilt_angles")) { - vtkNew array; - array->SetName("tilt_angles"); - array->SetNumberOfTuples(numTiltAngles); - array->FillComponent(0, 0.0); - fd->AddArray(array); - } else { - // if it exists, ensure the size of the tilt angles array - // corresponds to the size of the data - auto array = fd->GetArray("tilt_angles"); - if (numTiltAngles != array->GetNumberOfTuples()) { - array->SetNumberOfTuples(numTiltAngles); - } - } -} -} // namespace - -namespace tomviz { - -class DataSource::DSInternals -{ -public: - vtkNew m_transfer2D; - vtkNew GradientOpacityMap; - // vtkSmartPointer DataSourceProxy; - vtkSmartPointer m_darkData; - vtkSmartPointer m_whiteData; - vtkSmartPointer ProducerProxy; - QList Operators; - vtkSmartPointer ColorMap; - DataSource::DataSourceType Type; - vtkSmartPointer Units; - vtkVector3d DisplayPosition; - vtkVector3d DisplayOrientation; - PersistenceState PersistState = PersistenceState::Saved; - vtkRectd m_transferFunction2DBox; - bool UnitsModified = false; - bool Forkable = true; - // Track data array renames - QMap CurrentToOriginal; - QList timeSeriesSteps; - int currentTimeStep = 0; - - // Checks if the tilt angles data array exists on the given VTK data - // and creates it if it does not exist. - void ensureTiltAnglesArrayExists() - { - auto alg = - vtkAlgorithm::SafeDownCast(this->ProducerProxy->GetClientSideObject()); - Q_ASSERT(alg); - auto data = alg->GetOutputDataObject(0); - createOrResizeTiltAnglesArray(data); - } -}; - -DataSource::DataSource(vtkSMSourceProxy* dataSource, DataSourceType dataType) - : QObject(nullptr), Internals(new DSInternals) -{ - const char* sourceFilename = nullptr; - - QByteArray fileNameBytes; - if (vtkSMCoreUtilities::GetFileNameProperty(dataSource)) { - vtkSMPropertyHelper helper( - dataSource, vtkSMCoreUtilities::GetFileNameProperty(dataSource)); - // If we are dealing with an image stack find the prefix to use - // when displaying the data source. - if (helper.GetNumberOfElements() > 1) { - QStringList fileNames; - for (unsigned int i = 0; i < helper.GetNumberOfElements(); i++) { - fileNames << QString(helper.GetAsString(i)); - } - QFileInfo fileInfo(fileNames[0]); - QString fileName = - QString("%1*.%2").arg(findPrefix(fileNames)).arg(fileInfo.suffix()); - fileNameBytes = fileName.toLatin1(); - sourceFilename = fileNameBytes.data(); - } else { - sourceFilename = helper.GetAsString(); - } - } - - dataSource->UpdatePipeline(); - auto algo = vtkAlgorithm::SafeDownCast(dataSource->GetClientSideObject()); - Q_ASSERT(algo); - auto data = algo->GetOutputDataObject(0); - auto image = vtkImageData::SafeDownCast(data); - - // Initialize our object, and set the file name. - init(image, dataType, PersistenceState::Saved); - setFileName(sourceFilename); -} - -DataSource::DataSource(vtkImageData* data, DataSourceType dataType, - QObject* parentObject, PersistenceState persistState) - : QObject(parentObject), Internals(new DSInternals) -{ - init(data, dataType, persistState); -} - -DataSource::DataSource(const QString& label, DataSourceType dataType, - QObject* parent, PersistenceState persistState, - const QJsonObject& sourceInfo) - : QObject(parent), Internals(new DSInternals) -{ - init(nullptr, dataType, persistState); - - if (!label.isNull()) { - setLabel(label); - } - if (!sourceInfo.isEmpty()) { - m_json["sourceInformation"] = sourceInfo; - } -} - -DataSource::~DataSource() -{ - if (this->Internals->ProducerProxy) { - vtkNew controller; - controller->UnRegisterProxy(this->Internals->ProducerProxy); - } - delete m_pythonProxy; -} - -// Simple templated function to extend the image data. -template -void appendImageData(vtkImageData* data, vtkImageData* slice, T* originalPtr) -{ - auto dataArray = data->GetPointData()->GetScalars(); - int extents[6]; - data->GetExtent(extents); - - // Figure out the number of bytes in the original data, and allocate a buffer. - int bufferSize = static_cast(dataArray->GetNumberOfTuples() * - dataArray->GetNumberOfComponents() * - sizeof(T)); - unsigned char* buffer = new unsigned char[bufferSize]; - // Copy the original image data, as the allocate scalars will erase it. - std::memcpy(buffer, originalPtr, bufferSize); - // Now increment the z extent, and reallocate the scalar array (destructive). - ++extents[5]; - data->SetExtent(extents); - data->AllocateScalars(data->GetScalarType(), - data->GetNumberOfScalarComponents()); - // Copy the old data back into the new memory location, delete the buffer. - auto ptr = data->GetScalarPointer(); - std::memcpy(ptr, buffer, bufferSize); - delete[] buffer; - buffer = 0; - - // Now copy the new slice into the array. - void* imagePtr = data->GetScalarPointer(0, 0, extents[5]); - auto sliceArray = slice->GetPointData()->GetScalars(); - void* slicePtr = sliceArray->GetVoidPointer(0); - int sliceSize = static_cast(sliceArray->GetNumberOfTuples() * - sliceArray->GetNumberOfComponents() * - sizeof(T)); - std::memcpy(imagePtr, slicePtr, sliceSize); - - // Let everyone know the data has changed, then re-execute the pipeline. - data->Modified(); -} - -bool DataSource::appendSlice(vtkImageData* slice) -{ - if (!slice) { - return false; - } - - int sliceExtents[6]; - slice->GetExtent(sliceExtents); - auto tp = algorithm(); - if (tp) { - auto data = vtkImageData::SafeDownCast(tp->GetOutputDataObject(0)); - if (data) { - int extents[6]; - data->GetExtent(extents); - std::cout << "The data is "; - for (int i = 0; i < 6; ++i) - std::cout << extents[i] << ", "; - std::cout << std::endl; - for (int i = 0; i < 4; ++i) { - if (extents[i] != sliceExtents[i]) { - std::cout << "Mismatch: " << extents[i] << " != " << sliceExtents[i] - << std::endl; - return false; - } - } - - // Now to append the slice onto our image data. - switch (data->GetScalarType()) { - vtkTemplateMacro(appendImageData( - data, slice, static_cast(data->GetScalarPointer()))); - } - - emit dataChanged(); - emit dataPropertiesChanged(); - pipeline()->execute()->deleteWhenFinished(); - } - } - return true; -} - -void DataSource::setFileName(const QString& filename) -{ - QStringList fileNames = QStringList(filename); - setFileNames(fileNames); -} - -QString DataSource::fileName() const -{ - auto reader = m_json.value("reader").toObject(QJsonObject()); - if (reader.contains("fileNames")) { - auto fileNames = reader["fileNames"].toArray(); - if (fileNames.size() > 0) { - return fileNames[0].toString(); - } - } - return QString(); -} - -void DataSource::setFileNames(const QStringList fileNames) -{ - auto reader = m_json.value("reader").toObject(QJsonObject()); - QJsonArray files; - foreach (QString file, fileNames) { - files.append(file); - } - - if (fileNames.size() != 0) { - // Set the python proxy file name too - m_pythonProxy->setFileName(fileNames[0].toStdString()); - } - - reader["fileNames"] = files; - m_json["reader"] = reader; -} - -void DataSource::setTvh5NodePath(const QString& path) -{ - auto reader = m_json.value("reader").toObject(); - reader["tvh5NodePath"] = path; - m_json["reader"] = reader; -} - -QString DataSource::tvh5NodePath() const -{ - return m_json.value("reader").toObject().value("tvh5NodePath").toString(); -} - -QStringList DataSource::fileNames() const -{ - auto reader = m_json.value("reader").toObject(QJsonObject()); - QStringList files; - if (reader.contains("fileNames")) { - QJsonArray fileArray = reader["fileNames"].toArray(); - for (const QJsonValue& file : fileArray) { - files.append(file.toString()); - } - } - return files; -} - -void DataSource::setMetadata(const MetadataType& meta) -{ - m_pythonProxy->setMetadata(meta); - m_metadata = meta; -} - -void DataSource::setDarkData(vtkSmartPointer image) -{ - m_pythonProxy->setDarkData(image); - this->Internals->m_darkData = image; -} - -vtkImageData* DataSource::darkData() const -{ - return this->Internals->m_darkData; -} - -void DataSource::setWhiteData(vtkSmartPointer image) -{ - m_pythonProxy->setWhiteData(image); - this->Internals->m_whiteData = image; -} - -vtkImageData* DataSource::whiteData() const -{ - return this->Internals->m_whiteData; -} - -bool DataSource::canReloadAndResample() const -{ - const auto& files = fileNames(); - - // This currently only works for single files - if (files.size() != 1) - return false; - - const auto& file = files[0]; - - static const QStringList h5Extensions = { "emd", "h5", "he5", "hdf5" }; - - // If it looks like an HDF5 type (based on its extension), it can be - // reloaded and resampled. - return std::any_of( - h5Extensions.cbegin(), h5Extensions.cend(), - [&file](const QString& x) { return file.endsWith(x, Qt::CaseInsensitive); }); -} - -bool DataSource::reloadAndResample() -{ - const auto& files = fileNames(); - - // This currently only works for single files - if (files.size() != 1) - return false; - - const auto& file = files[0]; - - auto algo = vtkAlgorithm::SafeDownCast(proxy()->GetClientSideObject()); - Q_ASSERT(algo); - auto data = algo->GetOutputDataObject(0); - auto image = vtkImageData::SafeDownCast(data); - - bool success; - QVariantMap options{ { "askForSubsample", true } }; - if (file.endsWith("emd", Qt::CaseInsensitive)) { - EmdFormat format; - success = format.read(file.toLatin1().data(), image, options); - } else if (GenericHDF5Format::isDataExchange(file.toStdString())) { - DataExchangeFormat format; - success = format.read(file.toLatin1().data(), image, options); - } else { - success = GenericHDF5Format::read(file.toLatin1().data(), image, options); - } - - // If there are operators, re-run the pipeline - if (!operators().empty()) - pipeline()->execute(this, operators().first())->deleteWhenFinished(); - - dataModified(); - emit activeScalarsChanged(); - emit dataPropertiesChanged(); - return success; -} - -bool DataSource::isImageStack() const -{ - auto reader = m_json.value("reader").toObject(QJsonObject()); - if (reader.contains("fileNames") && reader["fileNames"].isArray()) { - return reader["fileNames"].toArray().size() > 1; - } - return false; -} - -void DataSource::setReaderProperties(const QVariantMap& properties) -{ - m_json["reader"] = QJsonObject::fromVariantMap(properties); -} - -QVariantMap DataSource::readerProperties() const -{ - if (m_json.contains("reader") && m_json["reader"].isObject()) { - return m_json["reader"].toObject().toVariantMap(); - } else { - return QVariantMap(); - } -} - -void DataSource::setLabel(const QString& label) -{ - m_json["label"] = label; -} - -QString DataSource::label() const -{ - if (m_json.contains("label")) { - return m_json["label"].toString(); - } else { - return QFileInfo(fileName()).baseName(); - } -} - -QString DataSource::id() const -{ - return QString::asprintf("%p", static_cast(this)); -} - -template -QJsonArray toJsonArray(T* array, int size) -{ - QJsonArray ret; - for (int i = 0; i < size; ++i) { - ret.append(array[i]); - } - return ret; -} - -QJsonObject DataSource::serialize() const -{ - QJsonObject json = m_json; - json["label"] = label(); - - // If the data was subsampled, save the subsampling settings - if (wasSubsampled()) { - QJsonObject settings; - - int s[3]; - subsampleStrides(s); - settings["strides"] = toJsonArray(s, 3); - - int bs[6]; - subsampleVolumeBounds(bs); - settings["volumeBounds"] = toJsonArray(bs, 6); - json["subsampleSettings"] = settings; - } - - if (Internals->UnitsModified) { - json["spacing"] = toJsonArray(getSpacing(), 3); - if (this->Internals->Units) { - json["units"] = this->Internals->Units->GetValue(0).c_str(); - } - } - - json["origin"] = toJsonArray(displayPosition(), 3); - json["orientation"] = toJsonArray(displayOrientation(), 3); - - // Serialize the currently active scalars - json["activeScalars"] = activeScalars(); - QJsonObject scalarsRename; - for (auto it = Internals->CurrentToOriginal.begin(); - it != Internals->CurrentToOriginal.end(); ++it) { - scalarsRename[it.value()] = it.key(); - } - json["scalarsRename"] = scalarsRename; - - // Serialize the color map, opacity map, and others if needed. - json["colorOpacityMap"] = tomviz::serialize(colorMap()); - json["gradientOpacityMap"] = tomviz::serialize(gradientOpacityMap()); - QJsonObject boxJson; - auto& transfer2DBox = this->Internals->m_transferFunction2DBox; - boxJson["x"] = transfer2DBox.GetX(); - boxJson["y"] = transfer2DBox.GetY(); - boxJson["width"] = transfer2DBox.GetWidth(); - boxJson["height"] = transfer2DBox.GetHeight(); - json["colorMap2DBox"] = boxJson; - - // Serialize the operators... - QJsonArray jOperators; - foreach (Operator* op, this->Internals->Operators) { - QJsonObject jOperator = op->serialize(); - jOperators.append(jOperator); - } - if (!jOperators.isEmpty()) { - json["operators"] = jOperators; - } - - // Serialize the modules... - auto modules = ModuleManager::instance().findModulesGeneric(this, nullptr); - QJsonArray jModules; - foreach (Module* module, modules) { - QJsonObject jModule = module->serialize(); - jModules.append(jModule); - } - if (!jModules.isEmpty()) { - json["modules"] = jModules; - } - - json["id"] = id(); - - // LEGACY STUB: activeDataSource() removed from ActiveObjects - if (false) { - // Label itself as the active data source - json["active"] = true; - } - - return json; -} - -template -void fromJsonArray(const QJsonArray& array, T* out); - -template <> -void fromJsonArray(const QJsonArray& array, double* out) -{ - for (int i = 0; i < array.size(); ++i) { - out[i] = array.at(i).toDouble(); - } -} - -template -void fromJsonArray(const QJsonValue& value, T* out) -{ - fromJsonArray(value.toArray(), out); -} - -bool DataSource::deserialize(const QJsonObject& state) -{ - if (!state["label"].isUndefined()) { - setLabel(state["label"].toString()); - } - - if (state.contains("id")) { - ModuleManager::instance().addStateIdToDataSource(state["id"].toString(), - this); - } - - if (state.contains("scalarsRename")) { - auto scalarsRename = state["scalarsRename"].toObject(); - for (auto it = scalarsRename.begin(); it != scalarsRename.end(); ++it) { - renameScalarsArray(it.key(), it.value().toString()); - } - } - - if (state.contains("activeScalars")) { - setActiveScalars(state["activeScalars"].toString()); - } - - if (state.contains("colorOpacityMap")) { - tomviz::deserialize(colorMap(), state["colorOpacityMap"].toObject()); - } - if (state.contains("gradientOpacityMap")) { - tomviz::deserialize(gradientOpacityMap(), - state["gradientOpacityMap"].toObject()); - } - if (state.contains("colorMap2DBox")) { - auto boxJson = state["colorMap2DBox"].toObject(); - auto& transfer2DBox = this->Internals->m_transferFunction2DBox; - transfer2DBox.Set(boxJson["x"].toDouble(), boxJson["y"].toDouble(), - boxJson["width"].toDouble(), - boxJson["height"].toDouble()); - } - - if (state.contains("spacing")) { - double spacing[3]; - fromJsonArray(state["spacing"], spacing); - setSpacing(spacing); - } - - if (state.contains("units")) { - auto units = state["units"].toString(); - setUnits(units); - } - - if (state.contains("origin")) { - double origin[3]; - fromJsonArray(state["origin"], origin); - setDisplayPosition(origin); - } - - if (state.contains("orientation")) { - double orientation[3]; - fromJsonArray(state["orientation"], orientation); - setDisplayOrientation(orientation); - } - - // Check for modules on the data source first. - if (state.contains("modules") && state["modules"].isArray()) { - auto moduleArray = state["modules"].toArray(); - for (int i = 0; i < moduleArray.size(); ++i) { - auto moduleObj = moduleArray[i].toObject(); - auto viewId = moduleObj["viewId"].toInt(); - auto viewProxy = ModuleManager::instance().lookupView(viewId); - - // If we can't find the view, just default the currently active view - if (viewProxy == nullptr) { - viewProxy = ActiveObjects::instance().activeView(); - } - auto type = moduleObj["type"].toString(); - - // Plot modules require an OperatorResult, not a DataSource. They - // will be recreated when the operator pipeline is re-run and the - // user adds the Plot module again. - if (type == "Plot") { - qWarning() << "Skipping Plot module during state restore. Re-run" - << "the pipeline and add the Plot module to restore it."; - continue; - } - - auto m = - ModuleManager::instance().createAndAddModule(type, this, viewProxy); - if (!m) { - qWarning() << "Failed to create module of type:" << type; - continue; - } - m->deserialize(moduleObj); - } - } - // Now check for operators on the data source. - if (state.contains("operators") && state["operators"].isArray()) { - pipeline()->pause(); - Operator* op = nullptr; - QJsonObject operatorObj; - auto operatorArray = state["operators"].toArray(); - for (int i = 0; i < operatorArray.size(); ++i) { - operatorObj = operatorArray[i].toObject(); - op = OperatorFactory::instance().createOperator( - operatorObj["type"].toString(), this); - if (op && op->deserialize(operatorObj)) { - addOperator(op, /*append=*/true); - } - } - - // If we have a child data source we need to restore it once the data source - // has been create by the first execution of the pipeline. - if (op != nullptr && operatorObj.contains("dataSources")) { - // We currently support a single child data source. - auto dataSourcesState = operatorObj["dataSources"].toArray(); - auto connection = new QMetaObject::Connection; - *connection = connect(pipeline(), &Pipeline::finished, op, - [connection, dataSourcesState, op]() { - auto childDataSource = op->childDataSource(); - if (childDataSource != nullptr) { - childDataSource->deserialize( - dataSourcesState[0].toObject()); - } - QObject::disconnect(*connection); - delete connection; - }); - // If the child datasource has its own pipeline of operators increment the - // number of pipelineFinished signals to wait for before emitting - // stateLoaded() - if (dataSourcesState[0].toObject().contains("operators")) { - ModuleManager::instance().incrementPipelinesToWaitFor(); - } - } - - if (ModuleManager::instance().executePipelinesOnLoad()) { - pipeline()->resume(); - pipeline()->execute(this)->deleteWhenFinished(); - } - } - return true; -} - -DataSource* DataSource::clone() const -{ - auto newClone = new DataSource(vtkImageData::SafeDownCast(this->dataObject()), - this->Internals->Type, this->pipeline()); - newClone->setLabel(this->label()); - newClone->setPersistenceState(PersistenceState::Modified); - - if (this->Internals->Type == TiltSeries) { - newClone->setTiltAngles(getTiltAngles()); - } - - if (hasScanIDs(this->dataObject())) { - newClone->setScanIDs(getScanIDs()); - } - - QList newTimeSteps; - for (auto& timeStep : this->Internals->timeSeriesSteps) { - newTimeSteps.append(timeStep.clone()); - } - newClone->setTimeSeriesSteps(newTimeSteps); - - return newClone; -} - -vtkSMSourceProxy* DataSource::proxy() const -{ - return this->Internals->ProducerProxy; -} - -void DataSource::getExtent(int extent[6]) const -{ - vtkAlgorithm* tp = algorithm(); - if (tp) { - vtkImageData* data = vtkImageData::SafeDownCast(tp->GetOutputDataObject(0)); - if (data) { - data->GetExtent(extent); - return; - } - } - for (int i = 0; i < 6; ++i) { - extent[i] = 0; - } -} - -void DataSource::getBounds(double bounds[6]) -{ - vtkAlgorithm* tp = algorithm(); - if (tp) { - vtkImageData* data = vtkImageData::SafeDownCast(tp->GetOutputDataObject(0)); - if (data) { - data->GetBounds(bounds); - return; - } - } - for (int i = 0; i < 6; ++i) { - bounds[i] = 0.0; - } -} - -void DataSource::getRange(vtkImageData* imageData, double range[2]) -{ - for (int i = 0; i < 2; ++i) { - range[i] = 0.0; - } - - if (!imageData) { - return; - } - - auto* arrayPtr = imageData->GetPointData()->GetScalars(); - if (!arrayPtr) { - return; - } - - arrayPtr->GetFiniteRange(range, -1); -} - -void DataSource::getSpacing(double spacing[3]) const -{ - vtkAlgorithm* tp = algorithm(); - if (tp) { - vtkImageData* data = vtkImageData::SafeDownCast(tp->GetOutputDataObject(0)); - if (data) { - data->GetSpacing(spacing); - return; - } - } - for (int i = 0; i < 3; ++i) { - spacing[i] = 1; - } -} - -const double* DataSource::getSpacing() const -{ - return imageData()->GetSpacing(); -} - -void DataSource::setSpacing(const double spacing[3], bool markModified) -{ - if (markModified) { - Internals->UnitsModified = true; - } - - double mySpacing[3] = { spacing[0], spacing[1], spacing[2] }; - auto* data = imageData(); - if (data) { - data->SetSpacing(mySpacing); - } - emit dataPropertiesChanged(); -} - -void DataSource::getPhysicalDimensions(double lengths[3]) const -{ - int extent[6]; - getExtent(extent); - auto* spacing = getSpacing(); - - for (int axis = 0; axis < 3; ++axis) { - lengths[axis] = - spacing[axis] * (extent[2 * axis + 1] - extent[2 * axis] + 1); - } -} - -void DataSource::setActiveScalars(const QString& arrayName) -{ - if (!getScalarsArray(arrayName)) { - qWarning() << QString("Unable to select %1 as the active scalars array") - .arg(arrayName); - return; - } - - vtkAlgorithm* alg = algorithm(); - if (alg) { - vtkImageData* data = - vtkImageData::SafeDownCast(alg->GetOutputDataObject(0)); - if (data) { - data->GetPointData()->SetActiveScalars(arrayName.toLatin1().data()); - } - } - - dataModified(); - - emit activeScalarsChanged(); - emit dataPropertiesChanged(); -} - -void DataSource::setActiveScalars(int arrayIdx) -{ - QStringList scalars = listScalars(); - - if (arrayIdx < 0 || arrayIdx >= scalars.length()) { - return; - } - - setActiveScalars(scalars[arrayIdx]); -} - -QString DataSource::activeScalars() const -{ - QString returnValue; - vtkAlgorithm* alg = algorithm(); - if (alg) { - vtkImageData* data = - vtkImageData::SafeDownCast(alg->GetOutputDataObject(0)); - if (data) { - vtkDataArray* scalars = data->GetPointData()->GetScalars(); - if (scalars) { - returnValue = scalars->GetName(); - } - } - } - - return returnValue; -} - -int DataSource::activeScalarsIdx() const -{ - QString arrayName = activeScalars(); - QStringList scalars = listScalars(); - return scalars.indexOf(arrayName); -} - -QString DataSource::scalarsName(int arrayIdx) const -{ - QString arrayName; - QStringList scalars = listScalars(); - - if (arrayIdx >= 0 && arrayIdx < scalars.length()) { - arrayName = scalars[arrayIdx]; - } - - return arrayName; -} - -int DataSource::scalarsIdx(const QString& arrayName) const -{ - QStringList scalars = listScalars(); - return scalars.indexOf(arrayName); -} - -QStringList DataSource::listScalars() const -{ - QStringList scalars; - vtkAlgorithm* alg = algorithm(); - if (alg) { - vtkImageData* data = - vtkImageData::SafeDownCast(alg->GetOutputDataObject(0)); - if (data) { - vtkPointData* pointData = data->GetPointData(); - auto n = pointData->GetNumberOfArrays(); - for (int i = 0; i < n; ++i) { - scalars << pointData->GetArrayName(i); - } - } - } - return scalars; -} - -void DataSource::renameScalarsArray(const QString& oldName, - const QString& newName) -{ - if (oldName == newName) { - return; - } - - const bool isCurrentScalars = oldName == activeScalars(); - - // Ensure the array actually exist - vtkDataArray* dataArray = getScalarsArray(oldName); - if (dataArray == nullptr) { - return; - } - - // Ensure the target name is not already taken - vtkDataArray* targetArray = getScalarsArray(newName); - if (targetArray != nullptr) { - return; - } - - dataArray->SetName(newName.toLatin1().data()); - - if (isCurrentScalars) { - setActiveScalars(newName); - } else { - dataModified(); - emit activeScalarsChanged(); - emit dataPropertiesChanged(); - } - - auto originalName = Internals->CurrentToOriginal[oldName]; - Internals->CurrentToOriginal.remove(oldName); - Internals->CurrentToOriginal[newName] = originalName; -} - -vtkDataArray* DataSource::getScalarsArray(const QString& arrayName) const -{ - vtkAlgorithm* alg = algorithm(); - if (alg == nullptr) { - return nullptr; - } - vtkImageData* data = vtkImageData::SafeDownCast(alg->GetOutputDataObject(0)); - if (data == nullptr) { - return nullptr; - } - vtkPointData* pointData = data->GetPointData(); - if (pointData == nullptr) { - return nullptr; - } - if (pointData->HasArray(arrayName.toLatin1().data()) == 0) { - return nullptr; - } - return pointData->GetScalars(arrayName.toLatin1().data()); -} - -unsigned int DataSource::getNumberOfComponents() -{ - unsigned int numComponents = 0; - vtkAlgorithm* tp = this->algorithm(); - if (tp) { - vtkImageData* data = vtkImageData::SafeDownCast(tp->GetOutputDataObject(0)); - if (data) { - if (data->GetPointData() && data->GetPointData()->GetScalars()) { - numComponents = static_cast( - data->GetPointData()->GetScalars()->GetNumberOfComponents()); - } - } - } - - return numComponents; -} - -QString DataSource::getUnits() -{ - if (this->Internals->Units) { - return QString(this->Internals->Units->GetValue(0).c_str()); - } else { - return "nm"; - } -} - -void DataSource::setUnits(const QString& units, bool markModified) -{ - if (markModified) { - Internals->UnitsModified = true; - } - - if (!this->Internals->Units) { - this->Internals->Units = vtkSmartPointer::New(); - this->Internals->Units->SetName("units"); - this->Internals->Units->SetNumberOfValues(3); - this->Internals->Units->SetValue(0, "nm"); - this->Internals->Units->SetValue(1, "nm"); - this->Internals->Units->SetValue(2, "nm"); - vtkAlgorithm* alg = algorithm(); - if (alg) { - vtkDataObject* data = alg->GetOutputDataObject(0); - vtkFieldData* fd = data->GetFieldData(); - fd->AddArray(this->Internals->Units); - } - } - for (int i = 0; i < 3; ++i) { - this->Internals->Units->SetValue(i, units.toStdString().c_str()); - } - emit dataPropertiesChanged(); -} - -int DataSource::addOperator(Operator* op, bool append) -{ - op->setParent(this); - int index = -1; - if (!append) { - // LEGACY STUB: activeOperator() removed from ActiveObjects - Operator* activeOp = nullptr; - if (activeOp && activeOp->dataSource() == this) { - index = this->Internals->Operators.indexOf(activeOp); - } - } - if (index >= 0) { - // About to insert (not append). Ask the user for confirmation unless - // they previously checked "Don't ask again". - auto settings = pqApplicationCore::instance()->settings(); - bool skipConfirm = - settings->value("OperatorInsertConfirm/DontAsk", false).toBool(); - if (!skipConfirm) { - QMessageBox msgBox; - msgBox.setWindowTitle("Insert Operator?"); - msgBox.setText( - "Insert this operator before the selected operator in the pipeline?"); - auto* insertBtn = msgBox.addButton("Insert", QMessageBox::AcceptRole); - msgBox.addButton("Append to End", QMessageBox::RejectRole); - msgBox.setDefaultButton(insertBtn); - QCheckBox dontAskAgain("Don't ask again (always insert)"); - msgBox.setCheckBox(&dontAskAgain); - - msgBox.exec(); - - if (dontAskAgain.isChecked()) { - settings->setValue("OperatorInsertConfirm/DontAsk", true); - } - - if (msgBox.clickedButton() != insertBtn) { - // Append to the end instead of inserting - index = -1; - } - } - } - if (index >= 0) { - this->Internals->Operators.insert(index, op); - } else { - index = this->Internals->Operators.count(); - this->Internals->Operators.push_back(op); - } - emit operatorAdded(op); - - return index; -} - -bool DataSource::removeOperator(Operator* op) -{ - if (op) { - // We should emit that the operator was removed... - this->Internals->Operators.removeAll(op); - - emit this->operatorRemoved(op); - - op->deleteLater(); - - foreach (Operator* opPtr, this->Internals->Operators) { - std::cout << "Operator: " << opPtr->label().toLatin1().data() << std::endl; - } - - return true; - } - return false; -} - -bool DataSource::removeAllOperators() -{ - // TODO - return false if the pipeline is running - bool success = true; - - while (this->Internals->Operators.size() > 0) { - Operator* lastOperator = this->Internals->Operators.takeLast(); - - if (lastOperator->childDataSource() != nullptr) { - DataSource* childDataSource = lastOperator->childDataSource(); - - // Recurse on the child data source - success = childDataSource->removeAllOperators(); - if (!success) { - break; - } - } - - lastOperator->deleteLater(); - } - - if (success) { - ModuleManager::instance().removeAllModules(this); - } - - return success; -} - -void DataSource::dataModified() -{ - vtkTrivialProducer* tp = producer(); - if (tp == nullptr) { - return; - } - - tp->Modified(); - vtkDataObject* dObject = tp->GetOutputDataObject(0); - dObject->Modified(); - this->Internals->ProducerProxy->MarkModified(nullptr); - - vtkFieldData* fd = dObject->GetFieldData(); - if (fd->HasArray("tomviz_data_source_type")) { - vtkTypeInt8Array* typeArray = - vtkTypeInt8Array::SafeDownCast(fd->GetArray("tomviz_data_source_type")); - - // Casting is a bit hacky here, but it *should* work - setType((DataSourceType)(int)typeArray->GetTuple1(0)); - } else { - vtkNew typeArray; - typeArray->SetNumberOfComponents(1); - typeArray->SetNumberOfTuples(1); - typeArray->SetName("tomviz_data_source_type"); - typeArray->SetTuple1(0, this->Internals->Type); - fd->AddArray(typeArray); - } - - // This indirection is necessary to overcome a bug in VTK/ParaView when - // explicitly calling UpdatePipeline(). The extents don't reset to the whole - // extent. Until a proper fix makes it into VTK, this is needed. - vtkSMSessionProxyManager* pxm = - this->Internals->ProducerProxy->GetSessionProxyManager(); - vtkSMSourceProxy* filter = - vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("filters", "PassThrough")); - Q_ASSERT(filter); - vtkSMPropertyHelper(filter, "Input").Set(this->Internals->ProducerProxy, 0); - filter->UpdateVTKObjects(); - filter->UpdatePipeline(); - filter->Delete(); - - emit dataChanged(); -} - -const QList& DataSource::operators() const -{ - return this->Internals->Operators; -} - -void DataSource::translate(const double deltaPosition[3]) -{ - for (int i = 0; i < 3; ++i) { - this->Internals->DisplayPosition[i] += deltaPosition[i]; - } - emit displayPositionChanged(this->Internals->DisplayPosition[0], - this->Internals->DisplayPosition[1], - this->Internals->DisplayPosition[2]); -} - -const double* DataSource::displayPosition() const -{ - return this->Internals->DisplayPosition.GetData(); -} - -void DataSource::displayPosition(double position[3]) const -{ - auto* pos = displayPosition(); - for (int i = 0; i < 3; ++i) { - position[i] = pos[i]; - } -} - -void DataSource::setDisplayPosition(const double newPosition[3]) -{ - for (int i = 0; i < 3; ++i) { - this->Internals->DisplayPosition[i] = newPosition[i]; - } - emit displayPositionChanged(this->Internals->DisplayPosition[0], - this->Internals->DisplayPosition[1], - this->Internals->DisplayPosition[2]); -} - -const double* DataSource::displayOrientation() const -{ - return this->Internals->DisplayOrientation.GetData(); -} - -void DataSource::setDisplayOrientation(const double newOrientation[3]) -{ - for (int i = 0; i < 3; ++i) { - this->Internals->DisplayOrientation[i] = newOrientation[i]; - } - emit displayOrientationChanged(this->Internals->DisplayOrientation[0], - this->Internals->DisplayOrientation[1], - this->Internals->DisplayOrientation[2]); -} - -vtkDataObject* DataSource::copyData() -{ - this->Internals->ProducerProxy->UpdatePipeline(); - vtkDataObject* data = dataObject(); - vtkDataObject* copy = data->NewInstance(); - copy->DeepCopy(data); - - return copy; -} - -void DataSource::setData(vtkDataObject* newData) -{ - auto tp = producer(); - Q_ASSERT(tp); - tp->SetOutput(newData); - auto fd = newData->GetFieldData(); - vtkSmartPointer typeArray = - vtkTypeInt8Array::SafeDownCast(fd->GetArray("tomviz_data_source_type")); - if (typeArray && typeArray->GetTuple1(0) == TiltSeries) { - this->Internals->ensureTiltAnglesArrayExists(); - this->Internals->Type = TiltSeries; - } else if (typeArray && typeArray->GetTuple1(0) == FIB) { - this->Internals->Type = FIB; - } else { - this->Internals->Type = Volume; - } - if (fd->HasArray("units")) { - this->Internals->Units = - vtkStringArray::SafeDownCast(fd->GetAbstractArray("units")); - } else if (this->Internals->Units) { - fd->AddArray(this->Internals->Units); - } - if (!typeArray) { - typeArray = vtkSmartPointer::New(); - typeArray->SetNumberOfComponents(1); - typeArray->SetNumberOfTuples(1); - typeArray->SetName("tomviz_data_source_type"); - fd->AddArray(typeArray); - } - typeArray->SetTuple1(0, this->Internals->Type); - - // Initialize maps to track array renames - Internals->CurrentToOriginal.clear(); - auto arrayNames = listScalars(); - for (auto name : arrayNames) { - Internals->CurrentToOriginal[name] = name; - } - - // Make sure everything gets updated with the new data - dataModified(); -} - -void DataSource::copyData(vtkDataObject* newData) -{ - auto tp = producer(); - Q_ASSERT(tp); - auto oldData = tp->GetOutputDataObject(0); - Q_ASSERT(oldData); - - oldData->DeepCopy(newData); - - dataModified(); - - emit activeScalarsChanged(); -} - -void DataSource::onTimeChanged() -{ - if (!ActiveObjects::instance().timeSeriesAnimationsEnabled()) { - return; - } - - auto* timeKeeper = ActiveObjects::instance().activeTimeKeeper(); - if (!timeKeeper) { - return; - } - - // Use interpolation to figure out which time step we are at - auto numTimeSteps = numTimeSeriesSteps(); - if (numTimeSteps <= 1) { - return; - } - - auto timeSteps = timeKeeper->getTimeSteps(); - if (timeSteps.size() == 1) { - return; - } else if (timeSteps.size() == 0) { - // It's just a 0 to 1 default. - timeSteps.append(0); - timeSteps.append(1); - } - - auto timeStart = timeSteps.front(); - auto timeStop = timeSteps.back(); - auto time = timeKeeper->getTime(); - - auto scale = ((numTimeSteps - 1) / (timeStop - timeStart)); - auto timeStep = std::round((time - timeStart) * scale); - switchTimeSeriesStep(timeStep); -} - -void DataSource::switchTimeSeriesStep(int i) -{ - if (i >= this->Internals->timeSeriesSteps.size()) { - auto size = this->Internals->timeSeriesSteps.size(); - qCritical() << i - << "is out of bounds for number of time series steps: " << size; - return; - } - - m_changingTimeStep = true; - this->Internals->currentTimeStep = i; - producer()->SetOutput(this->Internals->timeSeriesSteps[i].image); - dataModified(); - m_changingTimeStep = false; - - emit timeStepChanged(); -} - -int DataSource::numTimeSeriesSteps() const -{ - return this->Internals->timeSeriesSteps.size(); -} - -int DataSource::currentTimeSeriesIndex() const -{ - return this->Internals->currentTimeStep; -} - -void DataSource::setTimeSeriesSteps(const QList& steps) -{ - this->Internals->timeSeriesSteps = steps; - emit timeStepsModified(); - - // Update the data if we need to - auto current = currentTimeSeriesIndex(); - if (current < steps.size() && steps[current].image != imageData()) { - // Re-use the logic here - switchTimeSeriesStep(current); - } -} - -void DataSource::addTimeSeriesSteps(const QList& steps) -{ - this->Internals->timeSeriesSteps.append(steps); - emit timeStepsModified(); -} - -void DataSource::addTimeSeriesStep(const TimeSeriesStep& step) -{ - this->Internals->timeSeriesSteps.append(step); - emit timeStepsModified(); -} - -TimeSeriesStep DataSource::currentTimeSeriesStep() -{ - if (!hasTimeSteps()) { - qDebug() << "currentTimeSeriesStep() was called, but there " - << "are no time steps!"; - return TimeSeriesStep(); - } - - auto current = this->Internals->currentTimeStep; - if (current >= numTimeSeriesSteps()) { - qDebug() << "Current time step is out of bounds!"; - return TimeSeriesStep(); - } - - return this->Internals->timeSeriesSteps[current]; -} - -QList DataSource::timeSeriesSteps() const -{ - return this->Internals->timeSeriesSteps; -} - -void DataSource::clearTimeSeriesSteps() -{ - this->Internals->timeSeriesSteps.clear(); - this->Internals->currentTimeStep = 0; - emit timeStepsModified(); - emit timeStepChanged(); -} - -vtkSMProxy* DataSource::colorMap() const -{ - return this->Internals->ColorMap; -} - -DataSource::DataSourceType DataSource::type() const -{ - return this->Internals->Type; -} - -void DataSource::setType(DataSourceType t) -{ - this->Internals->Type = t; - auto data = this->dataObject(); - setType(data, t); - if (t == TiltSeries) { - this->Internals->ensureTiltAnglesArrayExists(); - } - emit dataChanged(); -} - -bool DataSource::hasTiltAngles() -{ - vtkDataObject* data = this->dataObject(); - - return hasTiltAngles(data); -} - -QVector DataSource::getTiltAngles() const -{ - auto data = this->dataObject(); - return getTiltAngles(data); -} - -void DataSource::setTiltAngles(const QVector& angles) -{ - auto data = this->dataObject(); - setTiltAngles(data, angles); - emit dataChanged(); -} - -vtkSMProxy* DataSource::opacityMap() const -{ - return this->Internals->ColorMap - ? vtkSMPropertyHelper(this->Internals->ColorMap, - "ScalarOpacityFunction") - .GetAsProxy() - : nullptr; -} - -vtkPiecewiseFunction* DataSource::gradientOpacityMap() const -{ - return this->Internals->GradientOpacityMap; -} - -vtkImageData* DataSource::transferFunction2D() const -{ - return this->Internals->m_transfer2D; -} - -vtkRectd* DataSource::transferFunction2DBox() const -{ - return &this->Internals->m_transferFunction2DBox; -} - -bool DataSource::hasLabelMap() -{ - auto dataSource = proxy(); - if (!dataSource) { - return false; - } - - // We could just as easily go to the client side VTK object to get this info, - // but we'll go the ParaView route for now. - vtkPVDataInformation* dataInfo = dataSource->GetDataInformation(); - vtkPVDataSetAttributesInformation* pointDataInfo = - dataInfo->GetPointDataInformation(); - vtkPVArrayInformation* labelMapInfo = - pointDataInfo->GetArrayInformation("LabelMap"); - - return labelMapInfo != nullptr; -} - -void DataSource::updateColorMap() -{ - if (m_changingTimeStep) { - // Don't update the color map for time step changes - return; - } - - rescaleColorMap(colorMap(), this); -} - -void DataSource::setPersistenceState(DataSource::PersistenceState state) -{ - this->Internals->PersistState = state; -} - -DataSource::PersistenceState DataSource::persistenceState() const -{ - return this->Internals->PersistState; -} - -vtkTrivialProducer* DataSource::producer() const -{ - Q_ASSERT(vtkTrivialProducer::SafeDownCast(proxy()->GetClientSideObject())); - return vtkTrivialProducer::SafeDownCast(proxy()->GetClientSideObject()); -} - -void DataSource::init(vtkImageData* data, DataSourceType dataType, - PersistenceState persistState) -{ - m_pythonProxy = new DataSourceBase; - this->Internals->Type = dataType; - this->Internals->PersistState = persistState; - this->Internals->DisplayPosition.Set(0, 0, 0); - this->Internals->DisplayOrientation.Set(0, 0, 0); - - // Set up default rect for transfer function 2d... - // The widget knows to interpret a rect with negative width as - // uninitialized. - this->Internals->m_transferFunction2DBox.Set(0, 0, -1, -1); - - vtkNew controller; - auto pxm = ActiveObjects::instance().proxyManager(); - Q_ASSERT(pxm); - - // If dataSource is null then we need to create the producer - vtkSmartPointer source; - source.TakeReference(pxm->NewProxy("sources", "TrivialProducer")); - Q_ASSERT(source != nullptr); - Q_ASSERT(vtkSMSourceProxy::SafeDownCast(source)); - this->Internals->ProducerProxy = vtkSMSourceProxy::SafeDownCast(source); - controller->RegisterPipelineProxy(this->Internals->ProducerProxy); - - if (data) { - auto tp = vtkTrivialProducer::SafeDownCast(source->GetClientSideObject()); - tp->SetOutput(data); - ensureActiveArray(); - } - - // Initialize maps to track array renames - Internals->CurrentToOriginal.clear(); - auto arrayNames = listScalars(); - for (auto name : arrayNames) { - Internals->CurrentToOriginal[name] = name; - } - - // Setup color map for this data-source. - static unsigned int colorMapCounter = 0; - ++colorMapCounter; - - vtkNew tfmgr; - this->Internals->ColorMap = tfmgr->GetColorTransferFunction( - QString("DataSourceColorMap%1").arg(colorMapCounter).toLatin1().data(), - pxm); - ColorMap::instance().applyPreset(this->Internals->ColorMap); - updateColorMap(); - - // Every time the data changes, we should update the color map. - connect(this, &DataSource::dataChanged, this, &DataSource::updateColorMap); - - connect(this, &DataSource::dataPropertiesChanged, - [this]() { this->proxy()->MarkModified(nullptr); }); - - auto* timeKeeper = ActiveObjects::instance().activeTimeKeeper(); - if (timeKeeper) { - connect(timeKeeper, &pqTimeKeeper::timeChanged, this, - &DataSource::onTimeChanged); - } -} - -vtkAlgorithm* DataSource::algorithm() const -{ - if (!proxy() || !proxy()->GetClientSideObject()) { - return nullptr; - } - return vtkAlgorithm::SafeDownCast(proxy()->GetClientSideObject()); -} - -vtkDataObject* DataSource::dataObject() const -{ - auto alg = algorithm(); - if (!alg) { - return nullptr; - } - return alg->GetOutputDataObject(0); -} - -vtkImageData* DataSource::imageData() const -{ - return vtkImageData::SafeDownCast(dataObject()); -} - -vtkDataArray* DataSource::scalars() const -{ - return getScalarsArray(activeScalars()); -} - -QStringList DataSource::componentNames() -{ - ensureValidComponentNames(); - - QStringList names; - for (int i = 0; i < scalars()->GetNumberOfComponents(); ++i) { - names.append(scalars()->GetComponentName(i)); - } - - return names; -} - -void DataSource::setComponentNames(const QStringList& names) -{ - for (int i = 0; i < scalars()->GetNumberOfComponents(); ++i) { - scalars()->SetComponentName(i, names[i].toLatin1().data()); - } - emit componentNamesModified(); -} - -void DataSource::setComponentName(int index, const QString& name) -{ - scalars()->SetComponentName(index, name.toLatin1().data()); - emit componentNamesModified(); -} - -void DataSource::ensureValidComponentNames() -{ - bool modified = false; - QStringList approvedNames; - for (int i = 0; i < scalars()->GetNumberOfComponents(); ++i) { - QString name = scalars()->GetComponentName(i); - if (name.isEmpty() || approvedNames.contains(name)) { - // If this name is empty or duplicated, rename it. - size_t counter = 0; - QString newName; - do { - newName = name + QString::number(++counter); - } while (approvedNames.contains(newName)); - - scalars()->SetComponentName(i, newName.toLatin1().data()); - name = newName; - modified = true; - } - - approvedNames.append(name); - } - - if (modified) { - emit componentNamesModified(); - } -} - -Pipeline* DataSource::pipeline() const -{ - return qobject_cast(parent()); -} - -bool DataSource::unitsModified() -{ - return Internals->UnitsModified; -} - -bool DataSource::isTransient() const -{ - return Internals->PersistState == PersistenceState::Transient; -} - -bool DataSource::forkable() -{ - return Internals->Forkable; -} - -void DataSource::setForkable(bool forkable) -{ - Internals->Forkable = forkable; -} - -void DataSource::ensureActiveArray() -{ - // If there is no active array, then set one. - if (!imageData() || !imageData()->GetPointData()) { - return; - } - - auto* pointData = imageData()->GetPointData(); - if (pointData->GetScalars() || pointData->GetNumberOfArrays() == 0) { - return; - } - - pointData->SetActiveScalars(pointData->GetArrayName(0)); -} - -bool DataSource::hasTiltAngles(vtkDataObject* image) -{ - vtkFieldData* fd = image->GetFieldData(); - - return fd->HasArray("tilt_angles"); -} - -QVector DataSource::getTiltAngles(vtkDataObject* data) -{ - QVector result; - auto fd = data->GetFieldData(); - - if (fd->HasArray("tilt_angles")) { - auto tiltAngles = fd->GetArray("tilt_angles"); - result.resize(tiltAngles->GetNumberOfTuples()); - for (int i = 0; i < result.size(); ++i) { - result[i] = tiltAngles->GetTuple1(i); - } - } - return result; -} - -void DataSource::setTiltAngles(vtkDataObject* data, - const QVector& angles) -{ - auto fd = data->GetFieldData(); - createOrResizeTiltAnglesArray(data); - if (fd->HasArray("tilt_angles")) { - auto tiltAngles = fd->GetArray("tilt_angles"); - for (int i = 0; i < tiltAngles->GetNumberOfTuples() && i < angles.size(); - ++i) { - tiltAngles->SetTuple1(i, angles[i]); - } - } -} - -// My attempt to reduce some of the boiler plate code in the functions below -template -void setFieldDataArray(vtkFieldData* fd, const char* arrayName, int numTuples, - T* data) -{ - if (!fd->HasArray(arrayName)) { - vtkNew typeArray; - typeArray->SetNumberOfComponents(1); - typeArray->SetNumberOfTuples(numTuples); - typeArray->SetName(arrayName); - fd->AddArray(typeArray); - } - - ArrayType* typeArray = ArrayType::SafeDownCast(fd->GetArray(arrayName)); - for (int i = 0; i < numTuples; ++i) - typeArray->SetTuple1(i, data[i]); -} - -template -void getFieldDataArray(vtkFieldData* fd, const char* arrayName, int numTuples, - T* data) -{ - if (!fd->HasArray(arrayName)) - return; - - ArrayType* typeArray = ArrayType::SafeDownCast(fd->GetArray(arrayName)); - for (int i = 0; i < numTuples; ++i) - data[i] = typeArray->GetTuple1(i); -} - -void DataSource::setType(vtkDataObject* image, DataSourceType t) -{ - if (!image) - return; - - // Cast to int before setting - int i = static_cast(t); - - const char* arrayName = "tomviz_data_source_type"; - using ArrayType = vtkTypeInt8Array; - - vtkFieldData* fd = image->GetFieldData(); - setFieldDataArray(fd, arrayName, 1, &i); - - if (t != DataSourceType::TiltSeries) { - // Clear the tilt angles - clearTiltAngles(image); - } -} - -void DataSource::clearTiltAngles(vtkDataObject* image) -{ - if (!image) - return; - - const char* arrayName = "tilt_angles"; - auto fd = image->GetFieldData(); - if (fd->HasArray(arrayName)) { - fd->RemoveArray(arrayName); - } -} - -bool DataSource::hasScanIDs(vtkDataObject* image) -{ - if (!image) - return false; - - return image->GetFieldData()->HasArray("scan_ids"); -} - -QVector DataSource::getScanIDs(vtkDataObject* image) -{ - QVector result; - if (!image) - return result; - - auto fd = image->GetFieldData(); - if (fd->HasArray("scan_ids")) { - auto scanIds = fd->GetArray("scan_ids"); - result.resize(scanIds->GetNumberOfTuples()); - for (int i = 0; i < result.size(); ++i) { - result[i] = static_cast(scanIds->GetTuple1(i)); - } - } - return result; -} - -void DataSource::setScanIDs(vtkDataObject* image, - const QVector& scanIDs) -{ - if (!image) - return; - - auto fd = image->GetFieldData(); - int numTuples = scanIDs.size(); - std::vector data(numTuples); - for (int i = 0; i < numTuples; ++i) { - data[i] = scanIDs[i]; - } - setFieldDataArray(fd, "scan_ids", numTuples, data.data()); -} - -void DataSource::clearScanIDs(vtkDataObject* image) -{ - if (!image) - return; - - auto fd = image->GetFieldData(); - if (fd->HasArray("scan_ids")) { - fd->RemoveArray("scan_ids"); - } -} - -bool DataSource::hasScanIDs() -{ - return hasScanIDs(dataObject()); -} - -QVector DataSource::getScanIDs() const -{ - return getScanIDs(dataObject()); -} - -void DataSource::setScanIDs(const QVector& scanIDs) -{ - setScanIDs(dataObject(), scanIDs); -} - -void DataSource::clearScanIDs() -{ - clearScanIDs(dataObject()); -} - -bool DataSource::wasSubsampled(vtkDataObject* image) -{ - bool ret = false; - - if (!image) - return ret; - - const char* arrayName = "was_subsampled"; - using ArrayType = vtkTypeInt8Array; - - vtkFieldData* fd = image->GetFieldData(); - getFieldDataArray(fd, arrayName, 1, &ret); - return ret; -} - -void DataSource::setWasSubsampled(vtkDataObject* image, bool b) -{ - if (!image) - return; - - const char* arrayName = "was_subsampled"; - using ArrayType = vtkTypeInt8Array; - - vtkFieldData* fd = image->GetFieldData(); - setFieldDataArray(fd, arrayName, 1, &b); -} - -void DataSource::subsampleStrides(vtkDataObject* image, int s[3]) -{ - // Set the default in case we return - for (int i = 0; i < 3; ++i) - s[i] = 1; - - if (!image) - return; - - const char* arrayName = "subsample_strides"; - using ArrayType = vtkTypeInt32Array; - - vtkFieldData* fd = image->GetFieldData(); - getFieldDataArray(fd, arrayName, 3, s); -} - -void DataSource::setSubsampleStrides(vtkDataObject* image, int s[3]) -{ - if (!image) - return; - - const char* arrayName = "subsample_strides"; - using ArrayType = vtkTypeInt32Array; - - vtkFieldData* fd = image->GetFieldData(); - setFieldDataArray(fd, arrayName, 3, s); -} - -void DataSource::subsampleVolumeBounds(vtkDataObject* image, int bs[6]) -{ - // Set the default in case we return - for (int i = 0; i < 6; ++i) - bs[i] = -1; - - if (!image) - return; - - const char* arrayName = "subsample_volume_bounds"; - using ArrayType = vtkTypeInt32Array; - - vtkFieldData* fd = image->GetFieldData(); - getFieldDataArray(fd, arrayName, 6, bs); -} - -void DataSource::setSubsampleVolumeBounds(vtkDataObject* image, int bs[6]) -{ - if (!image) - return; - - const char* arrayName = "subsample_volume_bounds"; - using ArrayType = vtkTypeInt32Array; - - vtkFieldData* fd = image->GetFieldData(); - setFieldDataArray(fd, arrayName, 6, bs); -} - -} // namespace tomviz diff --git a/tomviz/legacy/DataSource.h b/tomviz/legacy/DataSource.h deleted file mode 100644 index be43745d9..000000000 --- a/tomviz/legacy/DataSource.h +++ /dev/null @@ -1,532 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizDataSource_h -#define tomvizDataSource_h - -#include - -#include -#include -#include -#include - -#include -#include - -#include "core/Variant.h" - -class vtkSMProxy; -class vtkSMSourceProxy; -class vtkImageData; -class vtkDataArray; -class vtkDataObject; -class vtkPiecewiseFunction; -class vtkAlgorithm; -class vtkTrivialProducer; - -namespace tomviz { -class DataSourceBase; -class Operator; -class Pipeline; -struct TimeSeriesStep; - -using MetadataType = std::map; - -/// Encapsulation for a DataSource. This class manages a data source, including -/// the provenance for any operations performed on the data source. -class DataSource : public QObject -{ - Q_OBJECT - -public: - /// The type of data in the data source. The data types currently supported - /// are volumetric data and image stacks representing tilt series. - enum DataSourceType - { - Volume, - TiltSeries, - FIB - }; - - enum class PersistenceState - { - Transient, // Doesn't need to be written to disk - Saved, // Written to disk - Modified // Needs to be written to disk - }; - - /// Deprecated constructor, prefer directly setting data. - DataSource(vtkSMSourceProxy* dataSource, DataSourceType dataType = Volume); - - /// \c dataSource is the original reader that reads the data into the - /// application. - DataSource(vtkImageData* dataSource, DataSourceType dataType = Volume, - QObject* parent = nullptr, - PersistenceState persistState = PersistenceState::Saved); - - /// Create a new dataSource not associated with a source proxy - DataSource(const QString& label = QString(), DataSourceType dataType = Volume, - QObject* parent = nullptr, - PersistenceState persistState = PersistenceState::Saved, - const QJsonObject& sourceInfo = QJsonObject()); - - ~DataSource() override; - - /// Append a slice to the data source, this must be of the same x and y - /// dimension as the existing slices in order to be appended. - bool appendSlice(vtkImageData* slice); - - /// Returns the proxy that can be inserted in ParaView pipelines. - /// This proxy instance doesn't change over the lifetime of a DataSource even - /// if new DataOperators are added to the source. - vtkSMSourceProxy* proxy() const; - - //// Returns the trivial producer to insert in VTK pipelines. - vtkTrivialProducer* producer() const; - - /// Returns the output data object associated with the proxy. - vtkDataObject* dataObject() const; - - /// Returns the image data associated with the proxy. - vtkImageData* imageData() const; - - /// Get the active scalars array - vtkDataArray* scalars() const; - - /// Get the names of the components. - /// Calls "ensureValidComponentNames()" first, and the names will be - /// modified if they are invalid. - QStringList componentNames(); - - /// Set the names of the components - void setComponentNames(const QStringList& names); - - /// Set the name of an individual component - void setComponentName(int index, const QString& name); - - /// Ensure component names are valid, and modify them if they are not. - void ensureValidComponentNames(); - - /// Returns a list of operators added to the DataSource. - const QList& operators() const; - - /// Add/remove operators. - int addOperator(Operator* op, bool append = false); - bool removeOperator(Operator* op); - bool removeAllOperators(); - - /// Creates a new clone from this DataSource. - DataSource* clone() const; - - /// Get the unique id for this DataSource. - QString id() const; - - /// Save the state out. - QJsonObject serialize() const; - bool deserialize(const QJsonObject& state); - - /// Set the file name. - void setFileName(const QString& fileName); - - /// Returns the name of the file used to load the data source. - QString fileName() const; - - /// Set the ordered list of file names if loading from a stack of images. - void setFileNames(const QStringList fileNames); - - /// Returns the list of files used to load the volume (if a stack was used). - QStringList fileNames() const; - - /// For a Tvh5 file, set the path to the node to read for this data source - void setTvh5NodePath(const QString& path); - - /// For a Tvh5 file, get the path to the node to read for this data source - QString tvh5NodePath() const; - - /// Return true is data source is an image stack, false otherwise. - bool isImageStack() const; - - /// Set the PV reader properties. - void setReaderProperties(const QVariantMap& properties); - - /// Get the PV reader properties. - QVariantMap readerProperties() const; - - /// Get the metadata - MetadataType metadata() { return m_metadata; } - - /// Set the metadata explicitly - void setMetadata(const MetadataType& m); - - /// Set/get the dark/white data, used in Data Exchange currently - void setDarkData(vtkSmartPointer image); - - /// Set/get the dark/white data, used in Data Exchange currently - vtkImageData* darkData() const; - - /// Set/get the dark/white data, used in Data Exchange currently - void setWhiteData(vtkSmartPointer image); - - /// Set/get the dark/white data, used in Data Exchange currently - vtkImageData* whiteData() const; - - // Used to track if a volume visualization module was added - bool volumeModuleAutoAdded() const; - void setVolumeModuleAutoAdded(bool b); - - /// Check to see if the data was subsampled while reading - bool wasSubsampled() const; - - /// Set whether the data was subsampled while reading - void setWasSubsampled(bool b); - - /// Get the strides used to generate the subsample - void subsampleStrides(int s[3]) const; - - /// Set the strides used to generate the subsample - void setSubsampleStrides(int s[3]); - - /// Get the volume bounds used to generate the subsample - void subsampleVolumeBounds(int bs[6]) const; - - /// Set the volume bounds used to generate the subsample - void setSubsampleVolumeBounds(int bs[6]); - - /// Can we reload and resample the original dataset? - bool canReloadAndResample() const; - - // Reload and resample the original dataset - bool reloadAndResample(); - - /// Set the label for the data source. - void setLabel(const QString& label); - - /// Returns the name of the filename used from the originalDataSource. - QString label() const; - - /// Returns the type of data in this DataSource - DataSourceType type() const; - /// Sets the type of data in the DataSource - void setType(DataSourceType t); - - /// Whether or not the DataSource has time series steps - bool hasTimeSteps() const { return numTimeSeriesSteps() != 0; } - - /// The number of time series steps that the DataSource has - int numTimeSeriesSteps() const; - - /// The index of the time series we are currently using - int currentTimeSeriesIndex() const; - - /// Switch to a different time series step - void switchTimeSeriesStep(int i); - - /// Set the time series steps - void setTimeSeriesSteps(const QList& steps); - - /// Append a list of time series steps - void addTimeSeriesSteps(const QList& steps); - - /// Add a time series step - void addTimeSeriesStep(const TimeSeriesStep& step); - - /// Get the current time series step - TimeSeriesStep currentTimeSeriesStep(); - - /// Get a copy of all time series steps - QList timeSeriesSteps() const; - - /// Remove all time series steps - void clearTimeSeriesSteps(); - - /// Are we in the middle of changing time steps? - bool isChangingTimeStep() const { return m_changingTimeStep; } - - /// Returns the color map for the DataSource. - vtkSMProxy* colorMap() const; - vtkSMProxy* opacityMap() const; - vtkPiecewiseFunction* gradientOpacityMap() const; - vtkImageData* transferFunction2D() const; - vtkRectd* transferFunction2DBox() const; - - /// Indicates whether the DataSource has a label map of the voxels. - bool hasLabelMap(); - - /// Crop the data to the given volume - void crop(int bounds[6]); - - /// Returns true if the dataset already has a tilt angles array - /// --- this CAN return true even if the dataset is currently a - /// volume. This is to tell if switching the dataset to a tilt - /// series also needs to set the tilt angles. - bool hasTiltAngles(); - - /// Get a copy of the current tilt angles - QVector getTiltAngles() const; - - /// Set the tilt angles to the values in the given QVector - void setTiltAngles(const QVector& angles); - - /// Remove the tilt angles from the data source - void clearTiltAngles(); - - /// Returns true if the dataset has scan IDs - bool hasScanIDs(); - - /// Get scan IDs (if available - otherwise an empty vector is returned) - QVector getScanIDs() const; - - /// Set the scan IDs - void setScanIDs(const QVector& scanIDs); - - /// Remove scan IDs - void clearScanIDs(); - - /// Moves the displayPosition of the DataSource by deltaPosition - void translate(const double deltaPosition[3]); - - /// Gets the display position of the data source - const double* displayPosition() const; - void displayPosition(double pos[3]) const; - - /// Sets the display position of the data source - void setDisplayPosition(const double newPosition[3]); - - /// Gets the display orientation of the data source - const double* displayOrientation() const; - - /// Sets the display orientation of the data source - void setDisplayOrientation(const double newOrientation[3]); - - /// Returns the extent of the transformed dataset - void getExtent(int extent[6]) const; - /// Returns the physical extent (bounds) of the transformed dataset - void getBounds(double bounds[6]); - /// Returns the range of the transformed dataset - void getRange(double range[2]) { getRange(imageData(), range); } - static void getRange(vtkImageData* data, double range[2]); - /// Returns the spacing of the transformed dataset - void getSpacing(double spacing[3]) const; - const double* getSpacing() const; - - /// Sets the scale factor (ratio between units and spacing) - /// one component per axis - void setSpacing(const double scaleFactor[3], bool markModified = true); - - /// Get the physical dimensions - /// Computed via spacing[i] * (extent[2 * i + 1] - extent[2 * i] + 1) - void getPhysicalDimensions(double lengths[3]) const; - - /// Set the active scalars by array name. - void setActiveScalars(const QString& arrayName); - QString activeScalars() const; - /// Set the active scalars by component index. - void setActiveScalars(int arrayIdx); - int activeScalarsIdx() const; - /// Get the scalars name for a given index. - QString scalarsName(int arrayIdx) const; - - /// Get the scalars idx for a given name. - int scalarsIdx(const QString& arrayName) const; - - /// Get the scalars list - QStringList listScalars() const; - - // Get pointer to scalar array - vtkDataArray* getScalarsArray(const QString& arrayName) const; - - /// Returns the number of components in the dataset. - unsigned int getNumberOfComponents(); - - /// Returns a string describing the units for the data - QString getUnits(); - /// Set the string describing the units - void setUnits(const QString& units, bool markModified = true); - - /// Set the persistence state - void setPersistenceState(PersistenceState state); - - /// Returns the persistence state - PersistenceState persistenceState() const; - - Pipeline* pipeline() const; - - /// Create copy of current data object, caller is responsible for ownership - vtkDataObject* copyData(); - - /// If there are arrays in the data, ensure one of them is active. - void ensureActiveArray(); - - /// Set data output of trivial producer to new data object, the trivial - /// producer takes over ownership of the data object. - void setData(vtkDataObject* newData); - - /// Copy data from a data object to the existing data. - void copyData(vtkDataObject* newData); - - bool unitsModified(); - - // Returns true if the data source is not associated with a file, false - // otherwise. - bool isTransient() const; - - /// Returns true if child operators can be added to this data source, false - // otherwise. - bool forkable(); - void setForkable(bool forkable); - - static void setType(vtkDataObject* image, DataSourceType t); - - static bool hasTiltAngles(vtkDataObject* image); - static QVector getTiltAngles(vtkDataObject* image); - static void setTiltAngles(vtkDataObject* image, - const QVector& angles); - static void clearTiltAngles(vtkDataObject* image); - - static bool hasScanIDs(vtkDataObject* image); - static QVector getScanIDs(vtkDataObject* image); - static void setScanIDs(vtkDataObject* image, const QVector& scanIDs); - static void clearScanIDs(vtkDataObject* image); - - /// Check to see if the data was subsampled while reading - static bool wasSubsampled(vtkDataObject* image); - - /// Set whether the data was subsampled while reading - static void setWasSubsampled(vtkDataObject* image, bool b); - - /// Get the strides used to generate the subsample - static void subsampleStrides(vtkDataObject* image, int s[3]); - - /// Set the strides used to generate the subsample - static void setSubsampleStrides(vtkDataObject* image, int s[3]); - - /// Get the volume bounds used to generate the subsample - static void subsampleVolumeBounds(vtkDataObject* image, int bs[6]); - - /// Set the volume bounds used to generate the subsample - static void setSubsampleVolumeBounds(vtkDataObject* image, int bs[6]); - - /// Get a simple proxy for the data source to simplify Python wrapping. - DataSourceBase* pythonProxy() const { return m_pythonProxy; } - -signals: - /// This signal is fired to notify the world that the DataSource may have - /// new/updated data. - void dataChanged(); - - /// This signal is fired to notify the world that the data's properties may - /// have changed. - void dataPropertiesChanged(); - - /// Fired when active scalars change - void activeScalarsChanged(); - - /// This signal is fired every time a new operator is added to this - /// DataSource. - void operatorAdded(Operator*); - - void operatorRemoved(Operator*); - - /// This signal is fired every time the display position is changed - /// Any actors based on this DataSource's data should update the position - /// on their actors to match this so the effect of setting the position is - /// to translate the dataset. - void displayPositionChanged(double newX, double newY, double newZ); - - /// This signal is fired every time the display orientation is changed - /// Any actors based on this DataSource's data should update the orientation - /// on their actors to match this so the effect of setting the orientation is - /// to rotate the dataset. - void displayOrientationChanged(double newX, double newY, double newZ); - - /// Indicates the component names have been modified - void componentNamesModified(); - - /// Indicates that the current time step has been changed - void timeStepChanged(); - - /// Indicate that the time steps have been modified - void timeStepsModified(); - -public slots: - void dataModified(); - void renameScalarsArray(const QString& oldName, const QString& newName); - -protected slots: - /// update the color map range. - void updateColorMap(); - - /// What to do when the time is changed... - void onTimeChanged(); - -private: - /// Private method to initialize the data source. - void init(vtkImageData* dataSource, DataSourceType dataType, - PersistenceState persistState); - - vtkAlgorithm* algorithm() const; - - Q_DISABLE_COPY(DataSource) - - class DSInternals; - const QScopedPointer Internals; - - /// This is a simple proxy class for forwarding Python calls. - DataSourceBase* m_pythonProxy = nullptr; - - QJsonObject m_json; - MetadataType m_metadata; - - bool m_changingTimeStep = false; - bool m_volumeModuleAutoAdded = false; -}; - -inline void DataSource::clearTiltAngles() -{ - clearTiltAngles(dataObject()); -} - -inline bool DataSource::wasSubsampled() const -{ - return wasSubsampled(dataObject()); -} - -inline void DataSource::setWasSubsampled(bool b) -{ - setWasSubsampled(dataObject(), b); -} - -inline void DataSource::subsampleStrides(int s[3]) const -{ - subsampleStrides(dataObject(), s); -} - -inline void DataSource::setSubsampleStrides(int s[3]) -{ - setSubsampleStrides(dataObject(), s); -} - -inline void DataSource::subsampleVolumeBounds(int bs[6]) const -{ - subsampleVolumeBounds(dataObject(), bs); -} - -inline void DataSource::setSubsampleVolumeBounds(int bs[6]) -{ - setSubsampleVolumeBounds(dataObject(), bs); -} - -inline bool DataSource::volumeModuleAutoAdded() const -{ - return m_volumeModuleAutoAdded; -} - -inline void DataSource::setVolumeModuleAutoAdded(bool b) -{ - m_volumeModuleAutoAdded = b; -} - -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/DockerExecutor.cxx b/tomviz/legacy/DockerExecutor.cxx deleted file mode 100644 index dd982959c..000000000 --- a/tomviz/legacy/DockerExecutor.cxx +++ /dev/null @@ -1,343 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "DockerExecutor.h" - -#include "DockerUtilities.h" -#include "ProgressDialog.h" -#include "Utilities.h" - -#include - -namespace tomviz { - -DockerPipelineExecutor::DockerPipelineExecutor(Pipeline* pipeline) - : ExternalPipelineExecutor(pipeline), m_statusCheckTimer(new QTimer(this)) -{ - m_statusCheckTimer->setInterval(5000); - connect(m_statusCheckTimer, &QTimer::timeout, this, - &DockerPipelineExecutor::checkContainerStatus); -} - -DockerPipelineExecutor::~DockerPipelineExecutor() = default; - -docker::DockerRunInvocation* DockerPipelineExecutor::run( - const QString& image, const QStringList& args, - const QMap& bindMounts) -{ - auto runInvocation = docker::run(image, QString(), args, bindMounts); - connect(runInvocation, &docker::DockerRunInvocation::error, this, - &DockerPipelineExecutor::error); - connect(runInvocation, &docker::DockerRunInvocation::finished, runInvocation, - [this, runInvocation](int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus) - if (exitCode) { - displayError("Docker Error", - QString("Docker run failed with: %1\n\n%2") - .arg(exitCode) - .arg(runInvocation->stdErr())); - } else { - m_containerId = runInvocation->containerId(); - // Start to monitor the status of the container - m_statusCheckTimer->start(); - // Print out stdout and stderr as it appears - followLogs(); - } - runInvocation->deleteLater(); - }); - - return runInvocation; -} - -void DockerPipelineExecutor::remove(const QString& containerId, bool force) -{ - auto removeInvocation = docker::remove(containerId, force); - connect(removeInvocation, &docker::DockerRunInvocation::error, this, - &DockerPipelineExecutor::error); - connect( - removeInvocation, &docker::DockerRunInvocation::finished, removeInvocation, - [this, removeInvocation](int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus) - if (exitCode) { - displayError("Docker Error", - QString("Docker remove failed with: %1\n\n%2") - .arg(exitCode) - .arg(removeInvocation->stdErr())); - } - removeInvocation->deleteLater(); - }); -} - -docker::DockerStopInvocation* DockerPipelineExecutor::stop( - const QString& containerId) -{ - auto stopInvocation = docker::stop(containerId, 0); - connect(stopInvocation, &docker::DockerStopInvocation::error, this, - &DockerPipelineExecutor::error); - connect( - stopInvocation, &docker::DockerStopInvocation::finished, stopInvocation, - [this, stopInvocation](int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus) - if (exitCode) { - displayError("Docker Error", - QString("Docker stop failed with: %1\n\n%2") - .arg(exitCode) - .arg(stopInvocation->stdErr())); - stopInvocation->deleteLater(); - return; - } - - PipelineSettings settings; - if (settings.dockerRemove() && !m_containerId.isEmpty()) { - // Remove the container - remove(m_containerId, true); - } - - stopInvocation->deleteLater(); - }); - - return stopInvocation; -} - -Pipeline::Future* DockerPipelineExecutor::execute(vtkDataObject* data, - QList operators, - int start, int end) -{ - - auto future = ExternalPipelineExecutor::execute(data, operators, start, end); - - // We are now ready to run the pipeline - auto args = executorArgs(start); - QMap bindMounts; - bindMounts[m_temporaryDir->path()] = CONTAINER_MOUNT; - - PipelineSettings settings; - QString image = settings.dockerImage(); - auto startContainer = [this, image, args, bindMounts]() { - auto msg = QString("Starting docker container."); - auto progress = new ProgressDialog("Docker run", msg, tomviz::mainWidget()); - progress->show(); - auto runInvocation = run(image, args, bindMounts); - connect(runInvocation, &docker::DockerPullInvocation::finished, - runInvocation, - [progress](int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitCode) - Q_UNUSED(exitStatus) - progress->hide(); - progress->deleteLater(); - }); - }; - - // Pull the latest version of the image, if haven't already - if (settings.dockerPull() && m_pullImage) { - auto msg = QString("Pulling docker image: %1").arg(image); - auto progress = - new ProgressDialog("Docker Pull", msg, tomviz::mainWidget()); - progress->show(); - m_pullImage = false; - auto pullInvocation = docker::pull(image); - connect(pullInvocation, &docker::DockerPullInvocation::error, this, - &DockerPipelineExecutor::error); - connect(pullInvocation, &docker::DockerPullInvocation::finished, - pullInvocation, - [this, image, args, bindMounts, pullInvocation, progress, - startContainer](int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus) - progress->hide(); - progress->deleteLater(); - if (exitCode) { - displayError("Docker Error", - QString("Docker pull failed with: %1\n\n%2") - .arg(exitCode) - .arg(pullInvocation->stdErr())); - } else { - startContainer(); - } - pullInvocation->deleteLater(); - }); - } else { - startContainer(); - } - - return future; -} - -void DockerPipelineExecutor::cancel(std::function canceled) -{ - // Call reset to stop progress updates, status checking and clean - // update state. - reset(); - auto stopInvocation = stop(m_containerId); - connect(stopInvocation, &docker::DockerStopInvocation::finished, - stopInvocation, - [canceled](int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus) - if (!exitCode) { - canceled(); - } - }); -} - -bool DockerPipelineExecutor::cancel(Operator* op) -{ - Q_UNUSED(op); - - if (m_containerId.isEmpty()) { - return false; - } - - // Cancel status checks - m_statusCheckTimer->stop(); - - // Stop the progress reader - m_progressReader->stop(); - - // Simply stop the container. - stop(m_containerId); - - // Clean update state. - reset(); - - // We can't cancel an individual operator so we return false, so the caller - // knows - return false; -} - -bool DockerPipelineExecutor::isRunning() -{ - return !m_containerId.isEmpty(); -} - -void DockerPipelineExecutor::error(QProcess::ProcessError error) -{ - auto invocation = qobject_cast(sender()); - displayError("Execution Error", - QString("An error occurred executing '%1', '%2'") - .arg(invocation->commandLine()) - .arg(error)); -} - -void DockerPipelineExecutor::containerError(int containerExitCode) -{ - auto logsInvocation = docker::logs(m_containerId); - connect(logsInvocation, &docker::DockerRunInvocation::error, this, - &DockerPipelineExecutor::error); - connect(logsInvocation, &docker::DockerRunInvocation::finished, - logsInvocation, [this, logsInvocation, containerExitCode]( - int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus) - if (exitCode) { - displayError("Docker Error", - QString("Docker logs failed with: %1\n\n%2") - .arg(exitCode) - .arg(logsInvocation->stdErr())); - logsInvocation->deleteLater(); - return; - } else { - auto logs = logsInvocation->logs(); - displayError( - "Pipeline Error", - QString("Docker container exited with non-zero exit code: %1." - "\n\nSee message logs for Docker logs.") - .arg(containerExitCode)); - qCritical() << logs; - } - logsInvocation->deleteLater(); - PipelineSettings settings; - if (settings.dockerRemove() && !m_containerId.isEmpty()) { - remove(m_containerId); - } - }); -} - -void DockerPipelineExecutor::checkContainerStatus() -{ - auto inspectInvocation = docker::inspect(m_containerId); - connect(inspectInvocation, &docker::DockerInspectInvocation::error, this, - &DockerPipelineExecutor::error); - connect(inspectInvocation, &docker::DockerInspectInvocation::finished, - inspectInvocation, [this, inspectInvocation]( - int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus) - if (exitCode) { - displayError("Docker Error", - QString("Docker inspect failed with: %1\n\n%2") - .arg(exitCode) - .arg(inspectInvocation->stdErr())); - } else { - // Check we haven't exited with an error. - auto status = inspectInvocation->status(); - if (status == "exited") { - if (inspectInvocation->exitCode()) { - containerError(inspectInvocation->exitCode()); - } - // Cancel the status checks we are done. - m_statusCheckTimer->stop(); - } - } - inspectInvocation->deleteLater(); - }); -} - -void DockerPipelineExecutor::pipelineStarted() -{ - qDebug("Pipeline started in docker container!"); -} - -void DockerPipelineExecutor::reset() -{ - // Cancel status checks - m_statusCheckTimer->stop(); - - ExternalPipelineExecutor::reset(); - - PipelineSettings settings; - if (settings.dockerRemove() && !m_containerId.isEmpty()) { - // Remove the container - remove(m_containerId, true); - } - - m_containerId = QString(); -} - -QString DockerPipelineExecutor::executorWorkingDir() -{ - return CONTAINER_MOUNT; -} - -void DockerPipelineExecutor::followLogs() -{ - if (m_containerId.isEmpty()) { - // Nothing to do - return; - } - - auto logsInvocation = docker::logs(m_containerId, true); - connect(logsInvocation, &docker::DockerLogsInvocation::error, this, - &DockerPipelineExecutor::error); - connect( - logsInvocation, &docker::DockerLogsInvocation::finished, logsInvocation, - [this, logsInvocation](int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitStatus) - if (exitCode) { - displayError("Docker Error", - QString("Docker logs failed with: %1\n\n%2") - .arg(exitCode) - .arg(logsInvocation->stdErr())); - } - logsInvocation->deleteLater(); - }); - - auto printFunc = [](QString s) { - // Since qDebug() will already print a newline, get rid of the - // newline from the string if it is there. - auto regexp = QRegularExpression("(?:\r\n|\n)$"); - s = s.remove(regexp); - qDebug().noquote() << s; - }; - - connect(logsInvocation, &docker::DockerInvocation::stdOutReceived, printFunc); - connect(logsInvocation, &docker::DockerInvocation::stdErrReceived, printFunc); -} - -} // namespace tomviz diff --git a/tomviz/legacy/DockerExecutor.h b/tomviz/legacy/DockerExecutor.h deleted file mode 100644 index c5f3949cc..000000000 --- a/tomviz/legacy/DockerExecutor.h +++ /dev/null @@ -1,65 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizDockerExecutor_h -#define tomvizDockerExecutor_h - -#include - -#include "PipelineExecutor.h" - -namespace tomviz { -class DataSource; -class Operator; -class Pipeline; - -namespace docker { -class DockerStopInvocation; -class DockerRunInvocation; -} - - -/// -/// Executor that orchestrates the execution of pipelines in Docker containers, -/// providing a pristine container-based pipeline environment. -/// -class DockerPipelineExecutor : public ExternalPipelineExecutor -{ - Q_OBJECT - -public: - DockerPipelineExecutor(Pipeline* pipeline); - ~DockerPipelineExecutor(); - Pipeline::Future* execute(vtkDataObject* data, QList operators, - int start = 0, int end = -1) override; - void cancel(std::function canceled) override; - bool cancel(Operator* op) override; - bool isRunning() override; - -protected: - QString executorWorkingDir() override; - void pipelineStarted() override; - void reset() override; - -private slots: - docker::DockerRunInvocation* run(const QString& image, - const QStringList& args, - const QMap& bindMounts); - void remove(const QString& containerId, bool force = false); - docker::DockerStopInvocation* stop(const QString& containerId); - void containerError(int exitCode); - void error(QProcess::ProcessError error); - -private: - bool m_pullImage = true; - QString m_containerId; - - QTimer* m_statusCheckTimer; - - void checkContainerStatus(); - void followLogs(); -}; - -} // namespace tomviz - -#endif // tomvizDockerExecutor_h diff --git a/tomviz/legacy/DockerUtilities.cxx b/tomviz/legacy/DockerUtilities.cxx deleted file mode 100644 index 7368a685c..000000000 --- a/tomviz/legacy/DockerUtilities.cxx +++ /dev/null @@ -1,281 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "DockerUtilities.h" -//#include -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -namespace docker { - -DockerInvocation::DockerInvocation(const QString& command, - const QStringList& args) - : m_command(command), m_args(args) -{ -} - -QString DockerInvocation::commandLine() const -{ - return QString("docker %1 %2").arg(m_command).arg(m_args.join(" ")); -} - -DockerInvocation* DockerInvocation::run() -{ - m_process = new QProcess(this); - - connect(m_process, &QProcess::readyReadStandardOutput, this, - &DockerInvocation::onStdOutReceived); - connect(m_process, &QProcess::readyReadStandardError, this, - &DockerInvocation::onStdErrReceived); - - // We emit the signals rather than connect signal to signal so we get the - // right sender. - connect(m_process, &QProcess::errorOccurred, - [this](QProcess::ProcessError error) { emit this->error(error); }); - - connect(m_process, static_cast( - &QProcess::finished), - [this](int exitCode, QProcess::ExitStatus exitStatus) { - emit this->finished(exitCode, exitStatus); - }); - m_process->setProgram("docker"); - QStringList dockerArgs; - dockerArgs.append(m_command); - dockerArgs.append(m_args); - - m_process->setArguments(dockerArgs); - - // Start in the next event loop so signals can be wired up. - QTimer::singleShot(0, [this]() { m_process->start(); }); - - return this; -} - -void DockerInvocation::onStdOutReceived() -{ - QString s = m_process->readAllStandardOutput(); - m_stdOutReceived += s; - - emit stdOutReceived(s); -} - -void DockerInvocation::onStdErrReceived() -{ - QString s = m_process->readAllStandardError(); - m_stdErrReceived += s; - - emit stdErrReceived(s); -} - -void DockerInvocation::init(const QString& command, const QStringList& args) -{ - m_command = command; - m_args = args; -} - -DockerRunInvocation::DockerRunInvocation( - const QString& image, const QString& entryPoint, - const QStringList& containerArgs, const QMap& bindMounts) - : m_image(image), m_entryPoint(entryPoint), m_containerArgs(containerArgs), - m_bindMounts(bindMounts) -{ - - QStringList args; - args.append("-d"); - if (!entryPoint.isNull()) { - args.append("--entrypoint"); - args.append(entryPoint); - } - - if (!bindMounts.isEmpty()) { - for (QMap::const_iterator iter = bindMounts.begin(); - iter != bindMounts.end(); ++iter) { - args.append("-v"); - args.append(QString("%1:%2").arg(iter.key()).arg(iter.value())); - } - } - - args.append(image); - args.append(containerArgs); - - init("run", args); -} - -DockerRunInvocation* DockerRunInvocation::run() -{ - return qobject_cast(DockerInvocation::run()); -} - -QString DockerRunInvocation::containerId() -{ - return stdOut().trimmed(); -} - -DockerPullInvocation::DockerPullInvocation(const QString& image) - : m_image(image) -{ - QStringList args; - args.append(image); - - init("pull", args); -} - -DockerPullInvocation* DockerPullInvocation::run() -{ - return qobject_cast(DockerInvocation::run()); -} - -DockerLogsInvocation::DockerLogsInvocation(const QString& containerId, - bool follow) - : m_containerId(containerId) -{ - QStringList args; - args.append(containerId); - - if (follow) - args.append("-f"); - - init("logs", args); -} - -DockerLogsInvocation* DockerLogsInvocation::run() -{ - return qobject_cast(DockerInvocation::run()); -} - -QString DockerLogsInvocation::logs() -{ - return stdErr() + stdOut(); -} - -DockerStopInvocation::DockerStopInvocation(const QString& containerId, - int timeout = 10) - : m_containerId(containerId), m_timeout(timeout) -{ - QStringList args; - args.append(containerId); - args.append("-t"); - args.append(QString::number(timeout)); - - init("stop", args); -} - -DockerStopInvocation* DockerStopInvocation::run() -{ - return qobject_cast(DockerInvocation::run()); -} - -DockerInspectInvocation::DockerInspectInvocation(const QString& containerId) - : m_containerId(containerId) -{ - QStringList args; - args.append(containerId); - - init("inspect", args); -} - -QString DockerInspectInvocation::status() -{ - if (m_status.isNull()) { - auto document = QJsonDocument::fromJson(stdOut().toLatin1()); - auto inspect = document.array()[0].toObject(); - auto state = inspect["State"].toObject(); - auto status = state["Status"]; - - if (status.isString()) { - m_status = status.toString(); - } - } - - return m_status; -} - -int DockerInspectInvocation::exitCode() -{ - if (m_exitCode < 0) { - auto document = QJsonDocument::fromJson(stdOut().toLatin1()); - auto inspect = document.array()[0].toObject(); - auto state = inspect["State"].toObject(); - auto exitCode = state["ExitCode"]; - - if (exitCode.isDouble()) { - m_exitCode = exitCode.toInt(); - } - } - - return m_exitCode; -} - -DockerInspectInvocation* DockerInspectInvocation::run() -{ - return qobject_cast(DockerInvocation::run()); -} - -DockerRemoveInvocation::DockerRemoveInvocation(const QString& containerId, - bool force) - : m_containerId(containerId) -{ - QStringList args; - args.append(containerId); - if (force) { - args.append("-f"); - } - - init("rm", args); -} - -DockerRemoveInvocation* DockerRemoveInvocation::run() -{ - return qobject_cast(DockerInvocation::run()); -} - -DockerRunInvocation* run(const QString& image, const QString& entryPoint, - const QStringList& containerArgs, - const QMap& bindMounts) -{ - auto invocation = - new DockerRunInvocation(image, entryPoint, containerArgs, bindMounts); - - return invocation->run(); -} - -DockerPullInvocation* pull(const QString& image) -{ - auto invocation = new DockerPullInvocation(image); - - return invocation->run(); -} - -DockerStopInvocation* stop(const QString& containerId, int wait) -{ - auto invocation = new DockerStopInvocation(containerId, wait); - return invocation->run(); -} - -DockerRemoveInvocation* remove(const QString& containerId, bool force) -{ - auto invocation = new DockerRemoveInvocation(containerId, force); - return invocation->run(); -} - -DockerLogsInvocation* logs(const QString& containerId, bool follow) -{ - auto invocation = new DockerLogsInvocation(containerId, follow); - return invocation->run(); -} - -DockerInspectInvocation* inspect(const QString& containerId) -{ - auto invocation = new DockerInspectInvocation(containerId); - return invocation->run(); -} -} - -} // namespace tomviz diff --git a/tomviz/legacy/DockerUtilities.h b/tomviz/legacy/DockerUtilities.h deleted file mode 100644 index 7238da9c9..000000000 --- a/tomviz/legacy/DockerUtilities.h +++ /dev/null @@ -1,154 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizDockerUtilties_h -#define tomvizDockerUtilties_h - -// Collection of utilities for interacting with docker. - -#include -#include -#include -#include -#include - -namespace tomviz { - -namespace docker { - -class DockerInvocation : public QObject -{ - Q_OBJECT - -public: - DockerInvocation(){}; - DockerInvocation(const QString& command, const QStringList& args); - - QString commandLine() const; - DockerInvocation* run(); - QString stdOut() { return m_stdOutReceived; } - QString stdErr() { return m_stdErrReceived; } - -signals: - void error(QProcess::ProcessError error); - void finished(int exitCode, QProcess::ExitStatus exitStatus); - - void stdOutReceived(const QString& s); - void stdErrReceived(const QString& s); - -protected: - void init(const QString& command, const QStringList& args); - -private slots: - void onStdOutReceived(); - void onStdErrReceived(); - -private: - QString m_command; - QStringList m_args; - QString m_stdOutReceived; - QString m_stdErrReceived; - QProcess* m_process; -}; - -class DockerRunInvocation : public DockerInvocation -{ - Q_OBJECT -public: - DockerRunInvocation(const QString& image, const QString& entryPoint, - const QStringList& containerArgs, - const QMap& bindMounts); - - DockerRunInvocation* run(); - QString containerId(); - -private: - QString m_image; - QString m_entryPoint; - QStringList m_containerArgs; - QMap m_bindMounts; -}; - -class DockerPullInvocation : public DockerInvocation -{ - Q_OBJECT -public: - DockerPullInvocation(const QString& image); - - DockerPullInvocation* run(); - -private: - QString m_image; -}; - -class DockerLogsInvocation : public DockerInvocation -{ - Q_OBJECT -public: - DockerLogsInvocation(const QString& containerId, bool follow = false); - - DockerLogsInvocation* run(); - QString logs(); - -private: - QString m_containerId; - QString m_logs; -}; - -class DockerStopInvocation : public DockerInvocation -{ - Q_OBJECT -public: - DockerStopInvocation(const QString& containerId, int timeout); - - DockerStopInvocation* run(); - -private: - QString m_containerId; - int m_timeout; -}; - -class DockerInspectInvocation : public DockerInvocation -{ - Q_OBJECT -public: - DockerInspectInvocation(const QString& containerId); - - DockerInspectInvocation* run(); - QString status(); - int exitCode(); - -private: - QString m_containerId; - int m_inspectResult; - QString m_status; - int m_exitCode = -1; -}; - -class DockerRemoveInvocation : public DockerInvocation -{ - Q_OBJECT -public: - DockerRemoveInvocation(const QString& containerId, bool force = false); - - DockerRemoveInvocation* run(); - -private: - QString m_containerId; -}; - -DockerRunInvocation* run( - const QString& image, const QString& entryPoint = QString(), - const QStringList& containerArgs = QStringList(), - const QMap& bindMounts = QMap()); - -DockerPullInvocation* pull(const QString& image); -DockerStopInvocation* stop(const QString& containerId, int wait = 10); -DockerRemoveInvocation* remove(const QString& containerId, bool force = false); -DockerLogsInvocation* logs(const QString& containerId, bool follow = false); -DockerInspectInvocation* inspect(const QString& containerId); - -} // namespace docker -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/ExternalPythonExecutor.cxx b/tomviz/legacy/ExternalPythonExecutor.cxx deleted file mode 100644 index 04aa31777..000000000 --- a/tomviz/legacy/ExternalPythonExecutor.cxx +++ /dev/null @@ -1,210 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include - -#include "ExternalPythonExecutor.h" -#include "DataSource.h" -#include "EmdFormat.h" -#include "operators/Operator.h" -#include "operators/OperatorPython.h" -#include "Pipeline.h" -#include "PipelineWorker.h" - -namespace tomviz { - -ExternalPythonExecutor::ExternalPythonExecutor(Pipeline* pipeline) - : ExternalPipelineExecutor(pipeline) -{ -} - -ExternalPythonExecutor::~ExternalPythonExecutor() = default; - -Pipeline::Future* ExternalPythonExecutor::execute(vtkDataObject* data, - QList operators, - int start, int end) -{ - m_receivedStdOut.clear(); - m_receivedStdErr.clear(); - - auto future = ExternalPipelineExecutor::execute(data, operators, start, end); - - // We are now ready to run the pipeline - QStringList args = executorArgs(start); - - PipelineSettings settings; - auto pythonExecutable = settings.externalPythonExecutablePath(); - - // Find the tomviz-pipeline executable - auto pythonExecutableFile = QFileInfo(pythonExecutable); - - if (!pythonExecutableFile.exists()) { - displayError("External Python Error", - QString("The external python executable doesn't exist: %1\n") - .arg(pythonExecutable)); - - return Pipeline::emptyFuture(); - } - - auto baseDir = pythonExecutableFile.dir(); - auto tomvizPipelineExecutable = - QFileInfo(baseDir.filePath("tomviz-pipeline")); - if (!tomvizPipelineExecutable.exists()) { - displayError( - "External Python Error", - QString("Unable to find tomviz-pipeline executable, please ensure " - "tomviz package has been installed in python environment." - "Click the Help button for more details on setting up your " - "Python environment.")); - - return Pipeline::emptyFuture(); - } - - m_process.reset(new QProcess(this)); - - connect(m_process.data(), &QProcess::readyReadStandardOutput, this, - &ExternalPythonExecutor::onStdOutReceived); - connect(m_process.data(), &QProcess::readyReadStandardError, this, - &ExternalPythonExecutor::onStdErrReceived); - - connect(m_process.data(), &QProcess::errorOccurred, this, - &ExternalPythonExecutor::error); - connect( - m_process.data(), - QOverload::of(&QProcess::finished), - [this](int exitCode, QProcess::ExitStatus exitStatus) { - if (exitStatus == QProcess::CrashExit) { - displayError("External Python Error", - QString("The external python process crash: %1\n\n " - "stderr:\n%2 \n\n stdout:\n%3 \n") - .arg(commandLine(this->m_process.data())) - .arg(m_receivedStdErr) - .arg(m_receivedStdOut)); - - } else if (exitCode != 0) { - displayError( - "External Python Error", - QString("The external python returned a non-zero exit code: %1\n\n " - "command: %2 \n\n stderr:\n%3 \n\n stdout:\n%4 \n") - .arg(exitCode) - .arg(commandLine(this->m_process.data())) - .arg(m_receivedStdErr) - .arg(m_receivedStdOut)); - } - }); - - // We have to get the process environment and unset TOMVIZ_APPLICATION and - // set that as the process environment for the process, otherwise the - // python package will think its running in the application. - auto processEnv = QProcessEnvironment::systemEnvironment(); - processEnv.remove("TOMVIZ_APPLICATION"); - // Remove vars related to python environment - processEnv.remove("PYTHONHOME"); - processEnv.remove("PYTHONPATH"); - - // Normally, python uses a buffer for stdout and stderr. However, - // we want everything printed from python to immediately appear in the - // tomviz messages box (rather than waiting until the program is finished). - // Unbuffer the python output so the messages get printed immediately. - processEnv.insert("PYTHONUNBUFFERED", "ON"); - - m_process->setProcessEnvironment(processEnv); - - m_process->start(tomvizPipelineExecutable.filePath(), args); - - return future; -} - -void ExternalPythonExecutor::cancel(std::function canceled) -{ - - reset(); - - m_process->kill(); - - canceled(); -} - -bool ExternalPythonExecutor::cancel(Operator* op) -{ - Q_UNUSED(op); - - // Stop the progress reader - m_progressReader->stop(); - - m_process->kill(); - - // Clean update state. - reset(); - - // We can't cancel an individual operator so we return false, so the caller - // knows - return false; -} - -bool ExternalPythonExecutor::isRunning() -{ - return !m_process.isNull() && m_process->state() != QProcess::NotRunning; -} - -void ExternalPythonExecutor::error(QProcess::ProcessError error) -{ - auto process = qobject_cast(sender()); - auto invocation = commandLine(process); - - displayError("Execution Error", - QString("An error occurred executing '%1', '%2'") - .arg(invocation) - .arg(error)); -} - -void ExternalPythonExecutor::pipelineStarted() -{ - qDebug("Pipeline started in external python!"); -} - -void ExternalPythonExecutor::reset() -{ - ExternalPipelineExecutor::reset(); - - m_process->waitForFinished(); - m_process.reset(); -} - -QString ExternalPythonExecutor::executorWorkingDir() -{ - return workingDir(); -} - -QString ExternalPythonExecutor::commandLine(QProcess* process) -{ - return QString("%1 %2") - .arg(process->program()) - .arg(process->arguments().join(" ")); -} - -void ExternalPythonExecutor::onStdOutReceived() -{ - QString s = m_process->readAllStandardOutput(); - m_receivedStdOut += s; - - // Since qDebug() will already print a newline, get rid of the - // newline from the string if it is there. - auto regexp = QRegularExpression("(?:\r\n|\n)$"); - s = s.remove(regexp); - qDebug().noquote() << s; -} - -void ExternalPythonExecutor::onStdErrReceived() -{ - QString s = m_process->readAllStandardError(); - m_receivedStdErr += s; - - // Since qDebug() will already print a newline, get rid of the - // newline from the string if it is there. - auto regexp = QRegularExpression("(?:\r\n|\n)$"); - s = s.remove(regexp); - qDebug().noquote() << s; -} - -} // namespace tomviz diff --git a/tomviz/legacy/ExternalPythonExecutor.h b/tomviz/legacy/ExternalPythonExecutor.h deleted file mode 100644 index ee9f83ebf..000000000 --- a/tomviz/legacy/ExternalPythonExecutor.h +++ /dev/null @@ -1,56 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizExternalPythonExecutor_h -#define tomvizExternalPythonExecutor_h - -#include "PipelineExecutor.h" - -#include -#include -#include - -namespace tomviz { - -class DataSource; -class Operator; -class Pipeline; - -/// -/// Executor that executes the pipeline in a specified external Python -/// environment in order to enable GPU acceleration, custom packages, etc. -/// -class ExternalPythonExecutor : public ExternalPipelineExecutor -{ - Q_OBJECT - -public: - ExternalPythonExecutor(Pipeline* pipeline); - ~ExternalPythonExecutor(); - Pipeline::Future* execute(vtkDataObject* data, QList operators, - int start = 0, int end = -1) override; - void cancel(std::function canceled) override; - bool cancel(Operator* op) override; - bool isRunning() override; - -protected: - QString executorWorkingDir() override; - -private slots: - void onStdOutReceived(); - void onStdErrReceived(); - void error(QProcess::ProcessError error); - -private: - void pipelineStarted() override; - void reset() override; - QString commandLine(QProcess* process); - - QScopedPointer m_process; - QString m_receivedStdOut; - QString m_receivedStdErr; -}; - -} // namespace tomviz - -#endif // tomvizExternalPythonExecutor_h diff --git a/tomviz/legacy/Pipeline.cxx b/tomviz/legacy/Pipeline.cxx deleted file mode 100644 index 1bd806507..000000000 --- a/tomviz/legacy/Pipeline.cxx +++ /dev/null @@ -1,699 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "Pipeline.h" - -#include "ActiveObjects.h" -#include "DataSource.h" -#include "DockerExecutor.h" -#include "DockerUtilities.h" -#include "EmdFormat.h" -#include "ExternalPythonExecutor.h" -#include "modules/ModuleManager.h" -#include "operators/Operator.h" -#include "ThreadedExecutor.h" -#include "Utilities.h" - -#include - -#include -#include -#include -#include -#include - -namespace tomviz { - -PipelineSettings::PipelineSettings() -{ - m_settings = pqApplicationCore::instance()->settings(); -} - -void PipelineSettings::setExecutionMode(Pipeline::ExecutionMode executor) -{ - auto metaEnum = QMetaEnum::fromType(); - setExecutionMode(QString(metaEnum.valueToKey(executor))); -} - -void PipelineSettings::setExecutionMode(const QString& executor) -{ - m_settings->setValue("pipeline/mode", executor); -} - -Pipeline::ExecutionMode PipelineSettings::executionMode() -{ - if (!m_settings->contains("pipeline/mode")) { - return Pipeline::ExecutionMode::Threaded; - } - auto metaEnum = QMetaEnum::fromType(); - - return static_cast(metaEnum.keyToValue( - m_settings->value("pipeline/mode").toString().toLatin1().data())); -} - -QString PipelineSettings::dockerImage() -{ - return m_settings->value("pipeline/docker.image").toString(); -} - -bool PipelineSettings::dockerPull() -{ - return m_settings->value("pipeline/docker.pull", true).toBool(); -} - -bool PipelineSettings::dockerRemove() -{ - return m_settings->value("pipeline/docker.remove", true).toBool(); -} - -QString PipelineSettings::externalPythonExecutablePath() -{ - return m_settings->value("pipeline/external.executable").toString(); -} - -void PipelineSettings::setDockerImage(const QString& image) -{ - m_settings->setValue("pipeline/docker.image", image); -} - -void PipelineSettings::setDockerPull(bool pull) -{ - m_settings->setValue("pipeline/docker.pull", pull); -} - -void PipelineSettings::setDockerRemove(bool remove) -{ - m_settings->setValue("pipeline/docker.remove", remove); -} - -void PipelineSettings::setExternalPythonExecutablePath( - const QString& executable) -{ - m_settings->setValue("pipeline/external.executable", executable); -} - -Pipeline::Pipeline(DataSource* dataSource, QObject* parent) : QObject(parent) -{ - m_data = dataSource; - m_data->setParent(this); - - addDataSource(dataSource); - - PipelineSettings settings; - auto executor = settings.executionMode(); - setExecutionMode(executor); -} - -Pipeline::~Pipeline() = default; - -Pipeline::Future* Pipeline::execute() -{ - return execute(m_data); -} - -class PipelineFutureInternal : public Pipeline::Future -{ - Q_OBJECT -public: - PipelineFutureInternal(Pipeline* pipeline, QList operators, - Pipeline::Future* future, bool recurse = true, - QObject* parent = nullptr); - -private: - Pipeline* m_pipeline; - QScopedPointer m_currentBranchFuture; - bool m_recurse = false; - - void setCurrentFuture(Pipeline::Future* future); -}; - -PipelineFutureInternal::PipelineFutureInternal(Pipeline* pipeline, - QList operators, - Pipeline::Future* future, - bool recurse, QObject* parent) - : Pipeline::Future(operators, parent), m_pipeline(pipeline), - m_recurse(recurse) -{ - setCurrentFuture(future); -} - -void PipelineFutureInternal::setCurrentFuture(Pipeline::Future* future) -{ - - m_currentBranchFuture.reset(future); - - connect(future, &Pipeline::Future::finished, future, [future, this]() { - auto operators = future->operators(); - // operators will be empty if we are returning the original data set ( i.e - // no operators have been run. - if (operators.isEmpty()) { - setResult(future->result()); - emit finished(); - return; - } - - auto lastOp = future->operators().last(); - - // Do we have another branch to execute - if (m_recurse && lastOp->childDataSource() != nullptr && - !lastOp->childDataSource()->operators().isEmpty()) { - auto child = lastOp->childDataSource(); - auto newFuture = m_pipeline->executor()->execute(child->dataObject(), - child->operators()); - this->setCurrentFuture(newFuture); - // Ensure the pipeline has ownership of the transformed data source. - lastOp->childDataSource()->setParent(m_pipeline); - } - // The pipeline execution is finished - else { - setResult(future->result()); - emit finished(); - } - }); -} - -Pipeline::Future* Pipeline::execute(DataSource* dataSource) -{ - if (beingEdited(dataSource)) { - return emptyFuture(); - } - - auto operators = dataSource->operators(); - if (operators.size() < 1) { - return emptyFuture(); - } - - Operator* firstModifiedOperator = operators.first(); - if (!isModified(dataSource, &firstModifiedOperator)) { - return emptyFuture(); - } - - return executeRange(dataSource, firstModifiedOperator, nullptr, true); -} - -Pipeline::Future* Pipeline::execute(DataSource* ds, Operator* start) -{ - return executeRange(ds, start, nullptr, true); -} - -Pipeline::Future* Pipeline::execute(DataSource* ds, Operator* start, - Operator* end) -{ - return executeRange(ds, start, end, false); -} - -Pipeline::Future* Pipeline::executeRange(DataSource* ds, Operator* start, - Operator* end, - bool checkBreakpoints) -{ - if (paused()) { - return emptyFuture(); - } - - if (ds == nullptr) { - ds = m_data; - } - - m_operatorsDeleted = false; - - emit started(); - - auto operators = ds->operators(); - if (operators.isEmpty()) { - emit finished(); - auto future = new Pipeline::Future(); - // Delay emitting signal until next event loop - QTimer::singleShot(0, [future] { emit future->finished(); }); - return future; - } - int startIndex = 0; - if (start == nullptr) { - start = operators.first(); - } - if (start == ds->operators().last() && start->isNew()) { - // See if we have any canceled operators in the pipeline, if so we have to - // re-run the anyway pipeline. - bool haveCanceled = false; - for (auto itr = operators.begin(); *itr != start; ++itr) { - auto currentOp = *itr; - if (currentOp->isCanceled()) { - currentOp->resetState(); - haveCanceled = true; - break; - } - } - - if (!haveCanceled) { - startIndex = operators.indexOf(start); - // Use transformed data source - ds = transformedDataSource(ds); - } - } - // If start is not the first operator and we haven't already adjusted - // startIndex (e.g. when resuming from a breakpoint), start from the - // correct position using the already-transformed intermediate data. - // but can only use the already transformed intermediate data if the - // previous operator is the one that created it, otherwise operators - // could be applied multiple times to already transformed data. - else if (start != operators.first() && startIndex == 0) { - startIndex = operators.indexOf(start); - if (startIndex > 0) { - auto prevOp = operators[startIndex - 1]; - // We can only use intermediate data if: - // 1. The previous op completed (so its output is valid) - // 2. The current op hasn't run yet (Queued) - // 3. No operators after start have already finished (would mean - // mid-chain insertion/edit, not a breakpoint resume) - bool canUseIntermediateData = - prevOp->isCompleted() && start->isQueued(); - if (canUseIntermediateData) { - for (int i = startIndex + 1; i < operators.size(); ++i) { - if (operators[i]->isFinished()) { - canUseIntermediateData = false; - break; - } - } - } - if (canUseIntermediateData) { - ds = transformedDataSource(ds); - } else { - startIndex = 0; - } - } - } - - // If we have been asked to run until the new operator we can just return - // the transformed data. - if (!operators.isEmpty() && end != nullptr && end->isNew()) { - auto transformed = transformedDataSource(); - auto dataObject = vtkImageData::SafeDownCast(transformed->copyData()); - auto future = new Pipeline::Future(dataObject); - dataObject->FastDelete(); - // Delay emitting signal until next event loop - QTimer::singleShot(0, [=] { emit future->finished(); }); - - return future; - } - - auto endIndex = -1; - if (end != nullptr) { - endIndex = operators.indexOf(end); - } - - // Search for breakpoints within the actual execution range. This happens - // AFTER startIndex determination so that the search covers the real range - // (which may start earlier than the modified operator). - Operator* breakpointOp = nullptr; - if (checkBreakpoints) { - int searchEnd = (endIndex == -1) ? operators.size() : endIndex; - for (int i = startIndex; i < searchEnd; ++i) { - if (operators[i]->hasBreakpoint()) { - breakpointOp = operators[i]; - break; - } - } - if (breakpointOp) { - int bpIdx = operators.indexOf(breakpointOp); - for (int i = bpIdx; i < operators.size(); ++i) { - operators[i]->resetState(); - } - endIndex = bpIdx; - } - } - - auto branchFuture = - m_executor->execute(ds->dataObject(), operators, startIndex, endIndex); - connect(branchFuture, &Pipeline::Future::finished, this, - &Pipeline::branchFinished); - - auto pipelineFuture = new PipelineFutureInternal( - this, branchFuture->operators(), branchFuture, operators.last() == end); - connect(pipelineFuture, &Pipeline::Future::finished, this, - &Pipeline::finished); - - if (breakpointOp) { - connect(pipelineFuture, &Pipeline::Future::finished, this, - [this, breakpointOp]() { emit breakpointReached(breakpointOp); }); - } - - return pipelineFuture; -} - -void Pipeline::startedEditingOp(Operator* op) -{ - ++m_editingOperators; - op->setEditing(); -} - -void Pipeline::finishedEditingOp(Operator* op) -{ - if (op->isModified()) { - op->resetState(); - } else { - op->setComplete(); - } - - if (m_editingOperators > 0) { - --m_editingOperators; - if (m_editingOperators == 0 && !isRunning()) { - execute(op->dataSource())->deleteWhenFinished(); - } - } -} - -bool Pipeline::beingEdited(DataSource* ds) const -{ - // If any operators in the pipeline are in editing state, - // don't execute the pipeline - if (ds == nullptr) { - return false; - } - auto operators = ds->operators(); - for (auto itr = operators.begin(); itr != operators.end(); ++itr) { - auto currentOp = *itr; - if (currentOp->isEditing()) { - return true; - } - // Also check if the operators in a branch are being edited. - if (beingEdited(currentOp->childDataSource())) { - return true; - } - } - return false; -} - -bool Pipeline::isModified(DataSource* datasource, Operator** start) const -{ - // If the m_operatorsDeleted flag is tripped - // (i.e. if one or more operator were deleted since the last execution) - // we should execute the pipeline even if no operators are in a modified state - if (m_operatorsDeleted) { - return true; - } - // If no operators are in a modified state, - // there is no need to execute the pipeline - if (datasource == nullptr) { - return false; - } - auto operators = datasource->operators(); - for (auto itr = operators.begin(); itr != operators.end(); ++itr) { - auto currentOp = *itr; - if (currentOp->isModified()) { - *start = currentOp; - return true; - } - // Also check if the operators in a branch are being edited. - if (isModified(currentOp->childDataSource(), start)) { - return true; - } - } - return false; -} - -void Pipeline::branchFinished() -{ - auto future = qobject_cast(sender()); - auto operators = future->operators(); - // operators will be empty if the original data source was returned as the - // result. i.e. no operators have been run. - if (operators.isEmpty()) { - return; - } - auto start = future->operators().first()->dataSource(); - auto newData = future->result(); - // We only add the transformed child data source if the last operator - // doesn't already have an explicit child data source i.e. - // hasChildDataSource is true. - auto lastOp = start->operators().last(); - if (!lastOp->isCompleted()) { - // The DataSource's last operator hasn't completed. This can happen when - // execution stopped early (e.g. at a breakpoint). If the last actually - // executed operator completed, update the visualization with the - // intermediate result; otherwise bail out. - if (operators.isEmpty() || !operators.last()->isCompleted()) { - return; - } - } - - if (!lastOp->hasChildDataSource()) { - DataSource* newChildDataSource = nullptr; - if (lastOp->childDataSource() == nullptr) { - newChildDataSource = new DataSource("Output"); - newChildDataSource->setPersistenceState( - tomviz::DataSource::PersistenceState::Transient); - newChildDataSource->setForkable(false); - newChildDataSource->setParent(this); - lastOp->setChildDataSource(newChildDataSource); - auto rootDataSource = dataSource(); - // connect signal to flow units and spacing to child data source. - connect(rootDataSource, &DataSource::dataPropertiesChanged, - newChildDataSource, - [rootDataSource, newChildDataSource]() { - // Only flow the properties if no user modifications have been - // made. - if (!newChildDataSource->unitsModified()) { - newChildDataSource->setUnits(rootDataSource->getUnits(), - false); - double spacing[3]; - rootDataSource->getSpacing(spacing); - newChildDataSource->setSpacing(spacing, false); - } - }); - } - - // Update the type if necessary - DataSource::DataSourceType type = DataSource::hasTiltAngles(newData) - ? DataSource::TiltSeries - : DataSource::Volume; - lastOp->childDataSource()->setData(newData); - lastOp->childDataSource()->setType(type); - lastOp->childDataSource()->dataModified(); - - if (newChildDataSource != nullptr) { - emit lastOp->newChildDataSource(newChildDataSource); - // Move modules from root data source. - moveModulesDown(newChildDataSource); - } - } - else { - // If this is the only operator, make sure the modules are moved down. - if (start->operators().size() == 1) - moveModulesDown(lastOp->childDataSource()); - } -} - -void Pipeline::pause() -{ - m_paused = true; -} - -bool Pipeline::paused() const -{ - return m_paused; -} - -void Pipeline::resume() -{ - m_paused = false; -} - -void Pipeline::cancel(std::function canceled) -{ - m_executor->cancel(canceled); -} - -bool Pipeline::isRunning() -{ - return m_executor->isRunning(); -} - -DataSource* Pipeline::findTransformedDataSource(DataSource* dataSource) -{ - auto op = findTransformedDataSourceOperator(dataSource); - if (op != nullptr) { - return op->childDataSource(); - } - - return nullptr; -} - -Operator* Pipeline::findTransformedDataSourceOperator(DataSource* dataSource) -{ - if (dataSource == nullptr) { - return nullptr; - } - - auto operators = dataSource->operators(); - for (auto itr = operators.rbegin(); itr != operators.rend(); ++itr) { - auto op = *itr; - if (op->childDataSource() != nullptr) { - auto child = op->childDataSource(); - // If the child has operators we need to go deeper - if (!child->operators().isEmpty()) { - return findTransformedDataSourceOperator(child); - } - - return op; - } - } - - return nullptr; -} - -void Pipeline::addDataSource(DataSource* dataSource) -{ - connect(dataSource, &DataSource::operatorAdded, [this](Operator* op) { - execute(op->dataSource())->deleteWhenFinished(); - }); - // Wire up transformModified to execute pipeline - connect(dataSource, &DataSource::operatorAdded, [this](Operator* op) { - // Extract out source and execute all. - connect(op, &Operator::transformModified, this, - [this]() { execute()->deleteWhenFinished(); }); - - // Ensure that new child data source signals are correctly wired up. - connect(op, - static_cast( - &Operator::newChildDataSource), - [this](DataSource* ds) { addDataSource(ds); }); - - // We need to ensure we move add datasource to the end of the branch, - // but only if the new operator is the last one (appended). For mid-chain - // insertions, the child DataSource should stay where it is. - auto operators = op->dataSource()->operators(); - if (operators.size() > 1 && op == operators.last()) { - auto transformedDataSourceOp = - findTransformedDataSourceOperator(op->dataSource()); - if (transformedDataSourceOp != nullptr) { - auto transformedDataSource = transformedDataSourceOp->childDataSource(); - transformedDataSourceOp->setChildDataSource(nullptr); - op->setChildDataSource(transformedDataSource); - emit operatorAdded(op, transformedDataSource); - } else { - emit operatorAdded(op); - } - } else { - emit operatorAdded(op); - } - }); - // Wire up operatorRemoved. TODO We need to check the branch of the - // pipeline we are currently executing. - connect(dataSource, &DataSource::operatorRemoved, [this](Operator* op) { - // If an operator has been removed, there's a chance that none of the - // remaining operators are in a modified state. - // But the pipeline should still be executed to reflect changes - if (!op->isNew()) { - m_operatorsDeleted = true; - } - if (op->childDataSource() != nullptr) { - auto transformedDataSource = op->childDataSource(); - auto operators = op->dataSource()->operators(); - // We have an operator to move it to. - if (!operators.isEmpty()) { - auto newOp = operators.last(); - op->setChildDataSource(nullptr); - newOp->setChildDataSource(transformedDataSource); - emit newOp->dataSourceMoved(transformedDataSource); - } - // Clean it up - else { - transformedDataSource->removeAllOperators(); - transformedDataSource->deleteLater(); - } - } - - // If pipeline is running see if we can safely remove the operator - if (isRunning()) { - // If we can't safely cancel the execution then trigger the rerun of the - // pipeline. - if (!m_executor->cancel(op)) { - execute(op->dataSource())->deleteWhenFinished(); - } - } else { - // Trigger the pipeline to run - execute(op->dataSource())->deleteWhenFinished(); - } - }); -} - -void Pipeline::addDefaultModules(DataSource* dataSource) -{ - // Note: In the future we can pull this out into a setting. - QStringList defaultModules = { "Outline", "Slice" }; - auto view = ActiveObjects::instance().activeView(); - - if (view == nullptr || !view->IsA("vtkSMRenderViewProxy")) { - return; - } - - foreach (QString name, defaultModules) { - ModuleManager::instance().createAndAddModule(name, dataSource, view); - } - // LEGACY STUB: setActiveModule() removed from ActiveObjects - ActiveObjects::instance().setActiveNode(nullptr); - - auto pqview = tomviz::convert(view); - pqview->resetDisplay(); - pqview->render(); -} - -DataSource* Pipeline::transformedDataSource(DataSource* ds) -{ - if (ds == nullptr) { - ds = dataSource(); - } - - auto transformed = findTransformedDataSource(ds); - if (transformed != nullptr) { - return transformed; - } - - // Default to dataSource at being of pipeline - return ds; -} - -void Pipeline::setExecutionMode(ExecutionMode executor) -{ - m_executionMode = executor; - if (executor == ExecutionMode::Docker) { - m_executor.reset(new DockerPipelineExecutor(this)); - } else if (executor == ExecutionMode::Threaded) { - m_executor.reset(new ThreadPipelineExecutor(this)); - } else if (executor == ExecutionMode::ExternalPython) { - m_executor.reset(new ExternalPythonExecutor(this)); - } else { - qCritical() << "Unknown executor type: " << executor; - } -} - -void Pipeline::Future::deleteWhenFinished() -{ - connect(this, &Pipeline::Future::finished, this, [this]() { deleteLater(); }); -} - -Pipeline::Future* Pipeline::emptyFuture() -{ - auto future = new Pipeline::Future(); - // Delay emitting signal until next event loop - QTimer::singleShot(0, [future] { emit future->finished(); }); - - return future; -} - -void Pipeline::moveModulesDown(DataSource* newChildDataSource) -{ - foreach (Module* module, ModuleManager::instance().findModules( - dataSource(), nullptr)) { - // TODO: We should really copy the module properties as well. - auto newModule = ModuleManager::instance().createAndAddModule( - module->label(), newChildDataSource, module->view()); - // Copy over properties using the serialization code. - newModule->deserialize(module->serialize()); - ModuleManager::instance().removeModule(module); - } -} - -} // namespace tomviz -#include "Pipeline.moc" - - diff --git a/tomviz/legacy/Pipeline.h b/tomviz/legacy/Pipeline.h deleted file mode 100644 index af0da0a40..000000000 --- a/tomviz/legacy/Pipeline.h +++ /dev/null @@ -1,211 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPipeline_h -#define tomvizPipeline_h - -#include - -#include "PipelineWorker.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -class pqSettings; - -namespace tomviz { -class DataSource; -class Operator; -class Pipeline; -class PipelineExecutor; - -namespace docker { -class DockerStopInvocation; -class DockerRunInvocation; -} - -class Pipeline : public QObject -{ - Q_OBJECT -public: - enum ExecutionMode - { - Threaded, - Docker, - ExternalPython - }; - Q_ENUM(ExecutionMode) - - class Future; - - Pipeline(DataSource* dataSource, QObject* parent = nullptr); - ~Pipeline() override; - - // Pause the automatic execution of the pipeline - void pause(); - - // Returns true if pipeline is currently paused, false otherwise. - bool paused() const; - - // Returns true if edit dialogs of operators in the pipeline are open - bool editingOperators() const { return m_editingOperators > 0; } - - // Resume the automatic execution of the pipeline. - void resume(); - - // Cancel execution of the pipeline. canceled is a optional callback - // that will be called when the pipeline has been successfully canceled. - void cancel(std::function canceled = nullptr); - - /// Return true if the pipeline is currently being executed. - bool isRunning(); - - /// Add default modules to this pipeline. - void addDefaultModules(DataSource* dataSource); - - /// The data source a the root of the pipeline. - DataSource* dataSource() { return m_data; } - - /// Returns that transformed data source associated with a given - /// data source. If no data source is provided the pipeline's root data source - /// will be used. - DataSource* transformedDataSource(DataSource* dataSource = nullptr); - - /// Set the execution mode to use when executing the pipeline. - void setExecutionMode(ExecutionMode executor); - - ExecutionMode executionMode() { return m_executionMode; }; - PipelineExecutor* executor() { return m_executor.data(); }; - - static Future* emptyFuture(); - -public slots: - /// Execute the entire pipeline, starting at the root data source. Note the - /// returned Future instance needs to be cleaned up. deleteWhenFinished() can - /// be called to ensure its cleanup when the pipeline execution is finished. - Future* execute(); - /// Execute the pipeline, starting at the given data source. Note the - /// returned Future instance needs to be cleaned up. deleteWhenFinished() can - /// be called to ensure its cleanup when the pipeline execution is finished. - Future* execute(DataSource* dataSource); - /// Execute the pipeline, starting at the operator provided. Note the - /// returned Future instance needs to be cleaned up. deleteWhenFinished() can - /// be called to ensure its cleanup when the pipeline execution is finished. - Future* execute(DataSource* dataSource, Operator* start); - /// Execute the pipeline, starting at 'start' end just before 'end'. If end is - /// nullptr the execution will run to the end of the pipeline. Note the - /// returned Future instance needs to be cleaned up. deleteWhenFinished() can - /// be called to ensure its cleanup when the pipeline execution is finished. - Future* execute(DataSource* dataSource, Operator* start, Operator* end); - - /// The user has started/finished editing an operator - void startedEditingOp(Operator* op); - void finishedEditingOp(Operator* op); - -signals: - /// This signal is when the execution of the pipeline starts. - void started(); - - /// This signal is fired the execution of the pipeline finishes. - void finished(); - - /// This signal is fired when execution stops at a breakpoint operator. - void breakpointReached(Operator* op); - - /// This signal is fired when an operator is added. The second argument - /// is the datasource that should be moved to become its output in the - /// pipeline view (or null if there isn't one). - void operatorAdded(Operator* op, DataSource* outputDS = nullptr); - -private slots: - void branchFinished(); - -private: - DataSource* findTransformedDataSource(DataSource* dataSource); - Operator* findTransformedDataSourceOperator(DataSource* dataSource); - // Move modules down below the new data source - void moveModulesDown(DataSource* newChildDataSource); - void addDataSource(DataSource* dataSource); - bool beingEdited(DataSource* dataSource) const; - bool isModified(DataSource* dataSource, Operator** firstModified) const; - Future* executeRange(DataSource* ds, Operator* start, Operator* end, - bool checkBreakpoints); - - DataSource* m_data; - bool m_paused = false; - bool m_operatorsDeleted = false; - QScopedPointer m_executor; - ExecutionMode m_executionMode = Threaded; - int m_editingOperators = 0; -}; - -/// Return from getCopyOfImagePriorTo for caller to track async operation. -class Pipeline::Future : public QObject -{ - Q_OBJECT - -public: - friend class ThreadPipelineExecutor; - - Future(vtkImageData* result, QObject* parent = nullptr) - : QObject(parent), m_imageData(result){}; - Future(vtkImageData* result, QList operators, - QObject* parent = nullptr) - : QObject(parent), m_imageData(result), m_operators(operators){}; - Future(QList operators, QObject* parent = nullptr) - : QObject(parent), m_operators(operators){}; - Future(QObject* parent = nullptr) : QObject(parent){}; - virtual ~Future() override{}; - - vtkSmartPointer result() { return m_imageData; } - void setResult(vtkSmartPointer result) { m_imageData = result; } - void setResult(vtkImageData* result) { m_imageData = result; } - QList operators() { return m_operators; } - void deleteWhenFinished(); - -signals: - void finished(); - void canceled(); - -private: - vtkSmartPointer m_imageData; - QList m_operators; -}; - -class PipelineSettings -{ -public: - PipelineSettings(); - Pipeline::ExecutionMode executionMode(); - QString dockerImage(); - bool dockerPull(); - bool dockerRemove(); - QString externalPythonExecutablePath(); - - void setExecutionMode(Pipeline::ExecutionMode executor); - void setExecutionMode(const QString& executor); - void setDockerImage(const QString& image); - void setDockerPull(bool pull); - void setDockerRemove(bool remove); - void setExternalPythonExecutablePath(const QString& executable); - -private: - pqSettings* m_settings; -}; - -} // namespace tomviz - -#endif // tomvizPipeline_h diff --git a/tomviz/legacy/PipelineExecutor.cxx b/tomviz/legacy/PipelineExecutor.cxx deleted file mode 100644 index dd405390b..000000000 --- a/tomviz/legacy/PipelineExecutor.cxx +++ /dev/null @@ -1,539 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "Pipeline.h" - -#include "ActiveObjects.h" -#include "DataExchangeFormat.h" -#include "DataSource.h" -#include "EmdFormat.h" -#include "modules/ModuleManager.h" -#include "operators/Operator.h" -#include "operators/OperatorPython.h" -#include "Pipeline.h" -#include "PipelineExecutor.h" -#include "PipelineWorker.h" -#include "ProgressDialog.h" -#include "Utilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -Q_DECLARE_METATYPE(vtkSmartPointer); - -namespace tomviz { -PipelineExecutor::PipelineExecutor(Pipeline* pipeline) : QObject(pipeline) -{ -} - -Pipeline* PipelineExecutor::pipeline() -{ - return qobject_cast(parent()); -} - -bool PipelineExecutor::cancel(Operator* op) -{ - Q_UNUSED(op) - // Default implementation doesn't allow canceling operators during execution. - return false; -} - -class ExternalPipelineFuture : public Pipeline::Future -{ -public: - ExternalPipelineFuture(QList operators, QObject* parent = nullptr); -}; - -ExternalPipelineFuture::ExternalPipelineFuture(QList operators, - QObject* parent) - : Pipeline::Future(operators, parent) -{ -} - -const char* ExternalPipelineExecutor::ORIGINAL_FILENAME = "original"; -const char* ExternalPipelineExecutor::TRANSFORM_FILENAME = "transformed.emd"; -const char* ExternalPipelineExecutor::STATE_FILENAME = "state.tvsm"; -const char* ExternalPipelineExecutor::CONTAINER_MOUNT = "/tomviz"; -const char* ExternalPipelineExecutor::PROGRESS_PATH = "progress"; - -Pipeline::Future* ExternalPipelineExecutor::execute(vtkDataObject* data, - QList operators, - int, int end) -{ - if (end == -1) { - end = operators.size(); - } - - m_temporaryDir.reset(new QTemporaryDir()); - if (!m_temporaryDir->isValid()) { - displayError("Directory Error", "Unable to create temporary directory."); - return Pipeline::emptyFuture(); - ; - } - - QString origFileName = originalFileName(); - - // First generate a state file for this pipeline - QJsonObject state; - QJsonObject dataSource; - QJsonObject reader; - QJsonArray fileNames; - fileNames.append(QDir(executorWorkingDir()).filePath(origFileName)); - reader["fileNames"] = fileNames; - dataSource["reader"] = reader; - QJsonArray pipelineOps; - foreach (Operator* op, operators) { - pipelineOps.append(op->serialize()); - } - dataSource["operators"] = pipelineOps; - QJsonArray dataSources; - dataSources.append(dataSource); - state["dataSources"] = dataSources; - - // Now write the update state file to the temporary directory. - QFile stateFile(QDir(workingDir()).filePath(STATE_FILENAME)); - if (!stateFile.open(QIODevice::WriteOnly)) { - displayError("Write Error", "Couldn't open state file for write."); - return Pipeline::emptyFuture(); - } - stateFile.write(QJsonDocument(state).toJson()); - stateFile.close(); - - // Write data to EMD or DataExchange - auto dataFilePath = QDir(workingDir()).filePath(origFileName); - if (origFileName.endsWith("emd")) { - auto imageData = vtkImageData::SafeDownCast(data); - if (!EmdFormat::write(dataFilePath.toLatin1().data(), imageData)) { - displayError("Write Error", - QString("Unable to write data at: %1").arg(dataFilePath)); - return Pipeline::emptyFuture(); - } - } else { - DataExchangeFormat dxfFile; - if (!dxfFile.write(dataFilePath.toLatin1().data(), - pipeline()->dataSource())) { - displayError("Write Error", - QString("Unable to write data at: %1").arg(dataFilePath)); - return Pipeline::emptyFuture(); - } - } - - // Start reading progress updates - auto progressPath = QDir(workingDir()).filePath(PROGRESS_PATH); - -// On Windows and MacOS we have to use files to pass progress updates rather -// than a local socket which we can use on Linux. Looks like docker on MacOS -// may support sharing local sockets as some point, see -// https://github.com/docker/for-mac/issues/483 -#if defined(Q_OS_WIN) || defined(Q_OS_MAC) - m_progressMode = "files"; - m_progressReader.reset(new FilesProgressReader(progressPath, operators)); -#else - m_progressMode = "socket"; - m_progressReader.reset( - new LocalSocketProgressReader(progressPath, operators)); -#endif - - auto future = new ExternalPipelineFuture(operators); - m_progressReader->start(); - connect(m_progressReader.data(), &ProgressReader::operatorStarted, this, - &ExternalPipelineExecutor::operatorStarted); - connect(m_progressReader.data(), &ProgressReader::operatorFinished, this, - &ExternalPipelineExecutor::operatorFinished); - connect(m_progressReader.data(), &ProgressReader::operatorError, this, - &ExternalPipelineExecutor::operatorError); - connect(m_progressReader.data(), &ProgressReader::operatorProgressMaximum, - this, &ExternalPipelineExecutor::operatorProgressMaximum); - connect(m_progressReader.data(), &ProgressReader::operatorProgressStep, this, - &ExternalPipelineExecutor::operatorProgressStep); - connect(m_progressReader.data(), &ProgressReader::operatorProgressMessage, - this, &ExternalPipelineExecutor::operatorProgressMessage); - connect(m_progressReader.data(), &ProgressReader::operatorProgressData, this, - &ExternalPipelineExecutor::operatorProgressData); - connect(m_progressReader.data(), &ProgressReader::pipelineStarted, this, - &ExternalPipelineExecutor::pipelineStarted); - connect(m_progressReader.data(), &ProgressReader::pipelineFinished, this, - [this, future]() { - auto transformedFilePath = - QDir(workingDir()).filePath(TRANSFORM_FILENAME); - vtkSmartPointer transformedData = - vtkImageData::New(); - vtkImageData* transformedImageData = - vtkImageData::SafeDownCast(transformedData.Get()); - // Make sure we don't ask the user about subsampling - QVariantMap options = { { "askForSubsample", false } }; - if (EmdFormat::read(transformedFilePath.toLatin1().data(), - transformedImageData, options)) { - future->setResult(transformedImageData); - } else { - displayError("Read Error", - QString("Unable to load transformed data at: %1") - .arg(transformedFilePath)); - } - emit future->finished(); - transformedImageData->FastDelete(); - }); - connect(future, &Pipeline::Future::finished, this, - &ExternalPipelineExecutor::reset); - - // We need to hook up the transformCanceled signal to each of the operators, - // so we can stop the container if the user cancels any of them. - for (Operator* op : operators) { - connect(op, &Operator::transformCanceled, future, - [this]() { this->cancel(nullptr); }); - } - - return future; -} - -ExternalPipelineExecutor::~ExternalPipelineExecutor() = default; - -ExternalPipelineExecutor::ExternalPipelineExecutor(Pipeline* pipeline) - : PipelineExecutor(pipeline) -{ -} - -void ExternalPipelineExecutor::displayError(const QString& title, - const QString& msg) -{ - qCritical() << msg; - QMessageBox msgBox(tomviz::mainWidget()); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setWindowTitle(title); - msgBox.setText("An error occurred during external pipeline execution"); - msgBox.setDetailedText(msg); - msgBox.exec(); -} - -QString ExternalPipelineExecutor::workingDir() -{ - return m_temporaryDir->path(); -} - -QStringList ExternalPipelineExecutor::executorArgs(int start) -{ - auto baseDir = QDir(executorWorkingDir()); - auto stateFilePath = baseDir.filePath(STATE_FILENAME); - auto outputPath = baseDir.filePath(TRANSFORM_FILENAME); - auto progressPath = baseDir.filePath(PROGRESS_PATH); - - QStringList args; - args << "-s"; - args << stateFilePath; - args << "-i"; - args << QString::number(start); - args << "-o"; - args << outputPath; - args << "-p"; - args << m_progressMode; - args << "-u"; - args << progressPath; - - return args; -} - -void ExternalPipelineExecutor::pipelineStarted() -{ -} - -void ExternalPipelineExecutor::operatorStarted(Operator* op) -{ - op->setState(OperatorState::Running); - emit op->transformingStarted(); - - auto pythonOp = qobject_cast(op); - if (pythonOp != nullptr) { - pythonOp->createChildDataSource(); - } -} - -void ExternalPipelineExecutor::operatorFinished(Operator* op) -{ - QDir temp(m_temporaryDir->path()); - auto operatorIndex = pipeline()->dataSource()->operators().indexOf(op); - QDir operatorPath(temp.filePath(QString::number(operatorIndex))); - // See it we have any child data source updates - if (operatorPath.exists()) { - QMap> childOutput; - - // We are looking for EMD files - foreach (const QFileInfo& fileInfo, - operatorPath.entryInfoList(QDir::Files)) { - - auto name = fileInfo.baseName(); - vtkNew childData; - // Make sure we don't ask the user about subsampling - QVariantMap options = { { "askForSubsample", false } }; - if (EmdFormat::read(fileInfo.filePath().toLatin1().data(), childData, - options)) { - childOutput[name] = childData; - emit pipeline()->finished(); - } else { - displayError( - "Read Error", - QString("Unable to load child data at: %1").arg(fileInfo.filePath())); - break; - } - } - - auto pythonOp = qobject_cast(op); - Q_ASSERT(pythonOp != nullptr); - pythonOp->updateChildDataSource(childOutput); - } - - op->setState(OperatorState::Complete); - emit op->transformingDone(TransformResult::Complete); -} - -void ExternalPipelineExecutor::operatorError(Operator* op, const QString& error) -{ - op->setState(OperatorState::Error); - emit op->transformingDone(TransformResult::Error); - - qCritical() << error; -} - -void ExternalPipelineExecutor::operatorProgressMaximum(Operator* op, int max) -{ - op->setTotalProgressSteps(max); -} - -void ExternalPipelineExecutor::operatorProgressStep(Operator* op, int step) -{ - op->setProgressStep(step); -} - -void ExternalPipelineExecutor::operatorProgressMessage(Operator* op, - const QString& msg) -{ - op->setProgressMessage(msg); -} - -void ExternalPipelineExecutor::operatorProgressData( - Operator* op, vtkSmartPointer data) -{ - auto pythonOperator = qobject_cast(op); - if (pythonOperator != nullptr) { - emit pythonOperator->childDataSourceUpdated(data); - } -} - -void ExternalPipelineExecutor::reset() -{ - // Stop the progress reader - m_progressReader->stop(); - - // Clean up temp directory - m_temporaryDir.reset(nullptr); -} - -QString ExternalPipelineExecutor::originalFileName() -{ - QString ext = ".emd"; - auto* dataSource = pipeline()->dataSource(); - if (dataSource->darkData() && dataSource->whiteData()) { - // Let's write out to data exchange - ext = ".h5"; - } - - return ORIGINAL_FILENAME + ext; -} - -ProgressReader::ProgressReader(const QString& path, - const QList& operators) - : m_path(path), m_operators(operators) -{ - connect(this, &ProgressReader::progressMessage, this, - &ProgressReader::progressReady); -} - -void ProgressReader::progressReady(const QString& progressMessage) -{ - if (progressMessage.isEmpty()) { - return; - } - - auto progressDoc = QJsonDocument::fromJson(progressMessage.toLatin1()); - if (!progressDoc.isObject()) { - qCritical() - << QString("Invalid progress message '%1'").arg(QString(progressMessage)); - return; - } - auto progressObj = progressDoc.object(); - auto type = progressObj["type"].toString(); - - // Operator progress - if (progressObj.contains("operator")) { - auto opIndex = progressObj["operator"].toInt(); - auto op = m_operators[opIndex]; - - if (type == "started") { - emit operatorStarted(op); - } else if (type == "finished") { - emit operatorFinished(op); - } else if (type == "error") { - auto error = progressObj["error"].toString(); - emit operatorError(op, error); - } else if (type == "progress.maximum") { - auto value = progressObj["value"].toInt(); - emit operatorProgressMaximum(op, value); - } else if (type == "progress.step") { - auto value = progressObj["value"].toInt(); - emit operatorProgressStep(op, value); - } else if (type == "progress.message") { - auto value = progressObj["value"].toString(); - emit operatorProgressMessage(op, value); - } else if (type == "progress.data") { - auto value = progressObj["value"].toString(); - emit operatorProgressData(op, readProgressData(value)); - } else { - qCritical() << QString("Unrecognized message type: %1").arg(type); - } - } - // Overall pipeline progress - else { - if (type == "started") { - emit pipelineStarted(); - } else if (type == "finished") { - emit pipelineFinished(); - } else { - qCritical() << QString("Unrecognized message type: %1").arg(type); - } - } -} - -vtkSmartPointer ProgressReader::readProgressData( - const QString& path) -{ - auto data = vtkSmartPointer::New(); - - auto hostPath = QFileInfo(m_path).absoluteDir().filePath(path); - - // Make sure we don't ask the user about subsampling - QVariantMap options = { { "askForSubsample", false } }; - if (!EmdFormat::read(hostPath.toLatin1().data(), data, options)) { - qCritical() << QString("Unable to load progress data at: %1").arg(path); - } - - return data; -} - -FilesProgressReader::FilesProgressReader(const QString& path, - const QList& operators) - : ProgressReader(path, operators), m_pathWatcher(new QFileSystemWatcher()) -{ - QDir dir(m_path); - if (!dir.exists()) { - dir.mkpath("."); - } - - connect(m_pathWatcher.data(), &QFileSystemWatcher::directoryChanged, this, - &FilesProgressReader::checkForProgressFiles); -} - -void FilesProgressReader::start() -{ - m_pathWatcher->addPath(m_path); -} - -void FilesProgressReader::stop() -{ - m_pathWatcher->removePath(m_path); -} - -void FilesProgressReader::checkForProgressFiles() -{ - QDir progressDir(m_path); - foreach (const QString fileName, - progressDir.entryList(QDir::Files, QDir::Name)) { - auto progressFilePath = progressDir.filePath(fileName); - QFile progressFile(progressFilePath); - if (!progressFile.exists()) { - continue; - } - - if (!progressFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - qCritical() << "Unable to read progress file: " << progressFilePath; - continue; - } - - auto msg = progressFile.readLine(); - progressFile.close(); - if (!msg.isEmpty()) { - emit progressMessage(msg); - - progressFile.remove(); - } else { - QTimer::singleShot(0, this, &FilesProgressReader::checkForProgressFiles); - } - } -} - -LocalSocketProgressReader::LocalSocketProgressReader( - const QString& path, const QList& operators) - : ProgressReader(path, operators), m_localServer(new QLocalServer()) -{ - - auto server = m_localServer.data(); - connect(server, &QLocalServer::newConnection, server, [this]() { - auto connection = m_localServer->nextPendingConnection(); - m_progressConnection.reset(connection); - - if (m_progressConnection) { - connect(connection, &QIODevice::readyRead, this, - &LocalSocketProgressReader::readProgress); - connect(connection, static_cast( - &QLocalSocket::errorOccurred), - [](QLocalSocket::LocalSocketError socketError) { - if (socketError != QLocalSocket::PeerClosedError) { - qCritical() - << QString("Socket connection error: %1").arg(socketError); - } - }); - } - }); -} - -void LocalSocketProgressReader::start() -{ - m_localServer->listen(m_path); -} - -void LocalSocketProgressReader::stop() -{ - m_localServer->close(); -} - -void LocalSocketProgressReader::readProgress() -{ - auto message = m_progressConnection->readLine(); - - if (message.isEmpty()) { - return; - } - - emit progressMessage(message); - - // If we have more data schedule ourselves again. - if (m_progressConnection->bytesAvailable() > 0) { - QTimer::singleShot(0, this, &LocalSocketProgressReader::readProgress); - } -} - -} // namespace tomviz diff --git a/tomviz/legacy/PipelineExecutor.h b/tomviz/legacy/PipelineExecutor.h deleted file mode 100644 index 95fe83ff8..000000000 --- a/tomviz/legacy/PipelineExecutor.h +++ /dev/null @@ -1,163 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPipelineExecutor_h -#define tomvizPipelineExecutor_h - -#include - -#include "Pipeline.h" -#include "PipelineWorker.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace tomviz { -class DataSource; -class Operator; -class Pipeline; - -class PipelineExecutor : public QObject -{ - Q_OBJECT - -public: - PipelineExecutor(Pipeline* pipeline); - - /// Execute list of operators on a give data source. start is the index into - /// the operator list indicating where the execution should start. - virtual Pipeline::Future* execute(vtkDataObject* data, - QList operators, int start = 0, - int end = -1) = 0; - virtual void cancel(std::function canceled) = 0; - virtual bool cancel(Operator* op); - virtual bool isRunning() = 0; - -protected: - Pipeline* pipeline(); -}; - -class ProgressReader; - -class ExternalPipelineExecutor : public PipelineExecutor -{ - Q_OBJECT - -public: - ExternalPipelineExecutor(Pipeline* pipeline); - ~ExternalPipelineExecutor(); - Pipeline::Future* execute(vtkDataObject* data, QList operators, - int start = 0, int end = -1) override; - - static const char* ORIGINAL_FILENAME; - static const char* TRANSFORM_FILENAME; - static const char* STATE_FILENAME; - static const char* CONTAINER_MOUNT; - static const char* PROGRESS_PATH; - -protected: - // The working directory that tomviz will write and read data from - virtual QString workingDir(); - // The working directory that will be passed to the executor - virtual QString executorWorkingDir() = 0; - virtual void operatorStarted(Operator* op); - virtual void operatorFinished(Operator* op); - virtual void operatorError(Operator* op, const QString& error); - virtual void operatorProgressMaximum(Operator* op, int max); - virtual void operatorProgressStep(Operator* op, int step); - virtual void operatorProgressMessage(Operator* op, const QString& msg); - virtual void operatorProgressData(Operator* op, - vtkSmartPointer data); - virtual void pipelineStarted(); - virtual void reset(); - - QString originalFileName(); - void displayError(const QString& title, const QString& msg); - QStringList executorArgs(int start); - - QScopedPointer m_temporaryDir; - QScopedPointer m_progressReader; - QString m_progressMode; -}; - -class ProgressReader : public QObject -{ - Q_OBJECT - -public: - ProgressReader(const QString& path, const QList& operators); - - virtual void start() = 0; - virtual void stop() = 0; - vtkSmartPointer readProgressData(const QString& path); - -signals: - void progressMessage(const QString& msg); - void operatorStarted(Operator* op); - void operatorFinished(Operator* op); - void operatorError(Operator* op, const QString& error); - void operatorCanceled(Operator* op); - void operatorProgressMaximum(Operator* op, int max); - void operatorProgressStep(Operator* op, int step); - void operatorProgressMessage(Operator* op, const QString& msg); - void operatorProgressData(Operator* op, vtkSmartPointer data); - void pipelineStarted(); - void pipelineFinished(); - -private slots: - void progressReady(const QString& msg); - -protected: - QString m_path; - QList m_operators; -}; - -class FilesProgressReader : public ProgressReader -{ - Q_OBJECT - -public: - FilesProgressReader(const QString& path, const QList& operators); - - void start(); - void stop(); - -private: - QScopedPointer m_pathWatcher; - - void checkForProgressFiles(); -}; - -class LocalSocketProgressReader : public ProgressReader -{ - Q_OBJECT - -public: - LocalSocketProgressReader(const QString& path, - const QList& operators); - - void start(); - void stop(); - -private: - QScopedPointer m_localServer; - QScopedPointer m_progressConnection; - - void readProgress(); -}; - -} // namespace tomviz - -#endif // tomvizPipelineExecutor_h diff --git a/tomviz/legacy/PipelineManager.cxx b/tomviz/legacy/PipelineManager.cxx deleted file mode 100644 index 0386a6919..000000000 --- a/tomviz/legacy/PipelineManager.cxx +++ /dev/null @@ -1,68 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "PipelineManager.h" - -#include "ActiveObjects.h" -#include "Pipeline.h" - -namespace tomviz { - -PipelineManager::PipelineManager(QObject* p) : QObject(p) -{ - PipelineSettings settings; - m_executionMode = settings.executionMode(); - emit executionModeUpdated(m_executionMode); -} - -PipelineManager::~PipelineManager() = default; - -PipelineManager& PipelineManager::instance() -{ - static PipelineManager theInstance; - return theInstance; -} - -void PipelineManager::updateExecutionMode(Pipeline::ExecutionMode mode) -{ - m_executionMode = mode; - emit executionModeUpdated(mode); -} - -void PipelineManager::addPipeline(Pipeline* pipeline) -{ - if (pipeline && !m_pipelines.contains(pipeline)) { - pipeline->setParent(this); - m_pipelines.push_back(pipeline); - - connect(this, &PipelineManager::executionModeUpdated, pipeline, - &Pipeline::setExecutionMode); - } -} - -void PipelineManager::removePipeline(Pipeline* pipeline) -{ - if (m_pipelines.removeOne(pipeline)) { - pipeline->deleteLater(); - } -} - -void PipelineManager::removeAllPipelines() -{ - foreach (Pipeline* pipeline, m_pipelines) { - pipeline->deleteLater(); - } - m_pipelines.clear(); -} - -Pipeline::ExecutionMode PipelineManager::executionMode() -{ - return m_executionMode; -} - -QList>& PipelineManager::pipelines() -{ - return m_pipelines; -} - -} // end of namespace tomviz diff --git a/tomviz/legacy/PipelineManager.h b/tomviz/legacy/PipelineManager.h deleted file mode 100644 index a3075ee78..000000000 --- a/tomviz/legacy/PipelineManager.h +++ /dev/null @@ -1,48 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPipelineManager_h -#define tomvizPipelineManager_h - -#include -#include - -#include "Pipeline.h" - -class QDir; - -namespace tomviz { - -class Pipeline; - -class PipelineManager : public QObject -{ - Q_OBJECT - -public: - static PipelineManager& instance(); - - /// Update the execution mode the pipelines are using. - void updateExecutionMode(Pipeline::ExecutionMode mode); - Pipeline::ExecutionMode executionMode(); - QList>& pipelines(); - -public slots: - void addPipeline(Pipeline*); - void removePipeline(Pipeline*); - void removeAllPipelines(); - -signals: - void executionModeUpdated(Pipeline::ExecutionMode mode); - -private: - Q_DISABLE_COPY(PipelineManager) - PipelineManager(QObject* parent = nullptr); - ~PipelineManager() override; - - QList> m_pipelines; - Pipeline::ExecutionMode m_executionMode; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/PipelineModel.cxx b/tomviz/legacy/PipelineModel.cxx deleted file mode 100644 index 967a610ed..000000000 --- a/tomviz/legacy/PipelineModel.cxx +++ /dev/null @@ -1,1188 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "PipelineModel.h" - -#include "ActiveObjects.h" -#include "DataSource.h" -#include "modules/Module.h" -#include "modules/ModuleManager.h" -#include "MoleculeSource.h" -#include "operators/Operator.h" -#include "operators/OperatorResult.h" -#include "Pipeline.h" - -#include -#include -#include - -#include -#include -#include -#include - -namespace tomviz { - -struct PipelineModel::Item -{ - Item(DataSource* source) : tag(DATASOURCE), s(source) {} - Item(Module* module) : tag(MODULE), m(module) {} - Item(Operator* op) : tag(OPERATOR), o(op) {} - Item(OperatorResult* result) : tag(RESULT), r(result) {} - Item(MoleculeSource* source) : tag(MOLECULESOURCE), ms(source) {} - - DataSource* dataSource() { return tag == DATASOURCE ? s : nullptr; } - Module* module() { return tag == MODULE ? m : nullptr; } - Operator* op() { return tag == OPERATOR ? o : nullptr; } - OperatorResult* result() { return tag == RESULT ? r : nullptr; } - MoleculeSource* moleculeSource() - { - return tag == MOLECULESOURCE ? ms : nullptr; - } - - enum - { - DATASOURCE, - MODULE, - OPERATOR, - RESULT, - MOLECULESOURCE - } tag; - union - { - DataSource* s; - Module* m; - Operator* o; - OperatorResult* r; - MoleculeSource* ms; - }; -}; - -class PipelineModel::TreeItem -{ -public: - explicit TreeItem(const PipelineModel::Item& item, - TreeItem* parent = nullptr); - ~TreeItem(); - - TreeItem* parent() { return m_parent; } - void setParent(TreeItem* parent) { m_parent = parent; } - TreeItem* child(int index); - TreeItem* lastChild(); - int childCount() const { return m_children.count(); } - QList& children() { return m_children; } - int childIndex() const; - bool appendChild(const PipelineModel::Item& item); - bool insertChild(int position, const PipelineModel::Item& item); - bool removeChild(int position); - PipelineModel::TreeItem* detachChild(int position); - PipelineModel::TreeItem* detach(); - bool attach(PipelineModel::TreeItem* treeItem); - - bool remove(DataSource* source); - bool remove(MoleculeSource* source); - bool remove(Module* module); - bool remove(Operator* op); - - /// Recursively search entire tree for given object. - TreeItem* find(MoleculeSource* source); - TreeItem* find(Module* module); - TreeItem* find(Operator* op); - TreeItem* find(OperatorResult* result); - - void setItem(const PipelineModel::Item& item) { m_item = item; } - DataSource* dataSource() { return m_item.dataSource(); } - Module* module() { return m_item.module(); } - Operator* op() { return m_item.op(); } - MoleculeSource* moleculeSource() { return m_item.moleculeSource(); } - OperatorResult* result() { return m_item.result(); } - -private: - QList m_children; - PipelineModel::Item m_item; - TreeItem* m_parent = nullptr; -}; - -PipelineModel::TreeItem::TreeItem(const PipelineModel::Item& i, TreeItem* p) - : m_item(i), m_parent(p) -{} - -PipelineModel::TreeItem::~TreeItem() -{ - qDeleteAll(m_children); -} - -PipelineModel::TreeItem* PipelineModel::TreeItem::child(int index) -{ - return m_children.value(index); -} - -PipelineModel::TreeItem* PipelineModel::TreeItem::lastChild() -{ - if (childCount() == 0) { - return nullptr; - } - return child(childCount() - 1); -} - -int PipelineModel::TreeItem::childIndex() const -{ - if (m_parent) { - return m_parent->m_children.indexOf(const_cast(this)); - } - return 0; -} - -bool PipelineModel::TreeItem::insertChild(int pos, - const PipelineModel::Item& item) -{ - if (pos < 0 || pos > m_children.size()) { - return false; - } - TreeItem* treeItem = new TreeItem(item, this); - m_children.insert(pos, treeItem); - return true; -} - -bool PipelineModel::TreeItem::appendChild(const PipelineModel::Item& item) -{ - TreeItem* treeItem = new TreeItem(item, this); - m_children.append(treeItem); - return true; -} - -bool PipelineModel::TreeItem::removeChild(int pos) -{ - if (pos < 0 || pos >= m_children.size()) { - return false; - } - delete m_children.takeAt(pos); - return true; -} - -PipelineModel::TreeItem* PipelineModel::TreeItem::detachChild(int pos) -{ - if (pos < 0 || pos >= m_children.size()) { - return nullptr; - } - auto child = m_children.takeAt(pos); - child->setParent(nullptr); - - return child; -} - -PipelineModel::TreeItem* PipelineModel::TreeItem::detach() -{ - - return parent()->detachChild(childIndex()); -} - -bool PipelineModel::TreeItem::attach(PipelineModel::TreeItem* treeItem) -{ - m_children.append(treeItem); - treeItem->setParent(this); - return true; -} - -bool PipelineModel::TreeItem::remove(DataSource* source) -{ - if (source != dataSource()) { - return false; - } - - // This item is a DataSource item. Remove all children. - foreach (auto childItem, m_children) { - if (childItem->op()) { - auto op = childItem->op(); - // Pause the pipeline - auto pipeline = op->dataSource()->pipeline(); - if (pipeline != nullptr) { - pipeline->pause(); - } - ModuleManager::instance().removeOperator(childItem->op()); - if (pipeline != nullptr) { - // Resume but don't execute as we are removing this data source. - pipeline->resume(); - } - } else if (childItem->module()) { - ModuleManager::instance().removeModule(childItem->module()); - } - } - if (parent()) { - parent()->removeChild(childIndex()); - return true; - } - return false; -} - -bool PipelineModel::TreeItem::remove(MoleculeSource* source) -{ - if (source != moleculeSource()) { - return false; - } - // This item is a DataSource item. Remove all children. - foreach (auto childItem, m_children) { - if (childItem->module()) { - ModuleManager::instance().removeModule(childItem->module()); - } - } - return true; -} - -bool PipelineModel::TreeItem::remove(Module* module) -{ - foreach (auto childItem, m_children) { - if (childItem->module() == module) { - removeChild(childItem->childIndex()); - return true; - } - } - return false; -} - -bool PipelineModel::TreeItem::remove(Operator* o) -{ - foreach (auto childItem, m_children) { - if (childItem->op() == o) { - foreach (auto resultItem, childItem->children()) { - auto dataSource = resultItem->dataSource(); - if (ModuleManager::instance().isChild(dataSource)) { - // if the result is a child datasource, allow it to remove its - // children - resultItem->remove(dataSource); - } else { - // Remove results - childItem->removeChild(resultItem->childIndex()); - } - } - removeChild(childItem->childIndex()); - return true; - } - } - return false; -} - -PipelineModel::TreeItem* PipelineModel::TreeItem::find(Module* module) -{ - if (this->module() == module) { - return this; - } else { - foreach (auto childItem, m_children) { - auto moduleItem = childItem->find(module); - if (moduleItem) { - return moduleItem; - } - } - } - return nullptr; -} - -PipelineModel::TreeItem* PipelineModel::TreeItem::find(Operator* op) -{ - if (this->op() == op) { - return this; - } else { - foreach (auto childItem, m_children) { - auto operatorItem = childItem->find(op); - if (operatorItem) { - return operatorItem; - } - } - } - return nullptr; -} - -PipelineModel::TreeItem* PipelineModel::TreeItem::find(OperatorResult* result) -{ - if (this->result() == result) { - return this; - } else { - foreach (auto childItem, m_children) { - auto resultItem = childItem->find(result); - if (resultItem) { - return resultItem; - } - } - } - return nullptr; -} - -PipelineModel::TreeItem* PipelineModel::TreeItem::find(MoleculeSource* source) -{ - if (this->moleculeSource() == source) { - return this; - } - return nullptr; -} - -PipelineModel::PipelineModel(QObject* p) : QAbstractItemModel(p) -{ - connect(&ModuleManager::instance(), &ModuleManager::dataSourceAdded, this, - &PipelineModel::dataSourceAdded); - connect(&ModuleManager::instance(), &ModuleManager::childDataSourceAdded, - this, &PipelineModel::childDataSourceAdded); - connect(&ModuleManager::instance(), &ModuleManager::moduleAdded, this, - &PipelineModel::moduleAdded); - connect(&ModuleManager::instance(), &ModuleManager::moleculeSourceAdded, this, - &PipelineModel::moleculeSourceAdded); - - connect(&ActiveObjects::instance(), - static_cast( - &ActiveObjects::viewChanged), - this, [this]() { beginResetModel(); endResetModel(); }); - connect(&ModuleManager::instance(), &ModuleManager::dataSourceRemoved, this, - &PipelineModel::dataSourceRemoved); - connect(&ModuleManager::instance(), &ModuleManager::moleculeSourceRemoved, - this, &PipelineModel::moleculeSourceRemoved); - connect(&ModuleManager::instance(), &ModuleManager::moduleRemoved, this, - &PipelineModel::moduleRemoved); - connect(&ModuleManager::instance(), &ModuleManager::childDataSourceRemoved, - this, &PipelineModel::childDataSourceRemoved); - - connect(&ModuleManager::instance(), &ModuleManager::operatorRemoved, this, - &PipelineModel::operatorRemoved); - // Need to register this for cross thread dataChanged signal - qRegisterMetaType>("QVector"); -} - -PipelineModel::~PipelineModel() {} - -namespace { -QIcon iconForDataObject(vtkDataObject* dataObject) -{ - if (vtkTable::SafeDownCast(dataObject)) { - return QIcon(":/pqWidgets/Icons/pqSpreadsheet.svg"); - } else if (vtkUnstructuredGrid::SafeDownCast(dataObject)) { - return QIcon(":/pqWidgets/Icons/pqUnstructuredGrid16.png"); - } else if (vtkStructuredGrid::SafeDownCast(dataObject)) { - return QIcon(":/pqWidgets/Icons/pqStructuredGrid16.png"); - } else if (vtkRectilinearGrid::SafeDownCast(dataObject)) { - return QIcon(":/pqWidgets/Icons/pqRectilinearGrid16.png"); - } - - return QIcon(":/icons/pqInspect.png"); -} - -QIcon iconForOperatorState(tomviz::OperatorState state) -{ - switch (state) { - case OperatorState::Complete: - return QIcon(":/icons/check.png"); - case OperatorState::Edit: - return QIcon(":/icons/edit.png"); - case OperatorState::Queued: - return QIcon(":/icons/question.png"); - case OperatorState::Error: - return QIcon(":/icons/error_notification.png"); - case OperatorState::Canceled: - return QIcon(":/icons/red_cross.png"); - case OperatorState::Running: - // Our subclass of QItemDelegate will take care of this animated icon - break; - } - - return QIcon(); -} - -QString tooltipForOperatorState(tomviz::OperatorState state) -{ - switch (state) { - case OperatorState::Running: - return QString("Running"); - case OperatorState::Complete: - return QString("Complete"); - case OperatorState::Edit: - return QString("Editing"); - case OperatorState::Queued: - return QString("Queued"); - case OperatorState::Error: - return QString("Error"); - case OperatorState::Canceled: - return QString("Canceled"); - } - - return ""; -} -} // namespace - -QVariant PipelineModel::data(const QModelIndex& index, int role) const -{ - if (!index.isValid() || index.column() > Column::state) - return QVariant(); - - auto treeItem = this->treeItem(index); - auto dataSource = treeItem->dataSource(); - auto moleculeSource = treeItem->moleculeSource(); - auto module = treeItem->module(); - auto op = treeItem->op(); - auto result = treeItem->result(); - - // Data source - if (dataSource) { - if (index.column() == Column::label) { - switch (role) { - case Qt::DecorationRole: - return QIcon(":/icons/pqInspect.png"); - case Qt::DisplayRole: { - QString label = dataSource->label(); - if (dataSource->persistenceState() == - DataSource::PersistenceState::Modified) { - label += QString(" *"); - } - return label; - } - case Qt::ToolTipRole: - return dataSource->fileName(); - case Qt::FontRole: - if (dataSource->persistenceState() == - DataSource::PersistenceState::Modified) { - QFont font; - font.setItalic(true); - return font; - } else { - return QVariant(); - } - default: - return QVariant(); - } - } - } else if (moleculeSource) { - if (index.column() == Column::label) { - switch (role) { - case Qt::DecorationRole: - return QIcon(":/icons/gradient_opacity.png"); - case Qt::DisplayRole: - return moleculeSource->label(); - case Qt::ToolTipRole: - return moleculeSource->label(); - default: - return QVariant(); - } - } else { - return QVariant(); - } - } else if (module) { - if (index.column() == Column::label) { - switch (role) { - case Qt::DecorationRole: - return module->icon(); - case Qt::DisplayRole: - return module->label(); - case Qt::ToolTipRole: - return module->label(); - default: - return QVariant(); - } - } else if (index.column() == Column::state) { - if (role == Qt::DecorationRole) { - if (module->visibility()) { - return QIcon(":/pqWidgets/Icons/pqEyeball.svg"); - } else { - return QIcon(":/pqWidgets/Icons/pqEyeballClosed.svg"); - } - } - } - } else if (op) { - if (index.column() == Column::label) { - switch (role) { - case Qt::DecorationRole: - return op->icon(); - case Qt::DisplayRole: - return op->label(); - case Qt::ToolTipRole: - if (op->isCanceled()) { - return "Operator was canceled"; - } else { - return op->label(); - } - case Qt::FontRole: - if (op->isCanceled()) { - QFont font; - font.setStrikeOut(true); - return font; - } else { - return QVariant(); - } - default: - return QVariant(); - } - } else if (index.column() == Column::state) { - switch (role) { - case Qt::DecorationRole: - return iconForOperatorState(op->state()); - case Qt::ToolTipRole: - return tooltipForOperatorState(op->state()); - default: - return QVariant(); - } - } - } else if (result) { - if (index.column() == Column::label) { - switch (role) { - case Qt::DecorationRole: - return iconForDataObject(result->dataObject()); - case Qt::DisplayRole: - return result->label(); - case Qt::ToolTipRole: - return result->description(); - default: - return QVariant(); - } - } - } - return QVariant(); -} - -bool PipelineModel::setData(const QModelIndex& index, const QVariant& value, - int role) -{ - if (role != Qt::CheckStateRole) { - return false; - } - - auto treeItem = this->treeItem(index); - if (index.column() == Column::state && treeItem->module()) { - treeItem->module()->setVisibility(value == Qt::Checked); - emit dataChanged(index, index); - } - return true; -} - -Qt::ItemFlags PipelineModel::flags(const QModelIndex& index) const -{ - if (!index.isValid()) - return Qt::ItemFlags(); - - auto treeItem = this->treeItem(index); - auto module = treeItem->module(); - auto view = ActiveObjects::instance().activeView(); - - if (module && module->view() != view) { - return Qt::NoItemFlags; - } - return QAbstractItemModel::flags(index); -} - -QVariant PipelineModel::headerData(int, Qt::Orientation, int) const -{ - return QVariant(); -} - -QModelIndex PipelineModel::index(int row, int column, - const QModelIndex& parent) const -{ - if (!parent.isValid() && row < m_treeItems.count()) { - // Data source - return createIndex(row, column, m_treeItems[row]); - } else { - // Module or operator - auto treeItem = this->treeItem(parent); - if (treeItem && row < treeItem->childCount()) { - return createIndex(row, column, treeItem->child(row)); - } - } - - return QModelIndex(); -} - -QModelIndex PipelineModel::parent(const QModelIndex& index) const -{ - if (!index.isValid()) { - return QModelIndex(); - } - auto treeItem = this->treeItem(index); - if (!treeItem || !treeItem->parent()) { - return QModelIndex(); - } - return createIndex(treeItem->parent()->childIndex(), 0, treeItem->parent()); -} - -int PipelineModel::rowCount(const QModelIndex& parent) const -{ - if (!parent.isValid()) { - return m_treeItems.count(); - } else { - auto treeItem = this->treeItem(parent); - return treeItem->childCount(); - } -} - -int PipelineModel::columnCount(const QModelIndex&) const -{ - return 2; -} - -DataSource* PipelineModel::dataSource(const QModelIndex& idx) -{ - if (idx.isValid()) { - auto treeItem = this->treeItem(idx); - return (treeItem ? treeItem->dataSource() : nullptr); - } else { - return nullptr; - } -} - -MoleculeSource* PipelineModel::moleculeSource(const QModelIndex& idx) -{ - if (idx.isValid()) { - auto treeItem = this->treeItem(idx); - return (treeItem ? treeItem->moleculeSource() : nullptr); - } else { - return nullptr; - } -} - -Module* PipelineModel::module(const QModelIndex& idx) -{ - if (idx.isValid()) { - auto treeItem = this->treeItem(idx); - return (treeItem ? treeItem->module() : nullptr); - } else { - return nullptr; - } -} - -Operator* PipelineModel::op(const QModelIndex& idx) -{ - if (idx.isValid()) { - auto treeItem = this->treeItem(idx); - return (treeItem ? treeItem->op() : nullptr); - } else { - return nullptr; - } -} - -OperatorResult* PipelineModel::result(const QModelIndex& idx) -{ - if (idx.isValid()) { - auto treeItem = this->treeItem(idx); - return (treeItem ? treeItem->result() : nullptr); - } else { - return nullptr; - } -} - -QModelIndex PipelineModel::dataSourceIndexHelper( - PipelineModel::TreeItem* treeItem, DataSource* source) -{ - Q_ASSERT(treeItem != nullptr); - if (!source) { - return QModelIndex(); - } else if (treeItem->dataSource() == source) { - - auto row = treeItem->childIndex(); - // If this item has no parent then we are dealing with a root data source, - // childIndex() will return 0. We need to find the item in m_treeItems to - // determine the correct index. - if (treeItem->parent() == nullptr) { - row = m_treeItems.indexOf(treeItem); - Q_ASSERT(row != -1); - } - - return createIndex(row, 0, treeItem); - } else { - // Recurse on children - foreach (auto childItem, treeItem->children()) { - QModelIndex childIndex = dataSourceIndexHelper(childItem, source); - if (childIndex.isValid()) { - return childIndex; - } - } - } - return QModelIndex(); -} - -QModelIndex PipelineModel::dataSourceIndex(DataSource* source) -{ - for (int i = 0; i < m_treeItems.count(); ++i) { - QModelIndex index = dataSourceIndexHelper(m_treeItems[i], source); - if (index.isValid()) { - return index; - } - } - - return QModelIndex(); -} - -QModelIndex PipelineModel::moleculeSourceIndex(MoleculeSource* source) -{ - foreach (auto treeItem, m_treeItems) { - auto moleculeItem = treeItem->find(source); - if (moleculeItem) { - return createIndex(moleculeItem->childIndex(), 0, moleculeItem); - } - } - return QModelIndex(); -} - -QModelIndex PipelineModel::moduleIndex(Module* module) -{ - foreach (auto treeItem, m_treeItems) { - auto moduleItem = treeItem->find(module); - if (moduleItem) { - return createIndex(moduleItem->childIndex(), 0, moduleItem); - } - } - return QModelIndex(); -} - -QModelIndex PipelineModel::operatorIndexHelper( - PipelineModel::TreeItem* treeItem, Operator* op) -{ - Q_ASSERT(treeItem != nullptr); - if (!op) { - return QModelIndex(); - } else if (treeItem->op() == op) { - return createIndex(treeItem->childIndex(), 0, treeItem); - } else { - // Recurse on children - foreach (auto childItem, treeItem->children()) { - QModelIndex childIndex = operatorIndexHelper(childItem, op); - if (childIndex.isValid()) { - return childIndex; - } - } - } - return QModelIndex(); -} - -QModelIndex PipelineModel::operatorIndex(Operator* op) -{ - foreach (auto treeItem, m_treeItems) { - auto operatorItem = treeItem->find(op); - if (operatorItem) { - return createIndex(operatorItem->childIndex(), 0, operatorItem); - } - } - return QModelIndex(); -} - -QModelIndex PipelineModel::resultIndex(OperatorResult* result) -{ - foreach (auto treeItem, m_treeItems) { - auto resultItem = treeItem->find(result); - if (resultItem) { - return createIndex(resultItem->childIndex(), 0, resultItem); - } - } - return QModelIndex(); -} - -void PipelineModel::dataSourceAdded(DataSource* dataSource) -{ - auto treeItem = new PipelineModel::TreeItem(PipelineModel::Item(dataSource)); - beginInsertRows(QModelIndex(), m_treeItems.size(), m_treeItems.size()); - m_treeItems.append(treeItem); - endInsertRows(); - auto pipeline = dataSource->pipeline(); - connect(pipeline, &Pipeline::operatorAdded, this, - &PipelineModel::operatorAdded, Qt::UniqueConnection); - - // Fire signal to indicate that the transformed data source has been modified - // when the pipeline has been executed. - // TODO This should probably be move else where! - connect(pipeline, &Pipeline::finished, [this, pipeline]() { - auto transformed = pipeline->transformedDataSource(); - emit dataSourceModified(transformed); - }); - - // Refresh all operator rows when a breakpoint is reached: the breakpoint - // operator's label column shows the play icon, and operators from the - // breakpoint onwards have their state column updated (Complete → Queued). - connect(pipeline, &Pipeline::breakpointReached, [this](Operator* op) { - auto ds = op->dataSource(); - if (!ds) - return; - auto ops = ds->operators(); - int bpIdx = ops.indexOf(op); - for (int i = bpIdx; i < ops.size(); ++i) { - auto idx = operatorIndex(ops[i]); - auto stateIdx = index(idx.row(), Column::state, idx.parent()); - emit dataChanged(idx, stateIdx); - } - }); - - // When restoring a data source from a state file it will have its operators - // before we can listen to the signal above. Display those operators. - foreach (auto op, dataSource->operators()) { - operatorAdded(op); - } - emit dataSourceItemAdded(dataSource); -} - -void PipelineModel::moleculeSourceAdded(MoleculeSource* moleculeSource) -{ - auto treeItem = - new PipelineModel::TreeItem(PipelineModel::Item(moleculeSource)); - beginInsertRows(QModelIndex(), m_treeItems.size(), m_treeItems.size()); - m_treeItems.append(treeItem); - endInsertRows(); - emit moleculeSourceItemAdded(moleculeSource); -} - -void PipelineModel::moduleAdded(Module* module) -{ - Q_ASSERT(module); - auto dataSource = module->dataSource(); - auto moleculeSource = module->moleculeSource(); - auto operatorResult = module->operatorResult(); - QModelIndex index; - if (moleculeSource) { - index = moleculeSourceIndex(moleculeSource); - } else if (operatorResult) { - index = resultIndex(operatorResult); - } else if (dataSource) { - index = dataSourceIndex(dataSource); - } - if (index.isValid()) { - auto dataSourceItem = treeItem(index); - // Modules straight after the data source so append after any current - // modules. - int insertionRow = dataSourceItem->childCount(); - for (int j = 0; j < dataSourceItem->childCount(); ++j) { - if (!dataSourceItem->child(j)->module()) { - insertionRow = j; - break; - } - } - - beginInsertRows(index, insertionRow, insertionRow); - dataSourceItem->insertChild(insertionRow, PipelineModel::Item(module)); - endInsertRows(); - } - emit moduleItemAdded(module); -} - -void PipelineModel::operatorAdded(Operator* op, - DataSource* transformedDataSource) -{ - // Operators are special, they operate on all data and are shown in the - // visualization modules. So there are some moves necessary to show this. - Q_ASSERT(op); - auto dataSource = op->dataSource(); - Q_ASSERT(dataSource); - connect(op, &Operator::labelModified, this, &PipelineModel::operatorModified); - connect(op, &Operator::transformingDone, this, - &PipelineModel::operatorTransformDone); - connect( - op, - static_cast(&Operator::newChildDataSource), - this, &PipelineModel::childDataSourceAdded); - // Make sure dataChange signal is emitted when operator is complete - connect(op, &Operator::transformingDone, [this, op]() { - auto opIndex = operatorIndex(op); - auto statusIndex = index(opIndex.row(), Column::state, opIndex.parent()); - emit dataChanged(statusIndex, statusIndex); - }); - // Refresh label column when breakpoint state changes (delegate paints it) - connect(op, &Operator::breakpointChanged, [this, op]() { - auto opIndex = operatorIndex(op); - emit dataChanged(opIndex, opIndex); - }); - connect(op, &Operator::dataSourceMoved, this, - &PipelineModel::dataSourceMoved); - - auto index = dataSourceIndex(dataSource); - auto dataSourceItem = treeItem(index); - // Find the correct insertion row based on the operator's position in the - // DataSource's operator list. - int insertionRow = dataSourceItem->childCount(); - auto operators = dataSource->operators(); - int opIndex = operators.indexOf(op); - if (opIndex >= 0 && opIndex < operators.size() - 1) { - // Mid-chain insertion: find the tree item of the next operator and insert - // before it. - auto nextOp = operators[opIndex + 1]; - for (int i = 0; i < dataSourceItem->childCount(); ++i) { - if (dataSourceItem->child(i)->op() == nextOp) { - insertionRow = i; - break; - } - } - } - beginInsertRows(index, insertionRow, insertionRow); - dataSourceItem->insertChild(insertionRow, PipelineModel::Item(op)); - endInsertRows(); - - // Insert operator results in the operator tree item - auto operatorTreeItem = dataSourceItem->find(op); - auto operatorIndex = this->operatorIndex(op); - int numResults = op->numberOfResults(); - if (numResults) { - - beginInsertRows(operatorIndex, 0, numResults - 1); - for (int j = 0; j < numResults; ++j) { - OperatorResult* result = op->resultAt(j); - operatorTreeItem->appendChild(PipelineModel::Item(result)); - } - endInsertRows(); - } - - // If there is a transformed data output to move, move it to be a child - // of this operator - if (transformedDataSource) { - moveDataSourceHelper(transformedDataSource, op); - } - - emit operatorItemAdded(op); -} - -void PipelineModel::operatorRemoved(Operator* op) -{ - removeOp(op); -} - -void PipelineModel::operatorModified() -{ - auto op = qobject_cast(sender()); - Q_ASSERT(op); - - auto index = operatorIndex(op); - dataChanged(index, index); -} - -void PipelineModel::operatorTransformDone() -{ - auto op = qobject_cast(sender()); - Q_ASSERT(op); - - // Find tree item for the operator - TreeItem* operatorItem = nullptr; - foreach (auto child, m_treeItems) { - auto foundChild = child->find(op); - if (foundChild) { - operatorItem = foundChild; - break; - } - } - - if (op->hasChildDataSource() || op->childDataSource() != nullptr) { - auto childDataSource = op->childDataSource(); - if (childDataSource) { - // The Operator's child data set is null initially. We need to set it - // after the Operator has been run. We also assume that the operator item - // has a single child, the child DataSource, and it is the last child of - // the operator tree item. - auto childItem = operatorItem->lastChild(); - if (childItem) { - childItem->setItem(PipelineModel::Item(childDataSource)); - } - } - } -} - -void PipelineModel::dataSourceRemoved(DataSource* source) -{ - auto index = dataSourceIndex(source); - - if (index.isValid()) { - auto item = treeItem(index); - beginRemoveRows(parent(index), index.row(), index.row()); - item->remove(source); - m_treeItems.removeAll(item); - delete item; - endRemoveRows(); - } -} - -void PipelineModel::childDataSourceRemoved(DataSource* source) -{ - auto index = dataSourceIndex(source); - - if (index.isValid()) { - auto operatorIndex = parent(index); - auto operatorTreeItem = treeItem(operatorIndex); - auto op = operatorTreeItem->op(); - - auto item = treeItem(index); - beginRemoveRows(parent(index), index.row(), index.row()); - // Since the item has a parent (it is a child data source), calling remove() - // will delete the item object. No need to delete it again here. - item->remove(source); - m_treeItems.removeAll(item); - endRemoveRows(); - - op->setModified(); - int numResults = op->numberOfResults(); - if (numResults) { - - beginInsertRows(operatorIndex, 0, numResults - 1); - for (int j = 0; j < numResults; ++j) { - OperatorResult* result = op->resultAt(j); - operatorTreeItem->appendChild(PipelineModel::Item(result)); - } - endInsertRows(); - } - } -} - -void PipelineModel::moleculeSourceRemoved(MoleculeSource* moleculeSource) -{ - auto index = moleculeSourceIndex(moleculeSource); - - if (index.isValid()) { - auto item = treeItem(index); - beginRemoveRows(parent(index), index.row(), index.row()); - item->remove(moleculeSource); - m_treeItems.removeAll(item); - delete item; - endRemoveRows(); - } -} - -void PipelineModel::moduleRemoved(Module* module) -{ - auto index = moduleIndex(module); - - if (index.isValid()) { - beginRemoveRows(parent(index), index.row(), index.row()); - auto item = treeItem(index); - item->parent()->remove(module); - endRemoveRows(); - } -} - -bool PipelineModel::removeDataSource(DataSource* source) -{ - if (ModuleManager::instance().isChild(source)) { - childDataSourceRemoved(source); - ModuleManager::instance().removeChildDataSource(source); - } else { - dataSourceRemoved(source); - ModuleManager::instance().removeDataSource(source); - } - return true; -} - -bool PipelineModel::removeMoleculeSource(MoleculeSource* moleculeSource) -{ - moleculeSourceRemoved(moleculeSource); - ModuleManager::instance().removeMoleculeSource(moleculeSource); - return true; -} - -bool PipelineModel::removeModule(Module* module) -{ - moduleRemoved(module); - ModuleManager::instance().removeModule(module); - return true; -} - -bool PipelineModel::removeOp(Operator* o) -{ - auto index = operatorIndex(o); - if (index.isValid()) { - // If this operator has a child data source (the "transformed" output), - // move it to the last remaining operator in the tree model BEFORE calling - // removeOperator(). Previously, this move happened via the signal chain - // (operatorRemoved -> Pipeline handler -> dataSourceMoved -> - // moveDataSourceHelper -> beginMoveRows), but beginMoveRows can crash - // when iterating persistent model indexes during the signal chain. - // By doing the move explicitly here, the subsequent signal-triggered - // moveDataSourceHelper becomes a no-op (oldParent == newParent). - auto childDS = o->childDataSource(); - if (childDS) { - auto operators = o->dataSource()->operators(); - operators.removeAll(o); - if (!operators.isEmpty()) { - moveDataSourceHelper(childDS, operators.last()); - } - } - - // Re-compute the index since moveDataSourceHelper may have modified the - // tree (the operator's child count changed). - index = operatorIndex(o); - if (!index.isValid()) { - return true; - } - - o->dataSource()->removeOperator(o); - beginRemoveRows(parent(index), index.row(), index.row()); - auto item = treeItem(index); - item->parent()->remove(o); - endRemoveRows(); - - return true; - } - - return false; -} - -PipelineModel::TreeItem* PipelineModel::treeItem(const QModelIndex& index) const -{ - if (!index.isValid()) { - return nullptr; - } - - return static_cast(index.internalPointer()); -} - -void PipelineModel::childDataSourceAdded(DataSource* dataSource) -{ - if (Operator* op = qobject_cast(sender())) { - - auto index = dataSourceIndex(op->dataSource()); - auto dataSourceItem = treeItem(index); - auto operatorTreeItem = dataSourceItem->find(op); - auto numResults = op->numberOfResults(); - auto operatorIndex = this->operatorIndex(op); - - // If the last child is already a data source when we just want to set the - // item rather than inserting a new row. - auto last = operatorTreeItem->lastChild(); - if (last && last->dataSource()) { - last->setItem(PipelineModel::Item(dataSource)); - } else { - beginInsertRows(operatorIndex, numResults, numResults); - operatorTreeItem->appendChild(PipelineModel::Item(dataSource)); - endInsertRows(); - } - } - - // When restoring a data source from a state file it will have its operators - // before we can listen to the signal above. Display those operators. - foreach (auto op, dataSource->operators()) { - operatorAdded(op); - } - - emit childDataSourceItemAdded(dataSource); -} - -void PipelineModel::moveDataSourceHelper(DataSource* dataSource, - Operator* newParent) -{ - auto index = dataSourceIndex(dataSource); - if (!index.isValid()) { - return; - } - auto dataSourceItem = treeItem(index); - if (!dataSourceItem || !dataSourceItem->parent()) { - return; - } - auto oldParent = dataSourceItem->parent()->op(); - // Already under the target parent (e.g. removeOp pre-moved it). - if (oldParent == newParent) { - return; - } - auto oldParentIndex = this->operatorIndex(oldParent); - auto operatorIndex = this->operatorIndex(newParent); - auto operatorTreeItem = this->treeItem(operatorIndex); - if (!operatorTreeItem) { - return; - } - - beginMoveRows(oldParentIndex, index.row(), index.row(), operatorIndex, - operatorTreeItem->childCount()); - dataSourceItem = dataSourceItem->detach(); - operatorTreeItem->attach(dataSourceItem); - endMoveRows(); -} - -void PipelineModel::dataSourceMoved(DataSource* dataSource) -{ - if (Operator* newParent = qobject_cast(sender())) { - moveDataSourceHelper(dataSource, newParent); - } -} - -} // namespace tomviz diff --git a/tomviz/legacy/PipelineModel.h b/tomviz/legacy/PipelineModel.h deleted file mode 100644 index 7a6213029..000000000 --- a/tomviz/legacy/PipelineModel.h +++ /dev/null @@ -1,105 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPipelineModel_h -#define tomvizPipelineModel_h - -#include - -namespace { - -enum Column -{ - label, - state -}; -} - -namespace tomviz { - -class DataSource; -class MoleculeSource; -class Module; -class Operator; -class OperatorResult; - -class PipelineModel : public QAbstractItemModel -{ - Q_OBJECT - -public: - explicit PipelineModel(QObject* parent = 0); - ~PipelineModel() override; - - QVariant data(const QModelIndex& index, int role) const override; - bool setData(const QModelIndex& index, const QVariant& value, - int role) override; - Qt::ItemFlags flags(const QModelIndex& index) const override; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - QModelIndex index(int row, int column, - const QModelIndex& parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex& index) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; - int columnCount(const QModelIndex& parent = QModelIndex()) const override; - - DataSource* dataSource(const QModelIndex& index); - MoleculeSource* moleculeSource(const QModelIndex& index); - Module* module(const QModelIndex& index); - Operator* op(const QModelIndex& index); - OperatorResult* result(const QModelIndex& index); - - QModelIndex dataSourceIndex(DataSource* source); - QModelIndex moleculeSourceIndex(MoleculeSource* source); - QModelIndex moduleIndex(Module* module); - QModelIndex operatorIndex(Operator* op); - QModelIndex resultIndex(OperatorResult* result); - - bool removeDataSource(DataSource* dataSource); - bool removeMoleculeSource(MoleculeSource* moleculeSource); - bool removeModule(Module* module); - bool removeOp(Operator* op); - bool removeResult(OperatorResult* result); - -public slots: - void dataSourceAdded(DataSource* dataSource); - void moleculeSourceAdded(MoleculeSource* moleculeSource); - void moduleAdded(Module* module); - void operatorAdded(Operator* op, DataSource* transformedDataSource = nullptr); - void operatorRemoved(Operator* op); - void operatorModified(); - void operatorTransformDone(); - - void dataSourceRemoved(DataSource* dataSource); - void moduleRemoved(Module* module); - void moleculeSourceRemoved(MoleculeSource* moleculeSource); - void childDataSourceAdded(DataSource* dataSource); - void childDataSourceRemoved(DataSource* dataSource); - void dataSourceMoved(DataSource* dataSource); - -signals: - void dataSourceItemAdded(DataSource* dataSource); - void childDataSourceItemAdded(DataSource* dataSource); - void moleculeSourceItemAdded(MoleculeSource* dataSource); - void moduleItemAdded(Module* module); - void operatorItemAdded(Operator* op); - void dataSourceModified(DataSource* dataSource); - -private: - struct Item; - class TreeItem; - - TreeItem* treeItem(const QModelIndex& index) const; - - QList m_treeItems; - - QModelIndex dataSourceIndexHelper(PipelineModel::TreeItem* treeItem, - DataSource* source); - QModelIndex operatorIndexHelper(PipelineModel::TreeItem* treeItem, - Operator* op); - void moveDataSourceHelper(DataSource* dataSource, Operator* newParent); -}; - -} // namespace tomviz - -#endif // tomvizPipelineModel_h diff --git a/tomviz/legacy/PipelineProxy.cxx b/tomviz/legacy/PipelineProxy.cxx deleted file mode 100644 index d9cfcc3b2..000000000 --- a/tomviz/legacy/PipelineProxy.cxx +++ /dev/null @@ -1,791 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "PipelineProxy.h" - -#include "core/PythonFactory.h" - -#include "ActiveObjects.h" -#include "DataSource.h" -#include "LoadDataReaction.h" -#include "modules/ModuleFactory.h" -#include "modules/ModuleManager.h" -#include "operators/OperatorFactory.h" -#include "operators/OperatorPython.h" -#include "PipelineManager.h" - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -namespace { - -Pipeline* findPipeline(QStringList& path, QString id = QString()) -{ - if (path.size() < 2) { - qCritical() << "Path doesn't have enough parts."; - return nullptr; - } - - auto iterator = path.begin(); - auto objType = *(iterator++); - - if (objType != "dataSources") { - qCritical() << "Path doesn't start with 'dataSources'."; - return nullptr; - } - - auto pipelineIndexStr = *(iterator++); - bool success = false; - auto pipelineIndex = pipelineIndexStr.toInt(&success); - if (!success) { - qCritical() << "Unable to convert index to int."; - return nullptr; - } - - auto& pipelineMgr = PipelineManager::instance(); - auto pipelines = pipelineMgr.pipelines(); - - if (pipelineIndex >= pipelines.size()) { - qCritical() << "Pipeline index no longer exists."; - return nullptr; - } - - // Now check the ids - auto dataSource = pipelines[pipelineIndex]->dataSource(); - QString currentId = - QString::asprintf("%p", static_cast(dataSource)); - if (!id.isEmpty() && currentId != id) { - qCritical() << "Pipeline no longer exists."; - return nullptr; - } - - // Remove consumed parts of the path - path.erase(path.begin(), iterator); - - return pipelines[pipelineIndex]; -} - -Operator* findOperator(QStringList& path, QString id = QString()) -{ - auto pipeline = findPipeline(path); - if (pipeline == nullptr) { - qCritical() << "Unable to find pipeline."; - return nullptr; - } - - if (path.size() < 2) { - qCritical() << "Path doesn't have enough parts."; - return nullptr; - } - - auto iterator = path.begin(); - auto objType = *(iterator++); - if (objType != "operators") { - qCritical() << "Path doesn't contain 'operators'."; - return nullptr; - } - - auto opIndexStr = *(iterator++); - bool success = false; - auto opIndex = opIndexStr.toInt(&success); - if (!success) { - qCritical() << "Unable to convert index to int."; - return nullptr; - } - - auto operators = pipeline->dataSource()->operators(); - if (opIndex >= operators.size()) { - qCritical() << "Operator index no longer exists."; - return nullptr; - } - - // Now check the ids - auto op = operators[opIndex]; - QString currentId = QString::asprintf("%p", static_cast(op)); - if (!id.isEmpty() && currentId != id) { - qCritical() << "Operator no longer exists."; - return nullptr; - } - - // Remove consumed parts of the path - path.erase(path.begin(), iterator); - - return op; -} - -Operator* findOperator(const QString& path, QString id = QString()) -{ - auto parts = path.split("/", Qt::SkipEmptyParts); - - return findOperator(parts, id); -} - -DataSource* findDataSource(QStringList& path, QString id = QString()) -{ - auto copy = QStringList(path); - auto pipeline = findPipeline(copy); - if (pipeline == nullptr) { - qCritical() << "Unable to find pipeline."; - return nullptr; - } - - // If the path no longer contains 'dataSources' we are done (its the root data - // source) - if (!copy.contains("dataSources")) { - path.erase(path.begin(), path.begin() + 2); - - // Now check the ids - auto dataSource = pipeline->dataSource(); - QString currentId = - QString::asprintf("%p", static_cast(dataSource)); - if (!id.isEmpty() && currentId != id) { - qCritical() << "Datasource no longer exists."; - return nullptr; - } - - return dataSource; - } - - // Find the operator (the child data source has to be associated with one ...) - auto op = findOperator(path); - if (op == nullptr) { - qCritical() << "Unable to find operator."; - return nullptr; - } - - auto dataSource = op->childDataSource(); - QString currentId = - QString::asprintf("%p", static_cast(dataSource)); - if (!id.isEmpty() && currentId != id) { - qCritical() << "Datasource no longer exists."; - return nullptr; - } - - // Remove the consumed data source - path.erase(path.begin(), path.begin() + 2); - - return dataSource; -} - -DataSource* findDataSource(const QString& path, QString id = QString()) -{ - auto parts = path.split("/", Qt::SkipEmptyParts); - - return findDataSource(parts, id); -} - -Module* findModule(QStringList& path, QString id = QString()) -{ - // First find the data source the module is attached to. - auto dataSource = findDataSource(path); - if (dataSource == nullptr) { - qCritical() << "Unable to find data source."; - return nullptr; - } - - if (path.size() < 2) { - qCritical() << "Path doesn't have enough parts."; - return nullptr; - } - - auto iterator = path.begin(); - auto objType = *(iterator++); - if (objType != "modules") { - qCritical() << "Path doesn't contain 'modules'."; - return nullptr; - } - - auto modIndexStr = *(iterator++); - bool success = false; - auto modIndex = modIndexStr.toInt(&success); - if (!success) { - qCritical() << "Unable to convert index to int."; - return nullptr; - } - - auto& moduleMgr = ModuleManager::instance(); - // TODO: We should use the view information here as well ... - auto modules = moduleMgr.findModulesGeneric( - dataSource, ActiveObjects::instance().activeView()); - - if (modIndex >= modules.size()) { - qCritical() << "Module index no longer exists."; - return nullptr; - } - - auto module = modules[modIndex]; - QString currentId = QString::asprintf("%p", static_cast(module)); - if (!id.isEmpty() && currentId != id) { - qCritical() << "Module no longer exists."; - return nullptr; - } - - return module; -} - -Module* findModule(const QString& path, QString id = QString()) -{ - auto parts = path.split("/", Qt::SkipEmptyParts); - - return findModule(parts, id); -} - -} // namespace - -PipelineProxy::PipelineProxy() -{ - QObject::connect(&ModuleManager::instance(), &ModuleManager::dataSourceAdded, - [this](DataSource* ds) { - QObject::connect(ds, &DataSource::dataChanged, - [this]() { this->syncToPython(); }); - - QObject::connect( - ds, &DataSource::operatorAdded, [this](Operator* op) { - QObject::connect(op, &Operator::transformModified, - [this]() { this->syncToPython(); }); - }); - - QObject::connect(ds, &DataSource::operatorRemoved, - [this]() { this->syncToPython(); }); - }); - - QObject::connect(&ModuleManager::instance(), - &ModuleManager::dataSourceRemoved, - [this]() { this->syncToPython(); }); - - QObject::connect(&ModuleManager::instance(), &ModuleManager::moduleAdded, - [this](Module* module) { - QObject::connect(module, &Module::renderNeeded, - [this]() { this->syncToPython(); }); - }); - - QObject::connect(&ModuleManager::instance(), &ModuleManager::moduleRemoved, - [this]() { this->syncToPython(); }); - - QObject::connect(&ModuleManager::instance(), - &ModuleManager::visibilityChanged, - [this]() { this->syncToPython(); }); - - auto appCore = pqApplicationCore::instance(); - auto model = appCore->getServerManagerModel(); - QObject::connect(model, &pqServerManagerModel::viewAdded, - [this]() { this->syncViewsToPython(); }); - - QObject::connect(model, &pqServerManagerModel::viewRemoved, - [this]() { this->syncViewsToPython(); }); - - QObject::connect(&ActiveObjects::instance(), - QOverload::of(&ActiveObjects::viewChanged), - [this]() { this->syncViewsToPython(); }); -} - -void PipelineProxy::syncToPython() -{ - if (!m_syncToPython) { - return; - } - - Python python; - auto tomvizState = python.import("tomviz.state"); - if (!tomvizState.isValid()) { - qCritical() << "Failed to import tomviz.state"; - } - - auto sync = tomvizState.findFunction("_sync_to_python"); - if (!sync.isValid()) { - qCritical() << "Unable to locate _sync_to_python."; - } - - auto state = serialize(); - - Python::Tuple args(1); - Python::Object pyState(state); - args.set(0, pyState); - Python::Dict kwargs; - sync.call(args, kwargs); -} - -void PipelineProxy::syncViewsToPython() -{ - Python python; - auto tomvizState = python.import("tomviz.state"); - if (!tomvizState.isValid()) { - qCritical() << "Failed to import tomviz.state"; - } - - auto sync = tomvizState.findFunction("_sync_views"); - if (!sync.isValid()) { - qCritical() << "Unable to locate _sync_view."; - } - sync.call(); -} - -std::string PipelineProxy::serialize() -{ - QJsonObject state; - QFileInfo info(QApplication::applicationDirPath()); - auto success = ModuleManager::instance().serialize(state, info.dir(), false); - - if (!success) { - qCritical() << "Serialization failed ..."; - } - - QJsonDocument doc(state); - auto stateByteArray = doc.toJson(QJsonDocument::Compact); - - return stateByteArray.toStdString(); -} - -void PipelineProxy::load(const std::string& state, - const std::string& stateRelDir) -{ - auto relDir = QDir(QByteArray::fromStdString(stateRelDir)); - auto json = QByteArray::fromStdString(state); - auto doc = QJsonDocument::fromJson(json); - - emit(&ModuleManager::instance())->enablePythonConsole(false); - - QObject::connect( - &ModuleManager::instance(), &ModuleManager::stateDoneLoading, []() { - tomviz::Python python; - - auto stateModule = python.import("tomviz.state"); - if (!stateModule.isValid()) { - qCritical() << "Failed to import tomviz.state"; - } - - auto tomviz = stateModule.findFunction("_init"); - if (!tomviz.isValid()) { - qCritical() << "Unable to locate _init."; - } - - Python::Tuple args(0); - Python::Dict kwargs; - tomviz.call(args, kwargs); - - emit(&ModuleManager::instance())->enablePythonConsole(true); - }); - this->disableSyncToPython(); - auto origExecuteOnLoad = ModuleManager::instance().executePipelinesOnLoad(); - ModuleManager::instance().executePipelinesOnLoad(false); - ModuleManager::instance().deserialize(doc.object(), relDir); - ModuleManager::instance().executePipelinesOnLoad(origExecuteOnLoad); - this->enableSyncToPython(); - // loop.exec(); -} - -std::string PipelineProxy::modulesJson() -{ - auto* view = ActiveObjects::instance().activeView(); - if (!view) { - return "{}"; - } - - vtkNew data; - data->SetDimensions(2, 2, 2); - data->AllocateScalars(VTK_INT, 1); - auto dataSource = new DataSource(data); - - QJsonObject modules; - - for (auto t : ModuleFactory::moduleTypes()) { - auto module = ModuleFactory::createModule(t, dataSource, view); - if (!module) { - continue; - } - module->setVisibility(false); - QFileInfo info(QApplication::applicationDirPath()); - auto state = module->serialize(); - modules[t] = state; - module->deleteLater(); - } - - QJsonDocument doc(modules); - auto stateByteArray = doc.toJson(QJsonDocument::Compact); - - // dataSource->deleteLater(); - - return stateByteArray.toStdString(); -} - -std::string PipelineProxy::operatorsJson() -{ - vtkNew data; - data->SetDimensions(2, 2, 2); - data->AllocateScalars(VTK_INT, 1); - auto dataSource = new DataSource(data); - - QJsonObject operators; - auto operatorTypes = OperatorFactory::instance().operatorTypes(); - // Remove Python we will deal with these separately - operatorTypes.removeAll("Python"); - for (auto& type : operatorTypes) { - auto op = OperatorFactory::instance().createOperator(type, dataSource); - auto state = op->serialize(); - operators[type] = state; - op->deleteLater(); - } - - // Now process the python operators - for (auto& info : OperatorFactory::instance().registeredPythonOperators()) { - auto op = new OperatorPython(dataSource); - auto doc = QJsonDocument::fromJson(info.json.toUtf8()); - auto name = doc.object()["name"].toString(); - // If not name is provide camelCase the label - if (name.isEmpty()) { - auto parts = info.label.split(" "); - auto iter = parts.begin(); - name = *(iter++); - - for (; iter != parts.end(); iter++) { - auto part = *iter; - part[0] = part[0].toUpper(); - name += part; - } - } - op->setJSONDescription(info.json); - op->setLabel(info.label); - op->setScript(info.source); - auto state = op->serialize(); - operators[name] = state; - op->deleteLater(); - } - - dataSource->deleteLater(); - - QJsonDocument doc(operators); - auto stateByteArray = doc.toJson(QJsonDocument::Compact); - - return stateByteArray.toStdString(); -} - -std::string PipelineProxy::serializeOperator(const std::string& path, - const std::string& id) -{ - auto p = QString::fromStdString(path); - auto i = QString::fromStdString(id); - auto op = findOperator(p, i); - if (op == nullptr) { - return ""; - } - - QJsonDocument doc(op->serialize()); - auto stateByteArray = doc.toJson(QJsonDocument::Compact); - - return stateByteArray.toStdString(); -} - -void PipelineProxy::updateOperator(const std::string& path, - const std::string& state) -{ - auto p = QString::fromStdString(path); - auto json = QByteArray::fromStdString(state); - auto doc = QJsonDocument::fromJson(json); - auto id = doc.object()["id"].toString(); - auto op = findOperator(p, id); - if (op == nullptr) { - return; - } - op->deserialize(doc.object()); -} - -std::string PipelineProxy::serializeModule(const std::string& path, - const std::string& id) -{ - auto p = QString::fromStdString(path); - auto i = QString::fromStdString(id); - auto module = findModule(p, i); - if (module == nullptr) { - return ""; - } - - QJsonDocument doc(module->serialize()); - auto stateByteArray = doc.toJson(QJsonDocument::Compact); - - return stateByteArray.toStdString(); -} - -void PipelineProxy::updateModule(const std::string& path, - const std::string& state) -{ - auto p = QString::fromStdString(path); - auto json = QByteArray::fromStdString(state); - auto doc = QJsonDocument::fromJson(json); - auto id = doc.object()["id"].toString(); - auto module = findModule(p, id); - if (module == nullptr) { - return; - } - module->deserialize(doc.object()); -} - -std::string PipelineProxy::serializeDataSource(const std::string& path, - const std::string& id) -{ - auto p = QString::fromStdString(path); - auto i = QString::fromStdString(id); - auto ds = findDataSource(p, i); - if (ds == nullptr) { - return ""; - } - - QJsonDocument doc(ds->serialize()); - auto stateByteArray = doc.toJson(QJsonDocument::Compact); - - return stateByteArray.toStdString(); -} - -void PipelineProxy::updateDataSource(const std::string& path, - const std::string& state) -{ - auto p = QString::fromStdString(path); - auto json = QByteArray::fromStdString(state); - auto doc = QJsonDocument::fromJson(json); - auto id = doc.object()["id"].toString(); - auto ds = findDataSource(p, id); - if (ds == nullptr) { - return; - } - ds->deserialize(doc.object()); -} - -void PipelineProxy::modified(std::vector opPaths, - std::vector modulePaths) -{ - for (auto path : opPaths) { - auto p = QString::fromStdString(path); - auto op = findOperator(p); - emit op->transformModified(); - } - - for (auto path : modulePaths) { - auto p = QString::fromStdString(path); - auto mod = findModule(p); - emit mod->renderNeeded(); - } -} - -std::string PipelineProxy::addModule(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& moduleType) -{ - auto p = QString::fromStdString(dataSourcePath); - auto t = QString::fromStdString(moduleType); - auto i = QString::fromStdString(dataSourceId); - auto dataSource = findDataSource(p, i); - auto module = ModuleManager::instance().createAndAddModule( - t, dataSource, ActiveObjects::instance().activeView()); - - if (module == nullptr) - return ""; - - QJsonDocument doc(module->serialize()); - auto stateByteArray = doc.toJson(QJsonDocument::Compact); - - return stateByteArray.toStdString(); -} - -std::string PipelineProxy::addOperator(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& opState) -{ - auto p = QString::fromStdString(dataSourcePath); - auto i = QString::fromStdString(dataSourceId); - auto json = QByteArray::fromStdString(opState); - auto doc = QJsonDocument::fromJson(json); - auto opJson = doc.object(); - auto dataSource = findDataSource(p, i); - auto type = opJson["type"].toString(); - - auto op = OperatorFactory::instance().createOperator(type, dataSource); - if (op == nullptr) { - qCritical() << "Failed to create operator."; - return ""; - } - if (op->deserialize(opJson)) { - dataSource->addOperator(op); - } else { - qCritical() << "Failed to deserialize operator."; - } - - QJsonDocument newDoc(op->serialize()); - auto stateByteArray = newDoc.toJson(QJsonDocument::Compact); - - return stateByteArray.toStdString(); -} - -std::string PipelineProxy::addDataSource(const std::string& dataSourceState) -{ - auto json = QByteArray::fromStdString(dataSourceState); - auto doc = QJsonDocument::fromJson(json); - auto obj = doc.object(); - auto origExecuteOnLoad = ModuleManager::instance().executePipelinesOnLoad(); - ModuleManager::instance().executePipelinesOnLoad(false); - auto ds = ModuleManager::instance().loadDataSource(obj); - ds->pipeline()->pause(); - ModuleManager::instance().executePipelinesOnLoad(origExecuteOnLoad); - - QJsonDocument newDoc(ds->serialize()); - auto stateByteArray = newDoc.toJson(QJsonDocument::Compact); - - return stateByteArray.toStdString(); -} - -void PipelineProxy::removeOperator(const std::string& opPath, - const std::string& dataSourceId, - const std::string& opId) -{ - auto p = QString::fromStdString(opPath); - auto i = QString::fromStdString(dataSourceId); - auto o = QString::fromStdString(opId); - - // Are we removing them all? - if (o.isEmpty()) { - auto ds = findDataSource(p, i); - if (ds == nullptr) { - qCritical() << "Failed to find data source."; - return; - } - - auto ops = ds->operators(); - while (ops.size() > 0) { - auto lastOperator = ops.takeLast(); - ModuleManager::instance().removeOperator(lastOperator); - } - } else { - auto op = findOperator(p, o); - if (op == nullptr) { - qCritical() << "Failed to find operator."; - return; - } - ModuleManager::instance().removeOperator(op); - } -} - -void PipelineProxy::removeModule(const std::string& modulePath, - const std::string& dataSourceId, - const std::string& moduleId) -{ - auto p = QString::fromStdString(modulePath); - auto i = QString::fromStdString(dataSourceId); - auto m = QString::fromStdString(moduleId); - - // Are we removing them all? - if (m.isEmpty()) { - auto ds = findDataSource(p, i); - if (ds == nullptr) { - qCritical() << "Failed to find data source."; - return; - } - ModuleManager::instance().removeAllModules(ds); - } else { - auto module = findModule(p, m); - if (module == nullptr) { - qCritical() << "Failed to find module."; - return; - } - ModuleManager::instance().removeModule(module); - } -} - -void PipelineProxy::removeDataSource(const std::string& dataSourcePath, - const std::string& dataSourceId) -{ - auto p = QString::fromStdString(dataSourcePath); - auto i = QString::fromStdString(dataSourceId); - - // Are we removing them all? - if (i.isEmpty()) { - ModuleManager::instance().removeAllDataSources(); - } else { - auto ds = findDataSource(p, i); - if (ds == nullptr) { - qCritical() << "Failed to find data source."; - return; - } - ModuleManager::instance().removeDataSource(ds); - } -} - -void PipelineProxy::enableSyncToPython() -{ - m_syncToPython = true; -} - -void PipelineProxy::disableSyncToPython() -{ - m_syncToPython = false; -} - -void PipelineProxy::pausePipeline(const std::string& dataSourcePath) -{ - auto p = QString::fromStdString(dataSourcePath); - auto ds = findDataSource(p); - if (ds == nullptr) { - qCritical() << "Failed to find data source."; - return; - } - - ds->pipeline()->pause(); -} - -void PipelineProxy::resumePipeline(const std::string& dataSourcePath) -{ - auto p = QString::fromStdString(dataSourcePath); - auto ds = findDataSource(p); - if (ds == nullptr) { - qCritical() << "Failed to find data source."; - return; - } - - ds->pipeline()->resume(); -} - -void PipelineProxy::executePipeline(const std::string& dataSourcePath) -{ - auto p = QString::fromStdString(dataSourcePath); - auto ds = findDataSource(p); - if (ds == nullptr) { - qCritical() << "Failed to find data source."; - return; - } - - ds->pipeline()->execute()->deleteWhenFinished(); -} - -bool PipelineProxy::pipelinePaused(const std::string& dataSourcePath) -{ - auto p = QString::fromStdString(dataSourcePath); - auto ds = findDataSource(p); - if (ds == nullptr) { - qCritical() << "Failed to find data source."; - return false; - } - - return ds->pipeline()->paused(); -} - -PipelineProxyBase* PipelineProxyFactory::create() -{ - return new PipelineProxy(); -} - -void PipelineProxyFactory::registerWithFactory() -{ - PythonFactory::instance().setPipelineProxyFactory(new PipelineProxyFactory); -} - -} // namespace tomviz diff --git a/tomviz/legacy/PipelineProxy.h b/tomviz/legacy/PipelineProxy.h deleted file mode 100644 index cd229a769..000000000 --- a/tomviz/legacy/PipelineProxy.h +++ /dev/null @@ -1,71 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPipelineProxy_h -#define tomvizPipelineProxy_h - -#include "core/PipelineProxyBase.h" - -namespace tomviz { - -/** Pure virtual base class providing a proxy to the pipeline. */ -class PipelineProxy : public PipelineProxyBase -{ -public: - PipelineProxy(); - std::string serialize() override; - void load(const std::string& state, const std::string& stateRelDir) override; - std::string modulesJson() override; - std::string operatorsJson() override; - std::string serializeOperator(const std::string& path, - const std::string& id) override; - void updateOperator(const std::string& path, - const std::string& state) override; - std::string serializeModule(const std::string& path, - const std::string& id) override; - void updateModule(const std::string& path, const std::string& state) override; - std::string serializeDataSource(const std::string& path, - const std::string& id) override; - void updateDataSource(const std::string& path, - const std::string& state) override; - std::string addModule(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& moduleType) override; - std::string addOperator(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& opState) override; - std::string addDataSource(const std::string& dataSourceState) override; - void removeOperator(const std::string& opPath, - const std::string& dataSourceId, - const std::string& opId = "") override; - void removeModule(const std::string& modulePath, - const std::string& dataSourceId, - const std::string& moduleId = "") override; - void removeDataSource(const std::string& dataSourcePath, - const std::string& dataSourceId = "") override; - void modified(std::vector opPaths, - std::vector modulePaths) override; - void syncToPython() override; - void enableSyncToPython() override; - void disableSyncToPython() override; - void pausePipeline(const std::string& dataSourcePath) override; - void resumePipeline(const std::string& dataSourcePath) override; - void executePipeline(const std::string& dataSourcePath) override; - bool pipelinePaused(const std::string& dataSourcePath) override; - - void syncViewsToPython() override; - -private: - bool m_syncToPython = true; -}; - -class PipelineProxyFactory : public PipelineProxyBaseFactory -{ -public: - PipelineProxyBase* create() override; - static void registerWithFactory(); -}; - -} // namespace tomviz - -#endif \ No newline at end of file diff --git a/tomviz/legacy/PipelineWorker.cxx b/tomviz/legacy/PipelineWorker.cxx deleted file mode 100644 index e525051fd..000000000 --- a/tomviz/legacy/PipelineWorker.cxx +++ /dev/null @@ -1,320 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "PipelineWorker.h" -#include "operators/Operator.h" - -#include -#include -#include -#include -#include - -#include - -namespace tomviz { - -class PipelineWorker::RunnableOperator : public QObject, public QRunnable -{ - Q_OBJECT - -public: - RunnableOperator(Operator* op, vtkDataObject* input, - QObject* parent = nullptr); - - /// Returns the data the operator operates on - vtkDataObject* data() { return m_data; } - Operator* op() { return m_operator; } - void run() override; - void cancel(); - bool isCanceled(); - -signals: - void complete(TransformResult result); - -private: - Operator* m_operator; - vtkDataObject* m_data; - Q_DISABLE_COPY(RunnableOperator) -}; - -class PipelineWorker::Run : public QObject -{ - Q_OBJECT - - enum class State - { - CREATED, - RUNNING, - CANCELED, - COMPLETE - }; - -public: - Run(vtkDataObject* data, QList operators); - - /// Clear all Operators from the queue and attempts to cancel the - /// running Operator. - void cancel(); - /// Returns true if the operator was successfully removed from the queue - /// before it was run, false otherwise. - bool cancel(Operator* op); - /// Returns true if we are currently running the operator pipeline, false - /// otherwise. - bool isRunning(); - - /// If the execution of the pipeline is still in progress then add this - /// operator to it. Return true is - bool addOperator(Operator* op); - - /// Returns the data object being used for this run. - vtkDataObject* data() { return m_data; } - - /// Start the pipeline execution - Future* start(); - - QList operators(); - -public slots: - void operatorComplete(TransformResult result); - - // Start the next operator in the queue - void startNextOperator(); - -signals: - void finished(bool result); - void canceled(); - -private: - RunnableOperator* m_running = nullptr; - vtkSmartPointer m_data; - QQueue m_runnableOperators; - QList m_complete; - QList m_operators; - State m_state = State::CREATED; -}; -} // namespace tomviz - -#include "PipelineWorker.moc" - - -namespace tomviz { -PipelineWorker::RunnableOperator::RunnableOperator(Operator* op, - vtkDataObject* data, - QObject* parent) - : QObject(parent), m_operator(op), m_data(data) -{ - setAutoDelete(false); -} - -void PipelineWorker::RunnableOperator::run() -{ - TransformResult result = m_operator->transform(m_data); - emit complete(result); -} - -void PipelineWorker::RunnableOperator::cancel() -{ - m_operator->cancelTransform(); -} - -bool PipelineWorker::RunnableOperator::isCanceled() -{ - return m_operator->isCanceled(); -} - -PipelineWorker::ConfigureThreadPool::ConfigureThreadPool() -{ - auto threads = QThread::idealThreadCount(); - if (threads < 1) { - threads = 1; - } else { - // Use half the threads we have available. - threads = threads / 2; - } - QThreadPool::globalInstance()->setMaxThreadCount(threads); -} - -PipelineWorker::Run::Run(vtkDataObject* data, QList operators) - : m_data(data) -{ - m_operators = operators; - foreach (auto op, operators) { - m_runnableOperators.enqueue(new RunnableOperator(op, m_data, this)); - } -} - -PipelineWorker::Future* PipelineWorker::Run::start() -{ - auto future = new PipelineWorker::Future(this); - connect(this, &PipelineWorker::Run::finished, future, - &PipelineWorker::Future::finished); - connect(this, &PipelineWorker::Run::canceled, future, - &PipelineWorker::Future::canceled); - - QTimer::singleShot(0, this, &PipelineWorker::Run::startNextOperator); - - m_state = State::RUNNING; - - return future; -} - -void PipelineWorker::Run::startNextOperator() -{ - - if (!m_runnableOperators.isEmpty()) { - m_running = m_runnableOperators.dequeue(); - connect(m_running, &RunnableOperator::complete, this, - &PipelineWorker::Run::operatorComplete); - QThreadPool::globalInstance()->start(m_running); - } -} - -void PipelineWorker::Run::operatorComplete(TransformResult transformResult) -{ - auto runnableOperator = qobject_cast(sender()); - - m_complete.append(runnableOperator); - - bool result = transformResult == TransformResult::Complete; - // Canceled - if (m_state == State::CANCELED || runnableOperator->isCanceled()) { - emit canceled(); - } - // Error - else if (!result) { - emit finished(result); - // The operator's state shows if it failed. This complete means the - // pipeline is no longer running. - m_state = State::COMPLETE; - } - // Run next operator - else if (!m_runnableOperators.isEmpty()) { - startNextOperator(); - } - // We are done - else { - m_state = State::COMPLETE; - emit finished(result); - } - - runnableOperator->deleteLater(); -} - -void PipelineWorker::Run::cancel() -{ - m_state = State::CANCELED; - // Try to cancel the currently running operator - if (m_running != nullptr) { - if (QThreadPool::globalInstance()->tryTake(m_running)) - m_running->deleteLater(); - m_running->cancel(); - m_running = nullptr; - } else { - emit canceled(); - } -} - -bool PipelineWorker::Run::cancel(Operator* op) -{ - - // If the operator is currently running we just have to cancel the execution - // of the whole pipeline. - if (m_running->op() == op) { - cancel(); - return false; - } - - foreach (auto runnable, m_runnableOperators) { - if (runnable->op() == op) { - m_runnableOperators.removeAll(runnable); - return true; - } - } - - return false; -} - -bool PipelineWorker::Run::isRunning() -{ - return m_state == State::RUNNING; -} - -bool PipelineWorker::Run::addOperator(Operator* op) -{ - if (!isRunning()) { - return false; - } - - m_runnableOperators.enqueue(new RunnableOperator(op, m_data, this)); - - return true; -} - -QList PipelineWorker::Run::operators() -{ - return m_operators; -} - -PipelineWorker::Future* PipelineWorker::run(vtkDataObject* data, Operator* op) -{ - QList ops; - ops << op; - - return run(data, ops); -} - -PipelineWorker::Future* PipelineWorker::run(vtkDataObject* data, - QList operators) -{ - // Set all the operators in the queued state - foreach (Operator* op, operators) { - op->resetState(); - } - - Run* run = new Run(data, operators); - - return run->start(); -} - -PipelineWorker::Future::Future(Run* run, QObject* parent) - : QObject(parent), m_run(run) -{} - -PipelineWorker::Future::~Future() -{ - m_run->deleteLater(); -} - -void PipelineWorker::Future::cancel() -{ - m_run->cancel(); -} - -bool PipelineWorker::Future::cancel(Operator* op) -{ - return m_run->cancel(op); -} - -bool PipelineWorker::Future::isRunning() -{ - return m_run->isRunning(); -} - -vtkDataObject* PipelineWorker::Future::result() -{ - return m_run->data(); -} - -bool PipelineWorker::Future::addOperator(Operator* op) -{ - return m_run->addOperator(op); -} - -QList PipelineWorker::Future::operators() -{ - return m_run->operators(); -} - -PipelineWorker::PipelineWorker(QObject* parent) : QObject(parent) {} -} // namespace tomviz diff --git a/tomviz/legacy/PipelineWorker.h b/tomviz/legacy/PipelineWorker.h deleted file mode 100644 index 4be773c12..000000000 --- a/tomviz/legacy/PipelineWorker.h +++ /dev/null @@ -1,82 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPipelineWorker_h -#define tomvizPipelineWorker_h - -#include -#include -#include - -class vtkDataObject; - -namespace tomviz { - -class Operator; - -/// Responsible for running Operator in a separate thread. Backed by the -/// QThreadPool. Operators are run in sequence, one at a time. -class PipelineWorker : public QObject -{ - Q_OBJECT - -public: - class Future; - PipelineWorker(QObject* parent = nullptr); - Future* run(vtkDataObject* data, Operator* op); - Future* run(vtkDataObject* data, QList ops); - -private: - class RunnableOperator; - class Run; - class ConfigureThreadPool - { - public: - ConfigureThreadPool(); - }; - - ConfigureThreadPool configure; -}; - -class PipelineWorker::Future : public QObject -{ - Q_OBJECT - friend class PipelineWorker::Run; - -public: - /// Clear all Operators from the queue and attempts to cancel the - /// running Operator. - void cancel(); - /// Returns true if the operator was successfully removed from the queue - /// before - /// it was run, false otherwise. - bool cancel(Operator* op); - /// Returns true if we are currently running the operator pipeline, false - /// otherwise. - bool isRunning(); - - vtkDataObject* result(); - - /// If the execution of the pipeline is still in progress then add this - /// operator to it. Return true is - bool addOperator(Operator* op); - - QList operators(); - - ~Future(); - -signals: - void canceled(); - void finished(bool result); - void progressRangeChanged(int minimum, int maximum); - void progressTextChanged(const QString& progressText); - void progressValueChanged(int progressValue); - -private: - Future(Run* run, QObject* parent = nullptr); - - Run* m_run; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/PythonGeneratedDatasetReaction.cxx b/tomviz/legacy/PythonGeneratedDatasetReaction.cxx deleted file mode 100644 index 476bb2032..000000000 --- a/tomviz/legacy/PythonGeneratedDatasetReaction.cxx +++ /dev/null @@ -1,565 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "PythonGeneratedDatasetReaction.h" - -#include "ActiveObjects.h" -#include "legacy/DataSource.h" -#include "LoadDataReaction.h" -#include "legacy/modules/ModuleManager.h" - -#include "pipeline/PortData.h" -#include "pipeline/PortType.h" -#include "pipeline/SourceNode.h" -#include "pipeline/data/VolumeData.h" -#include "PythonUtilities.h" -#include "Utilities.h" -#include "core/Variant.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace { - -class PythonGeneratedDataSource : public QObject -{ - Q_OBJECT - -public: - PythonGeneratedDataSource(const QString& l, QObject* p = nullptr) - : QObject(p), m_label(l) - {} - - void setScript(const QString& script) - { - tomviz::Python::initialize(); - - { - tomviz::Python python; - m_operatorModule = python.import("tomviz.internal_utils"); - if (!m_operatorModule.isValid()) { - qCritical() << "Failed to import tomviz.utils module."; - } - - // Don't let these be the same, even for similar scripts. Seems to - // cause python crashes. - QString moduleName = - QString("tomviz_%1%2").arg(m_label).arg(s_numberOfScripts++); - - tomviz::Python::Module module = - python.import(script, m_label, moduleName); - if (!module.isValid()) { - qCritical() << "Failed to create module."; - return; - } - - m_generateFunction = module.findFunction("generate_dataset"); - if (!m_generateFunction.isValid()) { - qCritical() << "Script does not have a 'generate_dataset' function."; - return; - } - - m_makeDatasetFunction = m_operatorModule.findFunction("make_dataset"); - if (!m_makeDatasetFunction.isValid()) { - qCritical() << "Could not find make_dataset function in tomviz.utils"; - return; - } - } - - m_pythonScript = script; - } - - tomviz::DataSource* createDataSource(const int shape[3]) - { - vtkNew image; - - { - tomviz::Python python; - - tomviz::Python::Object result; - tomviz::Python::Tuple args(5); - - args.set(0, shape[0]); - args.set(1, shape[1]); - args.set(2, shape[2]); - tomviz::Python::Object imageData = - tomviz::Python::VTK::GetObjectFromPointer(image); - args.set(3, imageData); - args.set(4, m_generateFunction); - - tomviz::Python::Dict kwargs; - foreach (QString key, m_arguments.keys()) { - tomviz::Variant value = tomviz::toVariant(m_arguments[key]); - kwargs.set(key, value); - } - - result = m_makeDatasetFunction.call(args, kwargs); - if (!result.isValid()) { - qCritical() << "Failed to execute script."; - return nullptr; - } - } - - QVariantMap vmap; - foreach (QString key, m_arguments.keys()) { - vmap[key] = m_arguments[key]; - } - - QJsonObject argsJson = QJsonObject::fromVariantMap(vmap); - - QJsonArray size; - size.append(shape[0]); - size.append(shape[1]); - size.append(shape[2]); - - QJsonObject description; - description["script"] = m_pythonScript; - description["label"] = m_label; - description["args"] = argsJson; - description["shape"] = size; - - tomviz::DataSource* dataSource = new tomviz::DataSource( - m_label, tomviz::DataSource::Volume, nullptr, - tomviz::DataSource::PersistenceState::Transient, description); - - dataSource->setData(image); - - return dataSource; - } - - void setArguments(QMap args) { m_arguments = args; } - -private: - tomviz::Python::Module m_operatorModule; - tomviz::Python::Function m_generateFunction; - tomviz::Python::Function m_makeDatasetFunction; - QString m_label; - QString m_pythonScript; - QMap m_arguments; - static int s_numberOfScripts; -}; - -int PythonGeneratedDataSource::s_numberOfScripts = 0; - -class ShapeWidget : public QWidget -{ - Q_OBJECT - -public: - ShapeWidget(QWidget* p = nullptr) - : QWidget(p), m_xSpinBox(new QSpinBox(this)), - m_ySpinBox(new QSpinBox(this)), m_zSpinBox(new QSpinBox(this)) - { - QHBoxLayout* boundsLayout = new QHBoxLayout; - - QLabel* xLabel = new QLabel("X:", this); - QLabel* yLabel = new QLabel("Y:", this); - QLabel* zLabel = new QLabel("Z:", this); - - m_xSpinBox->setMaximum(std::numeric_limits::max()); - m_xSpinBox->setMinimum(1); - m_xSpinBox->setValue(100); - m_ySpinBox->setMaximum(std::numeric_limits::max()); - m_ySpinBox->setMinimum(1); - m_ySpinBox->setValue(100); - m_zSpinBox->setMaximum(std::numeric_limits::max()); - m_zSpinBox->setMinimum(1); - m_zSpinBox->setValue(100); - - boundsLayout->addWidget(xLabel); - boundsLayout->addWidget(m_xSpinBox); - boundsLayout->addWidget(yLabel); - boundsLayout->addWidget(m_ySpinBox); - boundsLayout->addWidget(zLabel); - boundsLayout->addWidget(m_zSpinBox); - - setLayout(boundsLayout); - } - - void getShape(int shape[3]) - { - shape[0] = m_xSpinBox->value(); - shape[1] = m_ySpinBox->value(); - shape[2] = m_zSpinBox->value(); - } - - void setSpinBoxValue(int newX, int newY, int newZ) - { - m_xSpinBox->setValue(newX); - m_ySpinBox->setValue(newY); - m_zSpinBox->setValue(newZ); - } - - void setSpinBoxMaximum(int xmax, int ymax, int zmax) - { - m_xSpinBox->setMaximum(xmax); - m_ySpinBox->setMaximum(ymax); - m_zSpinBox->setMaximum(zmax); - } - -private: - Q_DISABLE_COPY(ShapeWidget) - - QSpinBox* m_xSpinBox; - QSpinBox* m_ySpinBox; - QSpinBox* m_zSpinBox; -}; -} // namespace - -#include "PythonGeneratedDatasetReaction.moc" - -namespace tomviz { - -namespace { -void addGeneratedSource(DataSource* ds) -{ - if (!ds) { - return; - } - auto* source = new pipeline::SourceNode(); - source->setLabel(ds->label()); - source->addOutput("volume", pipeline::PortType::ImageData); - auto vol = std::make_shared(ds->imageData()); - vol->setLabel(ds->label()); - source->setOutputData( - "volume", - pipeline::PortData(vol, pipeline::PortType::ImageData)); - LoadDataReaction::sourceNodeAdded(source, true, false); - delete ds; -} -} // namespace - -PythonGeneratedDatasetReaction::PythonGeneratedDatasetReaction( - QAction* parentObject, const QString& l, const QString& s) - : pqReaction(parentObject) -{ - m_scriptLabel = l; - m_scriptSource = s; -} - -void PythonGeneratedDatasetReaction::addDataset() -{ - PythonGeneratedDataSource generator(m_scriptLabel); - if (m_scriptLabel == "Constant Dataset") { - QDialog dialog; - dialog.setWindowTitle("Generate Constant Dataset"); - ShapeWidget* shapeWidget = new ShapeWidget(&dialog); - - QLabel* label = new QLabel("Value: ", &dialog); - QDoubleSpinBox* constant = new QDoubleSpinBox(&dialog); - - QHBoxLayout* parametersLayout = new QHBoxLayout; - parametersLayout->addWidget(label); - parametersLayout->addWidget(constant); - - QVBoxLayout* layout = new QVBoxLayout; - QDialogButtonBox* buttons = new QDialogButtonBox( - QDialogButtonBox::Cancel | QDialogButtonBox::Ok, Qt::Horizontal, &dialog); - QObject::connect(buttons, &QDialogButtonBox::accepted, &dialog, - &QDialog::accept); - QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, - &QDialog::reject); - - layout->addWidget(shapeWidget); - layout->addItem(parametersLayout); - layout->addWidget(buttons); - - dialog.setLayout(layout); - if (dialog.exec() != QDialog::Accepted) { - return; - } - - generator.setScript(m_scriptSource); - QMap args; - args["CONSTANT"] = constant->value(); - generator.setArguments(args); - - int shape[3]; - shapeWidget->getShape(shape); - addGeneratedSource(generator.createDataSource(shape)); - } else if (m_scriptLabel == "Random Particles") { - QDialog dialog; - dialog.setWindowTitle("Generate Random Particles"); - QVBoxLayout* layout = new QVBoxLayout; // overall layout - // Guide - QLabel* guide = new QLabel; - guide->setText("Generate many random 3D \"particles\" using the Fourier " - "Noise method. You can increase the \"Internal Complexity\" " - "of particles and their average \"Particle Size\". You can " - "also specify the sparsity (percentage of non-zero voxels) " - "of the generated dataset. Note: 512x512x512 may take a " - "couple minutes to run."); - guide->setWordWrap(true); - layout->addWidget(guide); - - // Shape Layout - ShapeWidget* shapeLayout = new ShapeWidget(&dialog); - shapeLayout->setSpinBoxValue(128, 128, 128); - shapeLayout->setSpinBoxMaximum(512, 512, 512); - // Parameter Layout - QGridLayout* parametersLayout = new QGridLayout; - - QLabel* label = new QLabel("Internal Complexity ([1-100]): ", &dialog); - parametersLayout->addWidget(label, 0, 0, 1, 2); - - QDoubleSpinBox* innerStructureParameter = new QDoubleSpinBox(&dialog); - innerStructureParameter->setRange(1, 100); - innerStructureParameter->setValue(30); - innerStructureParameter->setSingleStep(5); - parametersLayout->addWidget(innerStructureParameter, 0, 2, 1, 1); - - label = new QLabel("Particle Size ([1-100]): ", &dialog); - parametersLayout->addWidget(label, 1, 0, 1, 2); - - QDoubleSpinBox* shapeParameter = new QDoubleSpinBox(&dialog); - shapeParameter->setRange(1, 100); - shapeParameter->setValue(60); - shapeParameter->setSingleStep(5); - parametersLayout->addWidget(shapeParameter, 1, 2, 1, 1); - - label = new QLabel("Sparsity (percentage of non-zero voxels): ", &dialog); - parametersLayout->addWidget(label, 2, 0, 2, 1); - - QDoubleSpinBox* sparsityParameter = new QDoubleSpinBox(&dialog); - sparsityParameter->setRange(0, 1); - sparsityParameter->setValue(0.2); - sparsityParameter->setSingleStep(0.05); - parametersLayout->addWidget(sparsityParameter, 2, 2, 1, 1); - - // Buttons - QDialogButtonBox* buttons = new QDialogButtonBox( - QDialogButtonBox::Cancel | QDialogButtonBox::Ok, Qt::Horizontal, &dialog); - QObject::connect(buttons, &QDialogButtonBox::accepted, &dialog, - &QDialog::accept); - QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, - &QDialog::reject); - - layout->addWidget(shapeLayout); - layout->addItem(parametersLayout); - layout->addWidget(buttons); - dialog.setLayout(layout); - dialog.layout()->setSizeConstraint( - QLayout::SetFixedSize); // Make the UI non-resizeable - - // substitute values - if (dialog.exec() == QDialog::Accepted) { - generator.setScript(m_scriptSource); - QMap args; - args["p_in"] = innerStructureParameter->value(); - args["p_s"] = shapeParameter->value(); - args["sparsity"] = sparsityParameter->value(); - generator.setArguments(args); - - int shape[3]; - shapeLayout->getShape(shape); - addGeneratedSource(generator.createDataSource(shape)); - } - } else if (m_scriptLabel == "Electron Beam Shape") { - QDialog dialog; - dialog.setWindowTitle("Generate Electron Beam Shape"); - QVBoxLayout* layout = new QVBoxLayout; // overall layout - // Guide - QLabel* guide = new QLabel; - guide->setText("Generate a convergent electron beam in 3D. This represents " - "the 3D probe used for atomic resolution imaging in a " - "scanning transmission electron microscope."); - guide->setWordWrap(true); - layout->addWidget(guide); - - // Parameter Layout - QGridLayout* parametersLayout = new QGridLayout; - - QLabel* label = new QLabel("Beam energy (keV): ", &dialog); - parametersLayout->addWidget(label, 0, 0, 1, 2); - QDoubleSpinBox* voltage = new QDoubleSpinBox(&dialog); - voltage->setRange(1, 1000000); - voltage->setValue(300); - voltage->setSingleStep(50); - parametersLayout->addWidget(voltage, 0, 2, 1, 1); - - label = new QLabel("Semi-convergence angle (mrad): ", &dialog); - parametersLayout->addWidget(label, 1, 0, 1, 2); - QDoubleSpinBox* alpha_max = new QDoubleSpinBox(&dialog); - alpha_max->setRange(0, 500); - alpha_max->setValue(30); - alpha_max->setSingleStep(0.5); - parametersLayout->addWidget(alpha_max, 1, 2, 1, 1); - - label = new QLabel("Transerve plane (x-y): ", &dialog); - parametersLayout->addWidget(label, 2, 0, 1, 1); - label = new QLabel("Number of pixels: ", &dialog); - parametersLayout->addWidget(label, 2, 1, 1, 1); - QSpinBox* Nxy = new QSpinBox(&dialog); - Nxy->setRange(64, 2048); - Nxy->setValue(256); - Nxy->setSingleStep(1); - parametersLayout->addWidget(Nxy, 2, 2, 1, 1); - - label = new QLabel("x-y pixel size (angstrom): ", &dialog); - parametersLayout->addWidget(label, 3, 1, 1, 1); - QDoubleSpinBox* dxy = new QDoubleSpinBox(&dialog); - dxy->setDecimals(4); - dxy->setMinimum(0.0001); - dxy->setValue(0.1); - dxy->setSingleStep(0.1); - parametersLayout->addWidget(dxy, 3, 2, 1, 1); - - label = new QLabel("Propagation direction (z): ", &dialog); - parametersLayout->addWidget(label, 4, 0, 1, 1); - label = new QLabel("Number of pixels: ", &dialog); - parametersLayout->addWidget(label, 4, 1, 1, 1); - QSpinBox* Nz = new QSpinBox(&dialog); - Nz->setRange(1, 2048); - Nz->setValue(512); - Nz->setSingleStep(1); - parametersLayout->addWidget(Nz, 4, 2, 1, 1); - - label = new QLabel("Minimum defocus (nm): ", &dialog); - parametersLayout->addWidget(label, 5, 1, 1, 1); - QDoubleSpinBox* df_min = new QDoubleSpinBox(&dialog); - df_min->setRange(-1000000, 1000000); - df_min->setValue(-50.0); - df_min->setSingleStep(5.0); - parametersLayout->addWidget(df_min, 5, 2, 1, 1); - - label = new QLabel("Maximum defocus (nm): ", &dialog); - parametersLayout->addWidget(label, 6, 1, 1, 1); - QDoubleSpinBox* df_max = new QDoubleSpinBox(&dialog); - df_max->setRange(-1000000, 1000000); - df_max->setValue(100.0); - df_max->setSingleStep(5.0); - parametersLayout->addWidget(df_max, 6, 2, 1, 1); - - label = new QLabel("Third-order spherical aberration (mm): ", &dialog); - parametersLayout->addWidget(label, 0, 3, 1, 2); - QDoubleSpinBox* c3 = new QDoubleSpinBox(&dialog); - c3->setRange(-1000000, 1000000); - c3->setValue(0.2); - c3->setSingleStep(0.1); - parametersLayout->addWidget(c3, 0, 5, 1, 1); - - label = new QLabel("Twofold astigmatism: ", &dialog); - parametersLayout->addWidget(label, 1, 3, 1, 1); - label = new QLabel("Value (nm): ", &dialog); - parametersLayout->addWidget(label, 1, 4, 1, 1); - QDoubleSpinBox* f_a2 = new QDoubleSpinBox(&dialog); - f_a2->setRange(-1000000, 1000000); - f_a2->setValue(0.0); - f_a2->setSingleStep(1000); - parametersLayout->addWidget(f_a2, 1, 5, 1, 1); - label = new QLabel("Orientation (rad): ", &dialog); - parametersLayout->addWidget(label, 2, 4, 1, 1); - QDoubleSpinBox* phi_a2 = new QDoubleSpinBox(&dialog); - phi_a2->setValue(0.0); - phi_a2->setSingleStep(0.1); - parametersLayout->addWidget(phi_a2, 2, 5, 1, 1); - - label = new QLabel("Threefold astigmatism: ", &dialog); - parametersLayout->addWidget(label, 3, 3, 1, 1); - label = new QLabel("Value (nm): ", &dialog); - parametersLayout->addWidget(label, 3, 4, 1, 1); - QDoubleSpinBox* f_a3 = new QDoubleSpinBox(&dialog); - f_a3->setRange(-1000000, 1000000); - f_a3->setValue(0.0); - f_a3->setSingleStep(1000); - parametersLayout->addWidget(f_a3, 3, 5, 1, 1); - label = new QLabel("Orientation (rad): ", &dialog); - parametersLayout->addWidget(label, 4, 4, 1, 1); - QDoubleSpinBox* phi_a3 = new QDoubleSpinBox(&dialog); - phi_a3->setValue(0.0); - phi_a3->setSingleStep(0.1); - parametersLayout->addWidget(phi_a3, 4, 5, 1, 1); - - label = new QLabel("Coma: ", &dialog); - parametersLayout->addWidget(label, 5, 3, 1, 1); - label = new QLabel("Value (nm): ", &dialog); - parametersLayout->addWidget(label, 5, 4, 1, 1); - QDoubleSpinBox* f_c3 = new QDoubleSpinBox(&dialog); - f_c3->setRange(-1000000, 1000000); - f_c3->setValue(1500.0); - f_c3->setSingleStep(1000); - parametersLayout->addWidget(f_c3, 5, 5, 1, 1); - label = new QLabel("Orientation (rad): ", &dialog); - parametersLayout->addWidget(label, 6, 4, 1, 1); - QDoubleSpinBox* phi_c3 = new QDoubleSpinBox(&dialog); - phi_c3->setValue(0.0); - phi_c3->setSingleStep(0.1); - parametersLayout->addWidget(phi_c3, 6, 5, 1, 1); - - // Buttons - QDialogButtonBox* buttons = new QDialogButtonBox( - QDialogButtonBox::Cancel | QDialogButtonBox::Ok, Qt::Horizontal, &dialog); - QObject::connect(buttons, &QDialogButtonBox::accepted, &dialog, - &QDialog::accept); - QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, - &QDialog::reject); - - layout->addItem(parametersLayout); - layout->addWidget(buttons); - dialog.setLayout(layout); - dialog.layout()->setSizeConstraint( - QLayout::SetFixedSize); // Make the UI non-resizeable - - // substitute values - if (dialog.exec() == QDialog::Accepted) { - generator.setScript(m_scriptSource); - QMap args; - args["voltage"] = voltage->value(); - args["alpha_max"] = alpha_max->value(); - args["Nxy"] = Nxy->value(); - args["Nz"] = Nz->value(); - args["dxy"] = dxy->value(); - args["df_min"] = df_min->value(); - args["df_max"] = df_max->value(); - args["c3"] = c3->value(); - args["f_a2"] = f_a2->value(); - args["phi_a2"] = phi_a2->value(); - args["f_a3"] = f_a3->value(); - args["phi_a3"] = phi_a3->value(); - args["f_c3"] = f_c3->value(); - args["phi_c3"] = phi_c3->value(); - generator.setArguments(args); - - const int shape[3] = { Nxy->value(), Nxy->value(), Nz->value() }; - addGeneratedSource(generator.createDataSource(shape)); - } - } // end of else if -} - -DataSource* PythonGeneratedDatasetReaction::createDataSource( - const QJsonObject& sourceInformation) -{ - PythonGeneratedDataSource generator(sourceInformation["label"].toString()); - generator.setScript(sourceInformation["script"].toString()); - QVariantMap args = sourceInformation["args"].toObject().toVariantMap(); - generator.setArguments(args); - int shape[3]; - QJsonArray shapeJson = sourceInformation["shape"].toArray(); - shape[0] = shapeJson[0].toInt(); - shape[1] = shapeJson[1].toInt(); - shape[2] = shapeJson[2].toInt(); - return generator.createDataSource(shape); -} -} // namespace tomviz diff --git a/tomviz/legacy/PythonGeneratedDatasetReaction.h b/tomviz/legacy/PythonGeneratedDatasetReaction.h deleted file mode 100644 index 016c0aa7c..000000000 --- a/tomviz/legacy/PythonGeneratedDatasetReaction.h +++ /dev/null @@ -1,35 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPythonGeneratedDatasetReaction_h -#define tomvizPythonGeneratedDatasetReaction_h - -#include - -namespace tomviz { -class DataSource; - -class PythonGeneratedDatasetReaction : public pqReaction -{ - Q_OBJECT - -public: - PythonGeneratedDatasetReaction(QAction* parent, const QString& label, - const QString& source); - - void addDataset(); - - static DataSource* createDataSource(const QJsonObject& sourceInformation); - -protected: - void onTriggered() override { this->addDataset(); } - -private: - Q_DISABLE_COPY(PythonGeneratedDatasetReaction) - - QString m_scriptLabel; - QString m_scriptSource; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/ThreadedExecutor.cxx b/tomviz/legacy/ThreadedExecutor.cxx deleted file mode 100644 index 935c494e0..000000000 --- a/tomviz/legacy/ThreadedExecutor.cxx +++ /dev/null @@ -1,102 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ThreadedExecutor.h" - -namespace tomviz { - -class PipelineFutureThreadedInternal : public Pipeline::Future -{ -public: - PipelineFutureThreadedInternal(vtkImageData* imageData, - QList operators, - PipelineWorker::Future* future = nullptr, - QObject* parent = nullptr); - -private: - PipelineWorker::Future* m_future; -}; - -PipelineFutureThreadedInternal::PipelineFutureThreadedInternal( - vtkImageData* imageData, QList operators, - PipelineWorker::Future* future, QObject* parent) - : Pipeline::Future(imageData, operators, parent), m_future(future) -{ - connect(m_future, &PipelineWorker::Future::finished, this, - &Pipeline::Future::finished); - connect(m_future, &PipelineWorker::Future::canceled, this, - &Pipeline::Future::canceled); - - connect(m_future, &PipelineWorker::Future::finished, future, - [future]() { future->deleteLater(); }); - - connect(m_future, &PipelineWorker::Future::canceled, future, - [future]() { future->deleteLater(); }); -} - -ThreadPipelineExecutor::ThreadPipelineExecutor(Pipeline* pipeline) - : PipelineExecutor(pipeline) -{ - m_worker = new PipelineWorker(this); -} - -Pipeline::Future* ThreadPipelineExecutor::execute(vtkDataObject* data, - QList operators, - int start, int end) -{ - if (end == -1) { - end = operators.size(); - } - operators = operators.mid(start, end - start); - - // Cancel any running operators. TODO in the future we should be able to add - // operators to end of a running pipeline. - if (!m_future.isNull() && m_future->isRunning()) { - m_future->cancel(); - } - - auto copy = data->NewInstance(); - copy->DeepCopy(data); - - if (operators.isEmpty()) { - emit pipeline()->finished(); - auto future = new Pipeline::Future(); - future->setResult(vtkImageData::SafeDownCast(copy)); - copy->FastDelete(); - QTimer::singleShot(0, [future] { emit future->finished(); }); - return future; - } - - m_future = m_worker->run(copy, operators); - auto future = new PipelineFutureThreadedInternal( - vtkImageData::SafeDownCast(copy), operators, m_future.data(), this); - copy->FastDelete(); - - return future; -} - -void ThreadPipelineExecutor::cancel(std::function canceled) -{ - if (!m_future.isNull()) { - if (canceled) { - connect(m_future, &PipelineWorker::Future::canceled, canceled); - } - m_future->cancel(); - } -} - -bool ThreadPipelineExecutor::cancel(Operator* op) -{ - if (!m_future.isNull() && m_future->isRunning()) { - return m_future->cancel(op); - } - - return false; -} - -bool ThreadPipelineExecutor::isRunning() -{ - return !m_future.isNull() && m_future->isRunning(); -} - -} // namespace tomviz diff --git a/tomviz/legacy/ThreadedExecutor.h b/tomviz/legacy/ThreadedExecutor.h deleted file mode 100644 index 4de786856..000000000 --- a/tomviz/legacy/ThreadedExecutor.h +++ /dev/null @@ -1,39 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizThreadedExecutor_h -#define tomvizThreadedExecutor_h - -#include - -#include "PipelineExecutor.h" - -namespace tomviz { -class DataSource; -class Operator; -class Pipeline; - -/// -/// The default pipeline executor, providing execution of pipelines in a -/// background thread in order to retain interactivity in the user interface. -/// -class ThreadPipelineExecutor : public PipelineExecutor -{ - Q_OBJECT - -public: - ThreadPipelineExecutor(Pipeline* pipeline); - Pipeline::Future* execute(vtkDataObject* data, QList operators, - int start = 0, int end = -1) override; - void cancel(std::function canceled) override; - bool cancel(Operator* op) override; - bool isRunning() override; - -private: - PipelineWorker* m_worker; - QPointer m_future; -}; - -} // namespace tomviz - -#endif // tomvizThreadedExecutor_h diff --git a/tomviz/legacy/core/DataSourceBase.h b/tomviz/legacy/core/DataSourceBase.h deleted file mode 100644 index da679f704..000000000 --- a/tomviz/legacy/core/DataSourceBase.h +++ /dev/null @@ -1,40 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizDataSourceBase_h -#define tomvizDataSourceBase_h - -#include "Variant.h" - -class vtkImageData; - -namespace tomviz { - -using MetadataType = std::map; - -/** Pure virtual base class providing a proxy to the DataSource class. */ -class DataSourceBase -{ -public: - void setDarkData(vtkImageData* data) { m_dark = data; } - vtkImageData* darkData() const { return m_dark; } - - void setWhiteData(vtkImageData* data) { m_white = data; } - vtkImageData* whiteData() const { return m_white; } - - void setFileName(const std::string& name) { m_fileName = name; } - std::string fileName() const { return m_fileName; } - - void setMetadata(const MetadataType& meta) { m_metadata = meta; } - MetadataType metadata() const { return m_metadata; } - -private: - vtkImageData* m_dark = nullptr; - vtkImageData* m_white = nullptr; - std::string m_fileName; - MetadataType m_metadata; -}; - -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/core/OperatorProxyBase.h b/tomviz/legacy/core/OperatorProxyBase.h deleted file mode 100644 index 3b394ca63..000000000 --- a/tomviz/legacy/core/OperatorProxyBase.h +++ /dev/null @@ -1,48 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizOperatorProxyBase_h -#define tomvizOperatorProxyBase_h - -#include - -class vtkImageData; - -namespace tomviz { - -/** Pure virtual base class providing a proxy to the operator class. */ -class OperatorProxyBase -{ -public: - OperatorProxyBase(void*) {} - virtual ~OperatorProxyBase() = default; - - virtual bool canceled() = 0; - - virtual bool completed() = 0; - - virtual void setTotalProgressSteps(int progress) = 0; - - virtual int totalProgressSteps() = 0; - - virtual void setProgressStep(int progress) = 0; - - virtual int progressStep() = 0; - - virtual void setProgressMessage(const std::string& message) = 0; - - virtual std::string progressMessage() = 0; - - virtual void setProgressData(vtkImageData* object) = 0; -}; - -class OperatorProxyBaseFactory -{ -public: - virtual ~OperatorProxyBaseFactory() = default; - virtual OperatorProxyBase* create(void* o) = 0; -}; - -} // namespace tomviz - -#endif \ No newline at end of file diff --git a/tomviz/legacy/core/PipelineProxyBase.h b/tomviz/legacy/core/PipelineProxyBase.h deleted file mode 100644 index afe079b03..000000000 --- a/tomviz/legacy/core/PipelineProxyBase.h +++ /dev/null @@ -1,70 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizPipelineProxyBase_h -#define tomvizPipelineProxyBase_h - -#include -#include - -namespace tomviz { - -/** Pure virtual base class providing a proxy to the operator class. */ -class PipelineProxyBase -{ -public: - virtual ~PipelineProxyBase() = default; - virtual std::string serialize() = 0; - virtual void load(const std::string& state, - const std::string& stateRelDir) = 0; - virtual std::string modulesJson() = 0; - virtual std::string operatorsJson() = 0; - virtual std::string serializeOperator(const std::string& path, - const std::string& id) = 0; - virtual void updateOperator(const std::string& path, - const std::string& state) = 0; - virtual std::string serializeModule(const std::string& path, - const std::string& id) = 0; - virtual void updateModule(const std::string& path, - const std::string& state) = 0; - virtual std::string serializeDataSource(const std::string& path, - const std::string& id) = 0; - virtual void updateDataSource(const std::string& path, - const std::string& state) = 0; - virtual std::string addModule(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& moduleType) = 0; - virtual std::string addOperator(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& opState) = 0; - virtual std::string addDataSource(const std::string& dataSourceState) = 0; - virtual void removeOperator(const std::string& opPath, - const std::string& dataSourceId, - const std::string& opId = "") = 0; - virtual void removeModule(const std::string& modulePath, - const std::string& dataSourceId, - const std::string& moduleId = "") = 0; - virtual void removeDataSource(const std::string& dataSourcePath, - const std::string& dataSourceId = "") = 0; - virtual void modified(std::vector opPaths, - std::vector modulePaths) = 0; - virtual void syncToPython() = 0; - virtual void syncViewsToPython() = 0; - virtual void enableSyncToPython() = 0; - virtual void disableSyncToPython() = 0; - virtual void pausePipeline(const std::string& dataSourcePath) = 0; - virtual void resumePipeline(const std::string& dataSourcePath) = 0; - virtual void executePipeline(const std::string& dataSourcePath) = 0; - virtual bool pipelinePaused(const std::string& dataSourcePath) = 0; -}; - -class PipelineProxyBaseFactory -{ -public: - virtual ~PipelineProxyBaseFactory() = default; - virtual PipelineProxyBase* create() = 0; -}; - -} // namespace tomviz - -#endif \ No newline at end of file diff --git a/tomviz/legacy/modules/Module.cxx b/tomviz/legacy/modules/Module.cxx deleted file mode 100644 index 05070c621..000000000 --- a/tomviz/legacy/modules/Module.cxx +++ /dev/null @@ -1,368 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "Module.h" - -#include "ActiveObjects.h" -#include "../DataSource.h" -#include "ModuleFactory.h" -#include "MoleculeSource.h" -#include "../operators/OperatorResult.h" -#include "Utilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -class Module::MInternals -{ - vtkSmartPointer m_detachedColorMap; - vtkSmartPointer m_detachedOpacityMap; - vtkRectd m_detachedTransferFunction2DBox; - -public: - vtkWeakPointer m_colorMap; - vtkWeakPointer m_opacityMap; - vtkNew m_gradientOpacityMap; - - Module::TransferMode m_transferMode; - vtkNew m_transfer2D; - vtkSMProxy* detachedColorMap() - { - if (!m_detachedColorMap) { - static unsigned int colorMapCounter = 0; - ++colorMapCounter; - - auto pxm = ActiveObjects::instance().proxyManager(); - - vtkNew tfmgr; - m_detachedColorMap = tfmgr->GetColorTransferFunction( - QString("ModuleColorMap%1").arg(colorMapCounter).toLatin1().data(), - pxm); - m_detachedOpacityMap = - vtkSMPropertyHelper(m_detachedColorMap, "ScalarOpacityFunction") - .GetAsProxy(); - // The widget interprests negative width as uninitialized. - m_detachedTransferFunction2DBox.Set(0, 0, -1, -1); - } - return m_detachedColorMap; - } - - vtkSMProxy* detachedOpacityMap() - { - detachedColorMap(); - return m_detachedOpacityMap; - } - - vtkRectd* detachedTransferFunction2DBox() - { - detachedColorMap(); - return &m_detachedTransferFunction2DBox; - } -}; - -Module::Module(QObject* parentObject) - : QObject(parentObject), d(new Module::MInternals()), - m_activeScalars(Module::defaultScalarsIdx()) -{} - -Module::~Module() = default; - -bool Module::initialize(OperatorResult* result, vtkSMViewProxy* vtkView) -{ - m_view = vtkView; - m_operatorResult = result; - // LEGACY STUB: activeDataSource() removed from ActiveObjects - m_activeDataSource = nullptr; - return (m_view && m_view->IsA("vtkSMRenderViewProxy") && m_operatorResult); -} - -bool Module::initialize(MoleculeSource* data, vtkSMViewProxy* vtkView) -{ - m_view = vtkView; - m_activeMoleculeSource = data; - // LEGACY STUB: activeDataSource() removed from ActiveObjects - m_activeDataSource = nullptr; - return (m_view && m_view->IsA("vtkSMRenderViewProxy") && - m_activeMoleculeSource); -} - -bool Module::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - m_view = vtkView; - m_activeDataSource = data; - d->m_gradientOpacityMap->RemoveAllPoints(); - d->m_transfer2D->SetDimensions(1, 1, 1); - d->m_transfer2D->AllocateScalars(VTK_FLOAT, 4); - - if (m_view && m_view->IsA("vtkSMRenderViewProxy") && m_activeDataSource) { - // FIXME: we're connecting this too many times. Fix it. - connect(m_activeDataSource, &DataSource::dataChanged, - tomviz::convert(vtkView), &pqView::render); - connect(m_activeDataSource, &DataSource::dataChanged, this, - &Module::dataSourceChanged); - connect(m_activeDataSource, &DataSource::displayPositionChanged, this, - &Module::dataSourceMoved); - connect(m_activeDataSource, &DataSource::displayOrientationChanged, this, - &Module::dataSourceRotated); - } - return (m_view && m_view->IsA("vtkSMRenderViewProxy") && m_activeDataSource); -} - -vtkSMViewProxy* Module::view() const -{ - return m_view; -} - -DataSource* Module::dataSource() const -{ - return m_activeDataSource; -} - -MoleculeSource* Module::moleculeSource() const -{ - return m_activeMoleculeSource; -} - -OperatorResult* Module::operatorResult() const -{ - return m_operatorResult; -} - -void Module::addToPanel(QWidget* vtkNotUsed(panel)) {} - -void Module::prepareToRemoveFromPanel(QWidget* vtkNotUsed(panel)) {} - -void Module::setActiveScalars(int scalars) -{ - m_activeScalars = scalars; - emit dataSourceChanged(); -} - -void Module::setUseDetachedColorMap(bool val) -{ - m_useDetachedColorMap = val; - if (isColorMapNeeded() == false) { - return; - } - - if (m_useDetachedColorMap) { - d->m_colorMap = d->detachedColorMap(); - d->m_opacityMap = d->detachedOpacityMap(); - - tomviz::rescaleColorMap(d->m_colorMap, dataSource()); - pqCoreUtilities::connect(d->m_colorMap, vtkCommand::ModifiedEvent, this, - SLOT(onColorMapChanged())); - } else { - d->m_colorMap = nullptr; - d->m_opacityMap = nullptr; - } - updateColorMap(); - emit colorMapChanged(); -} - -vtkSMProxy* Module::colorMap() const -{ - if (useDetachedColorMap()) { - return d->m_colorMap.GetPointer(); - } - - if (colorMapDataSource()) { - return colorMapDataSource()->colorMap(); - } - - return nullptr; -} - -vtkSMProxy* Module::opacityMap() const -{ - Q_ASSERT(d->m_colorMap || !m_useDetachedColorMap); - return useDetachedColorMap() ? d->m_opacityMap.GetPointer() - : dataSource()->opacityMap(); -} - -vtkPiecewiseFunction* Module::gradientOpacityMap() const -{ - vtkPiecewiseFunction* gof = useDetachedColorMap() - ? d->m_gradientOpacityMap.GetPointer() - : dataSource()->gradientOpacityMap(); - - // Set default values - const int numPoints = gof->GetSize(); - if (numPoints == 0) { - vtkColorTransferFunction* lut = - vtkColorTransferFunction::SafeDownCast(colorMap()->GetClientSideObject()); - - double range[2]; - lut->GetRange(range); - - // For gradient magnitude, the volume mapper's fragment shader expects a - // range of [0, DataMax/4]. - const double maxValue = (range[1] - range[0]) / 4.0; - gof->AddPoint(0.0, 0.0); - gof->AddPoint(maxValue, 1.0); - } - - return gof; -} - -vtkImageData* Module::transferFunction2D() const -{ - if (useDetachedColorMap()) { - return d->m_transfer2D.GetPointer(); - } - - if (colorMapDataSource()) { - return colorMapDataSource()->transferFunction2D(); - } - - return nullptr; -} - -vtkRectd* Module::transferFunction2DBox() const -{ - return useDetachedColorMap() ? d->detachedTransferFunction2DBox() - : dataSource()->transferFunction2DBox(); -} - -QJsonObject Module::serialize() const -{ - QJsonObject json; - QJsonObject props; - props["visibility"] = visibility(); - if (isColorMapNeeded()) { - json["useDetachedColorMap"] = m_useDetachedColorMap; - if (m_useDetachedColorMap) { - json["colorOpacityMap"] = tomviz::serialize(d->detachedColorMap()); - json["gradientOpacityMap"] = tomviz::serialize(gradientOpacityMap()); - QJsonObject boxJson; - auto transfer2DBox = d->detachedTransferFunction2DBox(); - boxJson["x"] = transfer2DBox->GetX(); - boxJson["y"] = transfer2DBox->GetY(); - boxJson["width"] = transfer2DBox->GetWidth(); - boxJson["height"] = transfer2DBox->GetHeight(); - json["colorMap2DBox"] = boxJson; - } - } - json["properties"] = props; - json["activeScalars"] = activeScalarsName(); - json["id"] = QString::asprintf("%p", static_cast(this)); - json["type"] = ModuleFactory::moduleType(this); - json["viewId"] = static_cast(this->view()->GetGlobalID()); - - return json; -} - -bool Module::deserialize(const QJsonObject& json) -{ - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - setVisibility(props["visibility"].toBool()); - } - - if (isColorMapNeeded() && json.contains("useDetachedColorMap")) { - bool useDetachedColorMap = json["useDetachedColorMap"].toBool(); - if (useDetachedColorMap) { - if (json.contains("colorOpacityMap")) { - auto colorMap = json["colorOpacityMap"].toObject(); - tomviz::deserialize(d->detachedColorMap(), colorMap); - } - if (json.contains("gradientOpacityMap")) { - auto gradientOpacityMap = json["gradientOpacityMap"].toObject(); - tomviz::deserialize(d->m_gradientOpacityMap, gradientOpacityMap); - } - if (json.contains("colorMap2DBox")) { - auto boxJson = json["colorMap2DBox"].toObject(); - auto transfer2DBox = d->detachedTransferFunction2DBox(); - transfer2DBox->Set(boxJson["x"].toDouble(), boxJson["y"].toDouble(), - boxJson["width"].toDouble(), - boxJson["height"].toDouble()); - } - } - setUseDetachedColorMap(useDetachedColorMap); - } - - if (json.contains("activeScalars")) { - auto activeScalarsName = json["activeScalars"].toString(); - if (activeScalarsName == Module::defaultScalarsName()) { - m_activeScalars = Module::defaultScalarsIdx(); - } else { - m_activeScalars = dataSource()->scalarsIdx(activeScalarsName); - } - } - - return true; -} - -void Module::onColorMapChanged() -{ - emit colorMapChanged(); -} - -void Module::setTransferMode(const TransferMode mode) -{ - d->m_transferMode = static_cast(mode); - this->updateColorMap(); - - emit transferModeChanged(static_cast(mode)); -} - -Module::TransferMode Module::getTransferMode() const -{ - return d->m_transferMode; -} - -vtkDataObject* Module::dataToExport() -{ - return nullptr; -} - -bool Module::setVisibility(bool val) { - emit visibilityChanged(val); - - return true; -} - -bool Module::updateClippingPlane(vtkPlane*, bool) -{ - return false; -} - -QString Module::defaultScalarsName() -{ - return "tomviz::DefaultScalars"; -} - -int Module::defaultScalarsIdx() -{ - return -1; -} - -QString Module::activeScalarsName() const -{ - if (m_activeScalars == Module::defaultScalarsIdx()) { - return Module::defaultScalarsName(); - } else { - return dataSource()->scalarsName(m_activeScalars); - } -} -} // end of namespace tomviz diff --git a/tomviz/legacy/modules/Module.h b/tomviz/legacy/modules/Module.h deleted file mode 100644 index 351651f3e..000000000 --- a/tomviz/legacy/modules/Module.h +++ /dev/null @@ -1,210 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModule_h -#define tomvizModule_h - -#include - -#include -#include -#include - -#include -#include - -class QWidget; -class vtkImageData; -class vtkPlane; -class vtkSMProxy; -class vtkSMViewProxy; -class vtkPiecewiseFunction; -class vtkDataObject; - -namespace tomviz { - -class DataSource; -class MoleculeSource; -class OperatorResult; - -/// Abstract parent class for all Modules in tomviz. -class Module : public QObject -{ - Q_OBJECT - -public: - Module(QObject* parent = nullptr); - ~Module() override; - - /// Transfer function mode (1D or 2D). This enum needs to be synchronized with - /// the order of the pages in ui->swTransferMode. - enum TransferMode - { - SCALAR = 0, - GRADIENT_1D, - GRADIENT_2D - }; - void setTransferMode(const TransferMode mode); - TransferMode getTransferMode() const; - - /// Returns a label for this module. - virtual QString label() const = 0; - - /// Returns an icon to use for this module. - virtual QIcon icon() const = 0; - - /// Initialize the module for the data source and view. This is called after a - /// new module is instantiated. Subclasses override this method to setup the - /// visualization pipeline for this module. - virtual bool initialize(DataSource* dataSource, vtkSMViewProxy* view); - virtual bool initialize(MoleculeSource* moleculeSource, vtkSMViewProxy* view); - virtual bool initialize(OperatorResult* result, vtkSMViewProxy* view); - - /// Finalize the module. Subclasses should override this method to delete and - /// release all proxies (and data) created for this module. - virtual bool finalize() = 0; - - /// Returns the visibility for the module. - virtual bool visibility() const = 0; - - /// Accessors for the data-source and view associated with this Plot. - DataSource* dataSource() const; - MoleculeSource* moleculeSource() const; - vtkSMViewProxy* view() const; - // Modules can alternatively use an OperatorResult as DataSource - OperatorResult* operatorResult() const; - - /// Some modules act on one data source and apply coloring from another data - /// source (e.g. ModuleContour). This method allows a module to designate a - /// separate data source for coloring, if desired. - virtual DataSource* colorMapDataSource() const { return dataSource(); } - - /// serialize the state of the module. - virtual QJsonObject serialize() const; - virtual bool deserialize(const QJsonObject& json); - - /// Modules that use transfer functions should override this method to return - /// true. - virtual bool isColorMapNeeded() const { return false; } - virtual bool isOpacityMapped() const { return false; } - virtual bool areScalarsMapped() const { return false; } - - /// Flag indicating whether the module uses a "detached" color map or not. - /// This is only applicable when isColorMapNeeded() return true. - void setUseDetachedColorMap(bool); - bool useDetachedColorMap() const { return m_useDetachedColorMap; } - - /// This will either return the maps from the data source or detached ones - /// based on the UseDetachedColorMap flag. - vtkSMProxy* colorMap() const; - vtkSMProxy* opacityMap() const; - vtkPiecewiseFunction* gradientOpacityMap() const; - vtkImageData* transferFunction2D() const; - vtkRectd* transferFunction2DBox() const; - - virtual bool supportsGradientOpacity() { return false; } - - /// A description of the data type that will be exported. For instance if - /// exporting a mesh, this would return "Mesh". Returning an empty string - /// indicates that this module has nothing of interest to be exported. - virtual QString exportDataTypeString() { return ""; } - - /// Returns the data to export for this visualization module. - virtual vtkDataObject* dataToExport(); - - /// Returns the active scalars of the module - int activeScalars() const { return m_activeScalars; } - QString activeScalarsName() const; - static QString defaultScalarsName(); - static int defaultScalarsIdx(); - -signals: - - /// Emitted when the transfer function mode changed in the concrete - /// module (which would require external UI components to be adjusted). - void transferModeChanged(const int mode); - -public slots: - /// Set the visibility for this module. Subclasses should override this method - /// show/hide all representations created for this module. - virtual bool setVisibility(bool val); - - bool show() { return setVisibility(true); } - bool hide() { return setVisibility(false); } - - /// This method is called add the proxies in this module to a - /// pqProxiesWidget instance. Default implementation simply adds the view - /// properties. Subclasses should override to add proxies and relevant - /// properties to the panel. - virtual void addToPanel(QWidget* panel); - - /// This method is called just prior to removing this Module's properties from - /// the panel. Subclasses can use it to perform any necessary cleanup such as - /// disconnecting some signals and slots. - virtual void prepareToRemoveFromPanel(QWidget* panel); - - /// This method is called when the data source's display position changes. - virtual void dataSourceMoved(double newX, double newY, double newZ) = 0; - - /// This method is called when the data source's display orientation changes. - virtual void dataSourceRotated(double newX, double newY, double newZ) = 0; - - // This method is called when the active scalars for the module change - virtual void setActiveScalars(int scalars); - - /// Subclasses should override this method in order to use the clipping - /// plane on a module. - virtual bool updateClippingPlane(vtkPlane* plane, bool newFilter); - -protected: - /// Modules that use transfer functions for color/opacity should override this - /// method to set the color map on appropriate representations. This will be - /// called when the color map proxy is changed, for example, when - /// setUseDetachedColorMap is toggled. - virtual void updateColorMap() {} - -signals: - /// Emitted when the represented DataSource is updated. - void dataSourceChanged(); - - /// Emitted when the UseDetachedColorMap state changes or the detached color - /// map is modified - void colorMapChanged(); - - /// Emitted when the module properties are changed in a way that would require - /// a re-render of the scene to take effect. - void renderNeeded(); - - /// Emitted when the client side view needs to be updated (such as geometry - /// bounds being re-computed), but this was not done automatically. Most - /// widgets don't need this, but the slice module does for some reason. - void updateClientSideViewNeeded(); - - /// Emitted when the mouse is over a voxel - void mouseOverVoxel(const vtkVector3i& ijk, double v); - - /// Emitted when the module explicitly requires the opacity of the color map - /// to be enforced. This will cause the colormap to be detached, and the - /// "Separate Color Map" box to be checked and disabled. In practice, this is - /// only useful to make Orthogonal and regular slices transparent. - void opacityEnforced(bool); - - void visibilityChanged(bool); - -private slots: - void onColorMapChanged(); - -private: - Q_DISABLE_COPY(Module) - QPointer m_activeDataSource; - QPointer m_activeMoleculeSource; - QPointer m_operatorResult; - vtkWeakPointer m_view; - bool m_useDetachedColorMap = false; - - class MInternals; - const QScopedPointer d; - int m_activeScalars; -}; -} // namespace tomviz -#endif diff --git a/tomviz/legacy/modules/ModuleClip.cxx b/tomviz/legacy/modules/ModuleClip.cxx deleted file mode 100644 index 7dc890404..000000000 --- a/tomviz/legacy/modules/ModuleClip.cxx +++ /dev/null @@ -1,764 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleClip.h" - -#include "../DataSource.h" -#include "DoubleSliderWidget.h" -#include "IntSliderWidget.h" -#include "ModuleManager.h" -#include "Utilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -ModuleClip::ModuleClip(QObject* parentObject) : Module(parentObject) {} - -ModuleClip::~ModuleClip() -{ - finalize(); -} - -QIcon ModuleClip::icon() const -{ - return QIcon(":/pqWidgets/Icons/pqClip.svg"); -} - -bool ModuleClip::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - if (!Module::initialize(data, vtkView)) { - return false; - } - - vtkNew controller; - auto producer = data->proxy(); - auto pxm = producer->GetSessionProxyManager(); - - vtkSmartPointer proxy; - proxy.TakeReference(pxm->NewProxy("filters", "PassThrough")); - - // Create the Properties panel proxy - m_propsPanelProxy.TakeReference( - pxm->NewProxy("tomviz_proxies", "NonOrthogonalClip")); - - pqCoreUtilities::connect(m_propsPanelProxy, vtkCommand::PropertyModifiedEvent, - this, SLOT(onPropertyChanged())); - - m_clip = vtkSMSourceProxy::SafeDownCast(proxy); - Q_ASSERT(m_clip); - controller->PreInitializeProxy(m_clip); - vtkSMPropertyHelper(m_clip, "Input").Set(producer); - controller->PostInitializeProxy(m_clip); - controller->RegisterPipelineProxy(m_clip); - - // Give the proxy a friendly name for the GUI/Python world. - if (auto p = convert(proxy)) { - p->rename(label()); - } - - const bool widgetSetup = setupWidget(vtkView); - - if (widgetSetup) { - m_widget->SetDisplayOffset(data->displayPosition()); - m_widget->SetDisplayOrientation(data->displayOrientation()); - m_widget->On(); - onDirectionChanged(m_direction); - pqCoreUtilities::connect(m_widget, vtkCommand::InteractionEvent, this, - SLOT(onPlaneChanged())); - connect(data, &DataSource::dataChanged, this, &ModuleClip::dataUpdated); - foreach (Module* module, - ModuleManager::instance().findModulesGeneric(data, nullptr)) { - if (module->dataSource() == data) { - connect(this, &ModuleClip::clipFilterUpdated, module, - &Module::updateClippingPlane); - } - } - connect(&ModuleManager::instance(), &ModuleManager::moduleAdded, this, - [this, data](Module* module) { - if (module->dataSource() == data) { - connect(this, &ModuleClip::clipFilterUpdated, - module, &Module::updateClippingPlane); - emit clipFilterUpdated(m_clippingPlane, false); - } - }); - } - - Q_ASSERT(m_widget); - return widgetSetup; -} - -// Should only be called from initialize after Clip has been setup -bool ModuleClip::setupWidget(vtkSMViewProxy* vtkView) -{ - auto clipAlg = vtkAlgorithm::SafeDownCast(m_clip->GetClientSideObject()); - - vtkRenderWindowInteractor* rwi = vtkView->GetRenderWindow()->GetInteractor(); - - if (!rwi || !clipAlg) { - return false; - } - - m_widget = vtkSmartPointer::New(); - m_widget->SetLookupTable(createLookupTable()); - double color[3] = { 0.0 }; - m_widget->GetTexturePlaneProperty()->SetColor(color); - - // Set the interactor on the widget to be what the current - // render window is using. - m_widget->SetInteractor(rwi); - - m_clippingPlane = vtkSmartPointer::New(); - m_clippingPlane->SetOrigin(m_widget->GetCenter()); - m_clippingPlane->SetNormal(m_widget->GetNormal()); - - m_widget->SetInputConnection(clipAlg->GetOutputPort()); - - Q_ASSERT(rwi); - Q_ASSERT(clipAlg); - onPlaneChanged(); - return true; -} - -void ModuleClip::updatePlaneWidget() -{ - if (!m_planeSlider || !imageData()) - return; - - int dims[3]; - imageData()->GetDimensions(dims); - - int axis = directionAxis(m_direction); - int maxPlane = dims[axis] - 1; - - m_planeSlider->setMinimum(0); - m_planeSlider->setMaximum(maxPlane); -} - -bool ModuleClip::finalize() -{ - emit clipFilterUpdated(m_clippingPlane, true); - vtkNew controller; - controller->UnRegisterProxy(m_clip); - - m_clip = nullptr; - - if (m_widget != nullptr) { - m_widget->Off(); - } - - return true; -} - -bool ModuleClip::setVisibility(bool val) -{ - Q_ASSERT(m_widget); - m_widget->SetEnabled(val ? 1 : 0); - // If the state of the plane is set to visible, update the state of the arrow - // as well since it cannot update when the widget is not enabled - vtkSMPropertyHelper showPlaneProperty(m_propsPanelProxy, "ShowPlane"); - if (val && showPlaneProperty.GetAsInt()) { - vtkSMPropertyHelper showArrowProperty(m_propsPanelProxy, "ShowArrow"); - // Not this: it hides the plane as well as the arrow... - // Widget->SetEnabled(showArrowProperty.GetAsInt()); - m_widget->SetArrowVisibility(showArrowProperty.GetAsInt()); - m_widget->SetInteraction(showArrowProperty.GetAsInt()); - m_widget->SetTextureVisibility(showPlaneProperty.GetAsInt()); - } - val ? emit clipFilterUpdated(m_clippingPlane, false) : emit clipFilterUpdated(m_clippingPlane, true); - return true; -} - -bool ModuleClip::visibility() const -{ - if (m_widget) { - return m_widget->GetEnabled() != 0; - } else { - return false; - } -} - -void ModuleClip::addToPanel(QWidget* panel) -{ - if (panel->layout()) { - delete panel->layout(); - } - - QVBoxLayout* layout = new QVBoxLayout; - QFormLayout* formLayout = new QFormLayout; - - QWidget* container = new QWidget; - container->setLayout(formLayout); - layout->addWidget(container); - formLayout->setContentsMargins(0, 0, 0, 0); - - m_opacitySlider = new DoubleSliderWidget(true); - m_opacitySlider->setLineEditWidth(50); - m_opacitySlider->setMinimum(0); - m_opacitySlider->setMaximum(1); - m_opacitySlider->setValue(m_opacity); - onOpacityChanged(m_opacity); - formLayout->addRow("Opacity", m_opacitySlider); - - connect(m_opacitySlider, &DoubleSliderWidget::valueEdited, this, - &ModuleClip::onOpacityChanged); - connect(m_opacitySlider, &DoubleSliderWidget::valueChanged, this, - &ModuleClip::onOpacityChanged); - - m_colorSelector = new pqColorChooserButton(panel); - m_colorSelector->setShowAlphaChannel(false); - m_colorSelector->setChosenColor(m_planeColor); - formLayout->addRow("Select Color", m_colorSelector); - - connect(m_colorSelector, &pqColorChooserButton::chosenColorChanged, this, - &ModuleClip::onUpdateColor); - connect(m_colorSelector, &pqColorChooserButton::chosenColorChanged, this, - &ModuleClip::dataUpdated); - - QHBoxLayout* displayRowLayout = new QHBoxLayout; - QCheckBox* showPlane = new QCheckBox("Show Plane"); - displayRowLayout->addWidget(showPlane); - - m_Links.addPropertyLink(showPlane, "checked", SIGNAL(toggled(bool)), - m_propsPanelProxy, - m_propsPanelProxy->GetProperty("ShowPlane"), 0); - connect(showPlane, &QCheckBox::toggled, this, &ModuleClip::dataUpdated); - - QCheckBox* showArrow = new QCheckBox("Show Arrow"); - displayRowLayout->addWidget(showArrow); - formLayout->addRow(displayRowLayout); - - m_Links.addPropertyLink(showArrow, "checked", SIGNAL(toggled(bool)), - m_propsPanelProxy, - m_propsPanelProxy->GetProperty("ShowArrow"), 0); - connect(showArrow, &QCheckBox::toggled, this, &ModuleClip::dataUpdated); - connect(showPlane, &QCheckBox::toggled, showArrow, &QCheckBox::setEnabled); - - QCheckBox* invertPlane = new QCheckBox("Invert Plane Direction"); - formLayout->addRow(invertPlane); - - m_Links.addPropertyLink(invertPlane, "checked", SIGNAL(toggled(bool)), - m_propsPanelProxy, - m_propsPanelProxy->GetProperty("InvertPlane"), 0); - connect(invertPlane, &QCheckBox::toggled, this, - &ModuleClip::onInvertPlaneChanged); - connect(invertPlane, &QCheckBox::toggled, this, &ModuleClip::dataUpdated); - - m_directionCombo = new QComboBox(); - m_directionCombo->addItem("XY Plane", QVariant(Direction::XY)); - m_directionCombo->addItem("YZ Plane", QVariant(Direction::YZ)); - m_directionCombo->addItem("XZ Plane", QVariant(Direction::XZ)); - m_directionCombo->addItem("Custom", QVariant(Direction::Custom)); - m_directionCombo->setCurrentIndex(static_cast(m_direction)); - formLayout->addRow("Direction", m_directionCombo); - - m_planeSlider = new IntSliderWidget(true); - m_planeSlider->setLineEditWidth(50); - m_planeSlider->setPageStep(1); - m_planeSlider->setMinimum(0); - int axis = directionAxis(m_direction); - bool isOrtho = axis >= 0; - if (isOrtho) { - int dims[3]; - imageData()->GetDimensions(dims); - m_planeSlider->setMaximum(dims[axis] - 1); - } - - // Sanity check: make sure the plane value is within the bounds - if (m_planePosition < m_planeSlider->minimum()) - m_planePosition = m_planeSlider->minimum(); - else if (m_planePosition > m_planeSlider->maximum()) - m_planePosition = m_planeSlider->maximum(); - - m_planeSlider->setValue(m_planePosition); - - formLayout->addRow("Plane", m_planeSlider); - - auto line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - formLayout->addRow(line); - - QLabel* label = new QLabel("Point on Plane"); - layout->addWidget(label); - QHBoxLayout* row = new QHBoxLayout; - const char* labels[] = { "X:", "Y:", "Z:" }; - for (int i = 0; i < 3; ++i) { - label = new QLabel(labels[i]); - row->addWidget(label); - pqLineEdit* inputBox = new pqLineEdit; - inputBox->setEnabled(!isOrtho); - inputBox->setValidator(new QDoubleValidator(inputBox)); - m_Links.addPropertyLink( - inputBox, "text2", SIGNAL(textChanged(const QString&)), m_propsPanelProxy, - m_propsPanelProxy->GetProperty("PointOnPlane"), i); - connect(inputBox, &pqLineEdit::textChangedAndEditingFinished, this, - &ModuleClip::dataUpdated); - row->addWidget(inputBox); - m_pointInputs[i] = inputBox; - } - layout->addItem(row); - - label = new QLabel("Plane Normal"); - layout->addWidget(label); - row = new QHBoxLayout; - for (int i = 0; i < 3; ++i) { - label = new QLabel(labels[i]); - row->addWidget(label); - pqLineEdit* inputBox = new pqLineEdit; - inputBox->setEnabled(!isOrtho); - inputBox->setValidator(new QDoubleValidator(inputBox)); - m_Links.addPropertyLink( - inputBox, "text2", SIGNAL(textChanged(const QString&)), m_propsPanelProxy, - m_propsPanelProxy->GetProperty("PlaneNormal"), i); - connect(inputBox, &pqLineEdit::textChangedAndEditingFinished, this, - &ModuleClip::dataUpdated); - row->addWidget(inputBox); - m_normalInputs[i] = inputBox; - } - layout->addItem(row); - - layout->addStretch(); - - panel->setLayout(layout); - - connect(m_directionCombo, QOverload::of(&QComboBox::currentIndexChanged), - this, [this](int idx) { - Direction dir = m_directionCombo->itemData(idx).value(); - onDirectionChanged(dir); - }); - - connect(m_planeSlider, &IntSliderWidget::valueEdited, this, - QOverload::of(&ModuleClip::onPlaneChanged)); - connect(m_planeSlider, &IntSliderWidget::valueChanged, this, - QOverload::of(&ModuleClip::onPlaneChanged)); -} - -void ModuleClip::dataUpdated() -{ - m_Links.accept(); - // In case there are new planes, update min and max - updatePlaneWidget(); - m_widget->UpdatePlacement(); - emit renderNeeded(); -} - -QJsonObject ModuleClip::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - vtkSMPropertyHelper showArrowProperty(m_propsPanelProxy, "ShowArrow"); - props["showArrow"] = showArrowProperty.GetAsInt() != 0; - - vtkSMPropertyHelper showPlaneProperty(m_propsPanelProxy, "ShowPlane"); - props["showPlane"] = showPlaneProperty.GetAsInt() != 0; - - vtkSMPropertyHelper invertPlaneProperty(m_propsPanelProxy, "InvertPlane"); - props["invertPlane"] = invertPlaneProperty.GetAsInt() != 0; - - QJsonArray selection = { m_planeColor.redF(), m_planeColor.greenF(), - m_planeColor.blueF() }; - props["selectedColor"] = selection; - props["opacity"] = m_opacity; - - // Serialize the plane - double point[3]; - m_widget->GetOrigin(point); - QJsonArray origin = { point[0], point[1], point[2] }; - m_widget->GetPoint1(point); - QJsonArray point1 = { point[0], point[1], point[2] }; - m_widget->GetPoint2(point); - QJsonArray point2 = { point[0], point[1], point[2] }; - - props["origin"] = origin; - props["point1"] = point1; - props["point2"] = point2; - - props["plane"] = m_planePosition; - QVariant qData; - qData.setValue(m_direction); - props["direction"] = qData.toString(); - - json["properties"] = props; - return json; -} - -bool ModuleClip::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - vtkSMPropertyHelper showArrowProperty(m_propsPanelProxy, "ShowArrow"); - showArrowProperty.Set(props["showArrow"].toBool() ? 1 : 0); - vtkSMPropertyHelper showPlaneProperty(m_propsPanelProxy, "ShowPlane"); - showPlaneProperty.Set(props["showPlane"].toBool() ? 1 : 0); - vtkSMPropertyHelper invertPlaneProperty(m_propsPanelProxy, "InvertPlane"); - invertPlaneProperty.Set(props["invertPlane"].toBool() ? 1 : 0); - if (props.contains("origin") && props.contains("point1") && - props.contains("point2")) { - auto o = props["origin"].toArray(); - auto p1 = props["point1"].toArray(); - auto p2 = props["point2"].toArray(); - double origin[3] = { o[0].toDouble(), o[1].toDouble(), o[2].toDouble() }; - double point1[3] = { p1[0].toDouble(), p1[1].toDouble(), - p1[2].toDouble() }; - double point2[3] = { p2[0].toDouble(), p2[1].toDouble(), - p2[2].toDouble() }; - m_widget->SetOrigin(origin); - m_widget->SetPoint1(point1); - m_widget->SetPoint2(point2); - } - if (props.contains("selectedColor")) { - auto selection = props["selectedColor"].toArray(); - double color[3] = { selection[0].toDouble(), selection[1].toDouble(), - selection[2].toDouble() }; - if (m_colorSelector) { - setPlaneColor(color); - } - } - if (props.contains("opacity")) { - m_opacity = props["opacity"].toDouble(); - onOpacityChanged(m_opacity); - if (m_opacitySlider) { - m_opacitySlider->setValue(m_opacity); - } - } - - m_widget->UpdatePlacement(); - // If deserializing a former OrthogonalPlane, the direction is encoded in - // the property "planeMode" as an int - if (props.contains("planeMode")) { - Direction direction = modeToDirection(props["planeMode"].toInt()); - onDirectionChanged(direction); - } - if (props.contains("direction")) { - Direction direction = stringToDirection(props["direction"].toString()); - onDirectionChanged(direction); - } - if (props.contains("plane")) { - m_planePosition = props["plane"].toInt(); - onPlaneChanged(m_planePosition); - } - onPlaneChanged(); - return true; - } - return false; -} - -void ModuleClip::onPropertyChanged() -{ - // Avoid recursive clobbering of the plane position - if (m_ignoreSignals) { - return; - } - m_ignoreSignals = true; - vtkSMPropertyHelper showPlaneProperty(m_propsPanelProxy, "ShowPlane"); - if (m_widget->GetEnabled()) { - m_widget->SetTextureVisibility(showPlaneProperty.GetAsInt()); - m_widget->SetArrowVisibility(showPlaneProperty.GetAsInt()); - } - vtkSMPropertyHelper showArrowProperty(m_propsPanelProxy, "ShowArrow"); - if (m_widget->GetEnabled() && showPlaneProperty.GetAsInt()) { - // Not this: it hides the plane as well as the arrow... - // Widget->SetEnabled(showArrowProperty.GetAsInt()); - m_widget->SetArrowVisibility(showArrowProperty.GetAsInt()); - m_widget->SetInteraction(showArrowProperty.GetAsInt()); - } - vtkSMPropertyHelper pointProperty(m_propsPanelProxy, "PointOnPlane"); - std::vector centerPoint = pointProperty.GetDoubleArray(); - m_widget->SetCenter(¢erPoint[0]); - m_clippingPlane->SetOrigin(¢erPoint[0]); - vtkSMPropertyHelper normalProperty(m_propsPanelProxy, "PlaneNormal"); - std::vector normalVector = normalProperty.GetDoubleArray(); - m_widget->UpdatePlacement(); - m_ignoreSignals = false; - - if (m_widget->GetEnabled()) { - emit clipFilterUpdated(m_clippingPlane, false); - } -} - -void ModuleClip::onPlaneChanged() -{ - // Avoid recursive clobbering of the plane position - if (m_ignoreSignals) { - return; - } - m_ignoreSignals = true; - vtkSMPropertyHelper pointProperty(m_propsPanelProxy, "PointOnPlane"); - double* centerPoint = m_widget->GetCenter(); - pointProperty.Set(centerPoint, 3); - m_clippingPlane->SetOrigin(centerPoint); - vtkSMPropertyHelper normalProperty(m_propsPanelProxy, "PlaneNormal"); - double* normalVector = m_widget->GetNormal(); - normalProperty.Set(normalVector, 3); - m_clippingPlane->SetNormal(normalVector); - - // Adjust the plane slider if the plane has changed from dragging the arrow - onPlaneChanged(centerPoint); - - m_ignoreSignals = false; -} - -void ModuleClip::dataSourceMoved(double newX, double newY, double newZ) -{ - double pos[3] = { newX, newY, newZ }; - m_widget->SetDisplayOffset(pos); - // FIXME: we need to get m_clippingPlane to be aware of non-default - // data source origins. -} - -void ModuleClip::dataSourceRotated(double newX, double newY, double newZ) -{ - double orientation[3] = { newX, newY, newZ }; - m_widget->SetDisplayOrientation(orientation); - // FIXME: we need to get m_clippingPlane to be aware of non-default - // data source orientations. -} - -vtkImageData* ModuleClip::imageData() const -{ - vtkImageData* data = vtkImageData::SafeDownCast( - dataSource()->producer()->GetOutputDataObject(0)); - Q_ASSERT(data); - return data; -} - -void ModuleClip::onDirectionChanged(Direction direction) -{ - m_direction = direction; - int axis = directionAxis(direction); - - bool isOrtho = axis >= 0; - - for (int i = 0; i < 3; ++i) { - if (m_pointInputs[i]) { - m_pointInputs[i]->setEnabled(!isOrtho); - } - if (m_normalInputs[i]) { - m_normalInputs[i]->setEnabled(!isOrtho); - } - } - if (m_planeSlider) { - m_planeSlider->setVisible(isOrtho); - } - - m_widget->SetPlaneOrientation(axis); - - if (m_directionCombo) { - if (direction != m_directionCombo->currentData().value()) { - for (int i = 0; i < m_directionCombo->count(); ++i) { - Direction data = m_directionCombo->itemData(i).value(); - if (data == direction) { - m_directionCombo->setCurrentIndex(i); - } - } - } - } - - if (!isOrtho) { - emit clipFilterUpdated(m_clippingPlane, false); - return; - } - - int dims[3]; - imageData()->GetDimensions(dims); - - double normal[3] = { 0, 0, 0 }; - int planePosition = 1; - int maxPlane = dims[axis] - 1;; - - vtkSMPropertyHelper invertPlaneProperty(m_propsPanelProxy, "InvertPlane"); - if (invertPlaneProperty.GetAsInt() && isOrtho) { - normal[axis] = -1; - planePosition = maxPlane; - } else { - normal[axis] = 1; - } - - m_widget->SetNormal(normal); - if (m_planeSlider) { - m_planeSlider->setMinimum(0); - m_planeSlider->setMaximum(maxPlane); - } - - onPlaneChanged(planePosition); - onPlaneChanged(); - dataUpdated(); - - emit clipFilterUpdated(m_clippingPlane, false); -} - -void ModuleClip::onPlaneChanged(int planePosition) -{ - m_planePosition = planePosition; - int axis = directionAxis(m_direction); - - if (axis < 0) { - return; - } - - m_widget->SetSliceIndex(planePosition); - if (m_planeSlider) { - m_planeSlider->setValue(planePosition); - } - - onPlaneChanged(); - dataUpdated(); - - emit clipFilterUpdated(m_clippingPlane, false); -} - -void ModuleClip::onPlaneChanged(double* point) -{ - int axis = directionAxis(m_direction); - if (axis < 0) { - return; - } - - int dims[3]; - imageData()->GetDimensions(dims); - double bounds[6]; - imageData()->GetBounds(bounds); - int planePosition; - - planePosition = (dims[axis] - 1) * (point[axis] - bounds[2 * axis]) / - (bounds[2 * axis + 1] - bounds[2 * axis]); - - onPlaneChanged(planePosition); - - emit clipFilterUpdated(m_clippingPlane, false); -} - -void ModuleClip::onInvertPlaneChanged() -{ - double normal[3]; - m_widget->GetNormal(normal); - for (auto i = 0; i < 3; ++i) { - normal[i] *= -1; - } - m_widget->SetNormal(normal); - m_clippingPlane->SetNormal(normal); - onPropertyChanged(); -} - -void ModuleClip::onUpdateColor(const QColor& color) -{ - double rgb[3] = { color.redF(), color.greenF(), color.blueF() }; - m_widget->GetTexturePlaneProperty()->SetColor(rgb); - m_planeColor.setRgbF(rgb[0], rgb[1], rgb[2]); - emit renderNeeded(); -} - -void ModuleClip::setPlaneColor(double rgb[3]) -{ - QColor color(static_cast(rgb[0] * 255.0 + 0.5), - static_cast(rgb[1] * 255.0 + 0.5), - static_cast(rgb[2] * 255.0 + 0.5)); - m_colorSelector->setChosenColor(color); - onUpdateColor(color); -} - -void ModuleClip::onOpacityChanged(double opacity) -{ - m_opacity = opacity; - m_widget->SetOpacity(opacity); - emit renderNeeded(); -} - -vtkScalarsToColors* ModuleClip::createLookupTable() -{ - vtkLookupTable* lut = vtkLookupTable::New(); - lut->Register(m_widget); - lut->Delete(); - lut->SetNumberOfColors(256); - lut->SetSaturationRange(0, 0); - lut->SetValueRange(1, 1); - lut->Build(); - return lut; -} - -int ModuleClip::directionAxis(Direction direction) -{ - switch (direction) { - case Direction::XY: { - return 2; - } - case Direction::YZ: { - return 0; - } - case Direction::XZ: { - return 1; - } - default: { - return -1; - } - } -} - -ModuleClip::Direction ModuleClip::stringToDirection(const QString& name) -{ - if (name == "XY") { - return Direction::XY; - } else if (name == "YZ") { - return Direction::YZ; - } else if (name == "XZ") { - return Direction::XZ; - } else { - return Direction::Custom; - } -} - -ModuleClip::Direction ModuleClip::modeToDirection(int planeMode) -{ - switch (planeMode) { - case 5: { - return Direction::XY; - } - case 6: { - return Direction::YZ; - } - case 7: { - return Direction::XZ; - } - default: { - return Direction::Custom; - } - } -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleClip.h b/tomviz/legacy/modules/ModuleClip.h deleted file mode 100644 index bbbd96151..000000000 --- a/tomviz/legacy/modules/ModuleClip.h +++ /dev/null @@ -1,114 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleClip_h -#define tomvizModuleClip_h - -#include "Module.h" - -#include -#include - -#include - -class QComboBox; -class pqColorChooserButton; -class pqLineEdit; -class vtkPlane; -class vtkScalarsToColors; -class vtkSMProxy; -class vtkSMSourceProxy; -class vtkNonOrthoImagePlaneWidget; - -namespace tomviz { - -class IntSliderWidget; -class DoubleSliderWidget; - -class ModuleClip : public Module -{ - Q_OBJECT - -public: - ModuleClip(QObject* parent = nullptr); - virtual ~ModuleClip(); - - QString label() const override { return "Clip"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - bool finalize() override; - bool setVisibility(bool val) override; - bool visibility() const override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - void addToPanel(QWidget* panel) override; - - void dataSourceMoved(double newX, double newY, double newZ) override; - void dataSourceRotated(double newX, double newY, double newZ) override; - - QString exportDataTypeString() override { return "Image"; } - - enum Direction - { - XY = 0, - YZ = 1, - XZ = 2, - Custom = 3 - }; - Q_ENUM(Direction) - -signals: - void clipFilterUpdated(vtkPlane*, bool); - -protected: - void updatePlaneWidget(); - static Direction stringToDirection(const QString& name); - static Direction modeToDirection(int planeMode); - vtkImageData* imageData() const; - vtkScalarsToColors* createLookupTable(); - -private slots: - void onPropertyChanged(); - void onPlaneChanged(); - void onInvertPlaneChanged(); - - void dataUpdated(); - void onUpdateColor(const QColor& color); - void setPlaneColor(double rgb[3]); - - void onOpacityChanged(double opacity); - void onDirectionChanged(Direction direction); - void onPlaneChanged(int plane); - void onPlaneChanged(double* point); - int directionAxis(Direction direction); - -private: - // Should only be called from initialize after the ClipFilter has been setup. - bool setupWidget(vtkSMViewProxy* view); - - Q_DISABLE_COPY(ModuleClip) - - vtkWeakPointer m_clip; - vtkSmartPointer m_propsPanelProxy; - vtkSmartPointer m_widget; - vtkSmartPointer m_clippingPlane; - bool m_ignoreSignals = false; - - pqPropertyLinks m_Links; - - QPointer m_directionCombo; - QPointer m_planeSlider; - Direction m_direction = Direction::XY; - int m_planePosition = 0; - QPointer m_opacitySlider; - double m_opacity = 0.1; - QColor m_planeColor = QColor(0, 0, 0); - - QPointer m_colorSelector; - QPointer m_pointInputs[3]; - QPointer m_normalInputs[3]; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleContour.cxx b/tomviz/legacy/modules/ModuleContour.cxx deleted file mode 100644 index fe391ed76..000000000 --- a/tomviz/legacy/modules/ModuleContour.cxx +++ /dev/null @@ -1,611 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleContour.h" -#include "ModuleContourWidget.h" - -#include "../DataSource.h" - -#include "vtkActiveScalarsProducer.h" -#include "vtkActor.h" -#include "vtkColorTransferFunction.h" -#include "vtkDataSetMapper.h" -#include "vtkFlyingEdges3D.h" -#include "vtkPVRenderView.h" -#include "vtkPointData.h" -#include "vtkProbeFilter.h" -#include "vtkProperty.h" -#include "vtkSMViewProxy.h" - -#include -#include -#include - -#include - -namespace tomviz { - -class ModuleContour::Private -{ -public: - bool ColorByArray = false; - bool UseSolidColor = false; - QString ColorArrayName; - vtkNew ColorArrayProducer; - vtkNew ContourArrayProducer; -}; - -ModuleContour::ModuleContour(QObject* parentObject) : Module(parentObject) -{ - d = new Private; -} - -ModuleContour::~ModuleContour() -{ - finalize(); - - delete d; - d = nullptr; -} - -QIcon ModuleContour::icon() const -{ - return QIcon(":pqWidgets/Icons/pqIsosurface.svg"); -} - -bool ModuleContour::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - if (!Module::initialize(data, vtkView)) { - return false; - } - - d->ColorArrayName = data->activeScalars(); - - updateContourArrayProducer(); - - m_flyingEdges->SetInputConnection(d->ContourArrayProducer->GetOutputPort()); - resetIsoValue(); - - auto* displayPosition = data->displayPosition(); - m_actor->SetPosition(displayPosition[0], displayPosition[1], - displayPosition[2]); - auto* displayOrientation = data->displayOrientation(); - m_actor->SetOrientation(displayOrientation[0], displayOrientation[1], - displayOrientation[2]); - - m_mapper->SetInputConnection(m_flyingEdges->GetOutputPort()); - m_mapper->SetScalarModeToUsePointFieldData(); - onColorMapDataToggled(true); - updateColorMap(); - m_actor->SetMapper(m_mapper); - - m_actor->SetProperty(m_property); - m_property->SetSpecularPower(100); - - m_view = vtkPVRenderView::SafeDownCast(vtkView->GetClientSideView()); - m_view->AddPropToRenderer(m_actor); - m_view->Update(); - - connect(data, &DataSource::dataChanged, this, &ModuleContour::onDataChanged); - connect(data, &DataSource::dataPropertiesChanged, this, - &ModuleContour::onDataPropertiesChanged); - connect(data, &DataSource::activeScalarsChanged, this, - &ModuleContour::onActiveScalarsChanged); - - emit renderNeeded(); - return true; -} - -bool ModuleContour::finalize() -{ - if (m_view) { - m_view->RemovePropFromRenderer(m_actor); - } - return true; -} - -void ModuleContour::onDataChanged() -{ - // FIXME: Implementing the vtkActiveScalarsProducer as a producer breaks the - // vtk pipeline, requiring manual updates to keep in sync like here. - // It really should be implemented as a filter. - onDataPropertiesChanged(); -} - -void ModuleContour::onDataPropertiesChanged() -{ - updateContourArrayProducer(); - updateContourByArrayOptions(); - updateColorArrayProducer(); - updateColorByArrayOptions(); -} - -void ModuleContour::onActiveScalarsChanged() -{ - // We only need to update if we are using the default option - if (activeScalars() != Module::defaultScalarsIdx()) - return; - - updateContourArrayProducer(); - updateIsoRange(); - resetIsoValue(); - updateColorMap(); - emit renderNeeded(); -} - -void ModuleContour::updateContourArrayProducer() -{ - d->ContourArrayProducer->SetOutput(dataSource()->dataObject()); - auto name = contourByArrayName().toStdString(); - d->ContourArrayProducer->SetActiveScalars(name.c_str()); -} - -void ModuleContour::updateColorArrayProducer() -{ - if (!d->ColorByArray) { - clearColorArrayProducer(); - return; - } - - d->ColorArrayProducer->SetOutput(dataSource()->dataObject()); - auto activeName = colorByArrayName().toStdString(); - d->ColorArrayProducer->SetActiveScalars(activeName.c_str()); -} - -void ModuleContour::clearColorArrayProducer() -{ - d->ColorArrayProducer->SetOutput(nullptr); -} - -void ModuleContour::updateColorMap() -{ - auto* lut = - vtkColorTransferFunction::SafeDownCast(colorMap()->GetClientSideObject()); - m_mapper->SetLookupTable(lut); - - updateColorArray(); -} - -void ModuleContour::updateColorArray() -{ - std::string name; - if (colorByArray()) { - name = colorByArrayName().toStdString(); - } else if (useSolidColor()) { - // Have to set the color array to "" to use a solid color - // Diffuse color should already be set on the property - name = ""; - } else { - name = contourByArrayName().toStdString(); - } - - m_mapper->SelectColorArray(name.c_str()); -} - -bool ModuleContour::setVisibility(bool val) -{ - m_actor->SetVisibility(val ? 1 : 0); - Module::setVisibility(val); - return true; -} - -bool ModuleContour::visibility() const -{ - return m_actor->GetVisibility() == 1; -} - -void ModuleContour::addToPanel(QWidget* panel) -{ - if (panel->layout()) - delete panel->layout(); - - QVBoxLayout* layout = new QVBoxLayout; - panel->setLayout(layout); - - // Create, update and connect - m_controllers = new ModuleContourWidget; - layout->addWidget(m_controllers); - - updatePanel(); - - connect(m_controllers, &ModuleContourWidget::colorMapDataToggled, this, - &ModuleContour::onColorMapDataToggled); - connect(m_controllers, &ModuleContourWidget::ambientChanged, this, - &ModuleContour::onAmbientChanged); - connect(m_controllers, &ModuleContourWidget::diffuseChanged, this, - &ModuleContour::onDiffuseChanged); - connect(m_controllers, &ModuleContourWidget::specularChanged, this, - &ModuleContour::onSpecularChanged); - connect(m_controllers, &ModuleContourWidget::specularPowerChanged, this, - &ModuleContour::onSpecularPowerChanged); - connect(m_controllers, &ModuleContourWidget::isoChanged, this, - &ModuleContour::onIsoChanged); - connect(m_controllers, &ModuleContourWidget::representationChanged, this, - &ModuleContour::onRepresentationChanged); - connect(m_controllers, &ModuleContourWidget::opacityChanged, this, - &ModuleContour::onOpacityChanged); - connect(m_controllers, &ModuleContourWidget::colorChanged, this, - &ModuleContour::onColorChanged); - connect(m_controllers, &ModuleContourWidget::useSolidColorToggled, this, - &ModuleContour::onUseSolidColorToggled); - connect(m_controllers, &ModuleContourWidget::contourByArrayValueChanged, this, - &ModuleContour::onContourByArrayValueChanged); - connect(m_controllers, &ModuleContourWidget::colorByArrayToggled, this, - &ModuleContour::onColorByArrayToggled); - connect(m_controllers, &ModuleContourWidget::colorByArrayNameChanged, this, - &ModuleContour::onColorByArrayNameChanged); -} - -void ModuleContour::updatePanel() -{ - if (!m_controllers) - return; - - QSignalBlocker blocker(m_controllers); - - updateIsoRange(); - updateContourByArrayOptions(); - updateColorByArrayOptions(); - - m_controllers->setColorMapData(colorMapData()); - m_controllers->setAmbient(ambient()); - m_controllers->setDiffuse(diffuse()); - m_controllers->setSpecular(specular()); - m_controllers->setSpecularPower(specularPower()); - m_controllers->setIso(iso()); - m_controllers->setRepresentation(representation()); - m_controllers->setOpacity(opacity()); - m_controllers->setColor(color()); - m_controllers->setUseSolidColor(useSolidColor()); - m_controllers->setContourByArrayValue(activeScalars()); - m_controllers->setColorByArray(colorByArray()); - m_controllers->setColorByArrayName(colorByArrayName()); -} - -namespace { - -void getRange(vtkAlgorithm* alg, double range[2]) -{ - for (int i = 0; i < 2; ++i) - range[i] = 0.0; - - if (!alg) - return; - - auto* data = vtkDataSet::SafeDownCast(alg->GetOutputDataObject(0)); - if (!data) - return; - - auto* arrayPtr = data->GetPointData()->GetScalars(); - if (!arrayPtr) - return; - - arrayPtr->GetFiniteRange(range, -1); -} - -} // namespace - -void ModuleContour::updateIsoRange() -{ - if (!m_controllers) - return; - - double range[2]; - getRange(d->ContourArrayProducer, range); - m_controllers->setIsoRange(range); -} - -void ModuleContour::updateContourByArrayOptions() -{ - if (!m_controllers) - return; - - QSignalBlocker blocker(m_controllers); - m_controllers->setContourByArrayOptions(dataSource(), this); -} - -void ModuleContour::updateColorByArrayOptions() -{ - if (!m_controllers) - return; - - QSignalBlocker blocker(m_controllers); - QStringList options = dataSource()->listScalars(); - m_controllers->setColorByArrayOptions(options); - - if (!options.contains(colorByArrayName())) - onColorByArrayNameChanged(dataSource()->activeScalars()); - - m_controllers->setColorByArrayName(colorByArrayName()); -} - -void ModuleContour::resetIsoValue() -{ - double range[2]; - getRange(d->ContourArrayProducer, range); - - // Use 2/3 of the range by default - double val = (range[1] - range[0]) * 2 / 3 + range[0]; - setIsoValue(val); -} - -void ModuleContour::isoRange(double range[2]) -{ - getRange(d->ContourArrayProducer, range); -} - -QJsonObject ModuleContour::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - props["useSolidColor"] = useSolidColor(); - props["color"] = color().name(); - props["contourValue"] = iso(); - - QJsonObject lighting; - lighting["ambient"] = ambient(); - lighting["diffuse"] = diffuse(); - lighting["specular"] = specular(); - lighting["specularPower"] = specularPower(); - props["lighting"] = lighting; - - props["representation"] = representation(); - props["opacity"] = opacity(); - props["mapScalars"] = colorMapData(); - props["colorByArray"] = colorByArray(); - props["colorByArrayName"] = colorByArrayName(); - - json["properties"] = props; - - return json; -} - -bool ModuleContour::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - - if (!json["properties"].isObject()) - return false; - - auto props = json["properties"].toObject(); - onUseSolidColorToggled(props["useSolidColor"].toBool()); - onColorChanged(QColor(props["color"].toString())); - - QJsonObject lighting = props["lighting"].toObject(); - onAmbientChanged(lighting["ambient"].toDouble()); - onDiffuseChanged(lighting["diffuse"].toDouble()); - onSpecularChanged(lighting["specular"].toDouble()); - onSpecularPowerChanged(lighting["specularPower"].toDouble()); - - onRepresentationChanged(props["representation"].toString()); - onOpacityChanged(props["opacity"].toDouble()); - onColorMapDataToggled(props["mapScalars"].toBool()); - onColorByArrayToggled(props["colorByArray"].toBool()); - onColorByArrayNameChanged(props["colorByArrayName"].toString()); - - // Some of the above operations modify the contour value. - // Set this at the end. - onIsoChanged(props["contourValue"].toDouble()); - - updatePanel(); - - return true; -} - -void ModuleContour::dataSourceMoved(double newX, double newY, double newZ) -{ - m_actor->SetPosition(newX, newY, newZ); -} - -void ModuleContour::dataSourceRotated(double newX, double newY, double newZ) -{ - m_actor->SetOrientation(newX, newY, newZ); -} - -vtkDataObject* ModuleContour::dataToExport() -{ - return m_flyingEdges->GetOutputDataObject(0); -} - -void ModuleContour::setIsoValue(double value) -{ - onIsoChanged(value); - if (m_controllers) { - QSignalBlocker blocker(m_controllers); - m_controllers->setIso(value); - } -} - -bool ModuleContour::colorMapData() const -{ - return m_mapper->GetColorMode() == VTK_COLOR_MODE_MAP_SCALARS; -} - -double ModuleContour::ambient() const -{ - return m_property->GetAmbient(); -} - -double ModuleContour::diffuse() const -{ - return m_property->GetDiffuse(); -} - -double ModuleContour::specular() const -{ - return m_property->GetSpecular(); -} - -double ModuleContour::iso() const -{ - return m_flyingEdges->GetValue(0); -} - -double ModuleContour::specularPower() const -{ - return m_property->GetSpecularPower(); -} - -QString ModuleContour::representation() const -{ - return m_property->GetRepresentationAsString(); -} - -double ModuleContour::opacity() const -{ - return m_property->GetOpacity(); -} - -QColor ModuleContour::color() const -{ - double rgb[3]; - m_property->GetDiffuseColor(rgb); - return QColor(static_cast(rgb[0] * 255.0 + 0.5), - static_cast(rgb[1] * 255.0 + 0.5), - static_cast(rgb[2] * 255.0 + 0.5)); -} - -bool ModuleContour::useSolidColor() const -{ - return d->UseSolidColor; -} - -QString ModuleContour::contourByArrayName() const -{ - if (activeScalars() == Module::defaultScalarsIdx()) - return dataSource()->activeScalars(); - - return dataSource()->scalarsName(activeScalars()); -} - -bool ModuleContour::colorByArray() const -{ - return d->ColorByArray; -} - -QString ModuleContour::colorByArrayName() const -{ - return d->ColorArrayName; -} - -void ModuleContour::onColorMapDataToggled(const bool state) -{ - int mode = state ? VTK_COLOR_MODE_MAP_SCALARS : VTK_COLOR_MODE_DIRECT_SCALARS; - m_mapper->SetColorMode(mode); - emit renderNeeded(); -} - -void ModuleContour::onAmbientChanged(const double value) -{ - m_property->SetAmbient(value); - emit renderNeeded(); -} - -void ModuleContour::onDiffuseChanged(const double value) -{ - m_property->SetDiffuse(value); - emit renderNeeded(); -} - -void ModuleContour::onSpecularChanged(const double value) -{ - m_property->SetSpecular(value); - emit renderNeeded(); -} - -void ModuleContour::onSpecularPowerChanged(const double value) -{ - m_property->SetSpecularPower(value); - emit renderNeeded(); -} - -void ModuleContour::onIsoChanged(const double value) -{ - m_flyingEdges->SetValue(0, value); - emit renderNeeded(); -} - -void ModuleContour::onRepresentationChanged(const QString& representation) -{ - if (representation == "Surface") - m_property->SetRepresentationToSurface(); - else if (representation == "Points") - m_property->SetRepresentationToPoints(); - else if (representation == "Wireframe") - m_property->SetRepresentationToWireframe(); - - emit renderNeeded(); -} - -void ModuleContour::onOpacityChanged(const double value) -{ - m_property->SetOpacity(value); - emit renderNeeded(); -} - -void ModuleContour::onColorChanged(const QColor& c) -{ - double rgb[3] = { c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 }; - m_property->SetDiffuseColor(rgb); - emit renderNeeded(); -} - -void ModuleContour::onUseSolidColorToggled(const bool state) -{ - d->UseSolidColor = state; - updateColorMap(); - emit renderNeeded(); -} - -void ModuleContour::onContourByArrayValueChanged(int i) -{ - setActiveScalars(i); - updateContourArrayProducer(); - updateIsoRange(); - resetIsoValue(); - updateColorMap(); - emit renderNeeded(); -} - -void ModuleContour::onColorByArrayToggled(const bool state) -{ - d->ColorByArray = state; - updateColorArrayProducer(); - if (state) { - m_probeFilter->SetInputConnection(m_flyingEdges->GetOutputPort()); - m_probeFilter->SetSourceConnection(d->ColorArrayProducer->GetOutputPort()); - m_mapper->SetInputConnection(m_probeFilter->GetOutputPort()); - } else { - m_probeFilter->RemoveAllInputs(); - m_mapper->SetInputConnection(m_flyingEdges->GetOutputPort()); - } - updateColorMap(); - emit renderNeeded(); -} - -void ModuleContour::onColorByArrayNameChanged(const QString& name) -{ - d->ColorArrayName = name; - updateColorArrayProducer(); - updateColorMap(); - emit renderNeeded(); -} - -bool ModuleContour::updateClippingPlane(vtkPlane* plane, bool newFilter) -{ - if (m_mapper->GetNumberOfClippingPlanes()) { - m_mapper->RemoveClippingPlane(plane); - } - if (!newFilter) { - m_mapper->AddClippingPlane(plane); - } - - emit renderNeeded(); - - return true; -} - -} // end of namespace tomviz diff --git a/tomviz/legacy/modules/ModuleContour.h b/tomviz/legacy/modules/ModuleContour.h deleted file mode 100644 index 2075e094d..000000000 --- a/tomviz/legacy/modules/ModuleContour.h +++ /dev/null @@ -1,116 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleContour_h -#define tomvizModuleContour_h - -#include "Module.h" - -#include - -class vtkActor; -class vtkDataSetMapper; -class vtkFlyingEdges3D; -class vtkProbeFilter; -class vtkProperty; -class vtkPVRenderView; - -namespace tomviz { - -class ModuleContourWidget; - -class ModuleContour : public Module -{ - Q_OBJECT - -public: - ModuleContour(QObject* parent = nullptr); - ~ModuleContour() override; - - QString label() const override { return "Contour"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - bool finalize() override; - void addToPanel(QWidget*) override; - bool setVisibility(bool val) override; - bool visibility() const override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - bool isColorMapNeeded() const override { return true; } - - void dataSourceMoved(double newX, double newY, double newZ) override; - void dataSourceRotated(double newX, double newY, double newZ) override; - - void setIsoValue(double value); - void resetIsoValue(); - void isoRange(double range[2]); - - QString exportDataTypeString() override { return "Mesh"; } - - vtkDataObject* dataToExport() override; - - bool colorMapData() const; - double ambient() const; - double diffuse() const; - double specular() const; - double specularPower() const; - double iso() const; - QString representation() const; - double opacity() const; - QColor color() const; - bool useSolidColor() const; - QString contourByArrayName() const; - bool colorByArray() const; - QString colorByArrayName() const; - bool updateClippingPlane(vtkPlane* plane, bool newFilter) override; - -protected: - void updatePanel(); - void updateColorMap() override; - void updateColorArray(); - void updateContourArrayProducer(); - void updateColorArrayProducer(); - void clearColorArrayProducer(); - void updateIsoRange(); - void updateContourByArrayOptions(); - void updateColorByArrayOptions(); - - vtkNew m_actor; - vtkNew m_mapper; - vtkNew m_property; - vtkNew m_flyingEdges; - vtkNew m_probeFilter; - vtkWeakPointer m_view; - - class Private; - Private* d; - - QPointer m_controllers; - - QString m_representation; - -private slots: - void onDataChanged(); - void onDataPropertiesChanged(); - void onActiveScalarsChanged(); - void onColorMapDataToggled(const bool state); - void onAmbientChanged(const double value); - void onDiffuseChanged(const double value); - void onSpecularChanged(const double value); - void onSpecularPowerChanged(const double value); - void onIsoChanged(const double value); - void onRepresentationChanged(const QString& representation); - void onOpacityChanged(const double value); - void onColorChanged(const QColor& color); - void onUseSolidColorToggled(const bool state); - void onContourByArrayValueChanged(int i); - void onColorByArrayToggled(const bool state); - void onColorByArrayNameChanged(const QString& name); - -private: - Q_DISABLE_COPY(ModuleContour) -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleContourWidget.cxx b/tomviz/legacy/modules/ModuleContourWidget.cxx deleted file mode 100644 index 63bba7e26..000000000 --- a/tomviz/legacy/modules/ModuleContourWidget.cxx +++ /dev/null @@ -1,201 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleContourWidget.h" -#include "ui_LightingParametersForm.h" -#include "ui_ModuleContourWidget.h" - -#include - -namespace tomviz { - -ModuleContourWidget::ModuleContourWidget(QWidget* parent_) - : QWidget(parent_), m_ui(new Ui::ModuleContourWidget), - m_uiLighting(new Ui::LightingParametersForm) -{ - m_ui->setupUi(this); - - QWidget* lightingWidget = new QWidget; - m_uiLighting->setupUi(lightingWidget); - m_uiLighting->gbLighting->setCheckable(false); - QWidget::layout()->addWidget(lightingWidget); - qobject_cast(QWidget::layout())->addStretch(); - - m_ui->colorChooser->setShowAlphaChannel(false); - - const int leWidth = 50; - m_ui->sliValue->setLineEditWidth(leWidth); - m_ui->sliOpacity->setLineEditWidth(leWidth); - m_uiLighting->sliAmbient->setLineEditWidth(leWidth); - m_uiLighting->sliDiffuse->setLineEditWidth(leWidth); - m_uiLighting->sliSpecular->setLineEditWidth(leWidth); - m_uiLighting->sliSpecularPower->setLineEditWidth(leWidth); - - m_uiLighting->sliSpecularPower->setMaximum(150); - m_uiLighting->sliSpecularPower->setMinimum(1); - m_uiLighting->sliSpecularPower->setResolution(200); - - QStringList labelsRepre; - labelsRepre << tr("Surface") << tr("Wireframe") << tr("Points"); - - for (const auto& label : labelsRepre) - m_ui->cbRepresentation->addItem(label, label); - - connect(m_ui->cbColorMapData, &QCheckBox::toggled, this, - &ModuleContourWidget::colorMapDataToggled); - connect(m_uiLighting->sliAmbient, &DoubleSliderWidget::valueEdited, this, - &ModuleContourWidget::ambientChanged); - connect(m_uiLighting->sliDiffuse, &DoubleSliderWidget::valueEdited, this, - &ModuleContourWidget::diffuseChanged); - connect(m_uiLighting->sliSpecular, &DoubleSliderWidget::valueEdited, this, - &ModuleContourWidget::specularChanged); - connect(m_uiLighting->sliSpecularPower, &DoubleSliderWidget::valueEdited, - this, &ModuleContourWidget::specularPowerChanged); - connect(m_ui->sliValue, &DoubleSliderWidget::valueEdited, this, - &ModuleContourWidget::isoChanged); - connect(m_ui->cbRepresentation, - QOverload::of(&QComboBox::currentIndexChanged), this, - &ModuleContourWidget::onRepresentationIndexChanged); - connect(m_ui->sliOpacity, &DoubleSliderWidget::valueEdited, this, - &ModuleContourWidget::opacityChanged); - connect(m_ui->colorChooser, &pqColorChooserButton::chosenColorChanged, this, - &ModuleContourWidget::colorChanged); - connect(m_ui->cbSelectColor, &QCheckBox::toggled, this, - &ModuleContourWidget::useSolidColorToggled); - connect(m_ui->cbColorByArray, &QCheckBox::toggled, this, - &ModuleContourWidget::colorByArrayToggled); - connect(m_ui->comboColorByArray, - QOverload::of(&QComboBox::currentIndexChanged), this, - &ModuleContourWidget::onColorByArrayIndexChanged); - connect(m_ui->comboContourByArray, - QOverload::of(&QComboBox::currentIndexChanged), this, - &ModuleContourWidget::onContourByArrayIndexChanged); -} - -ModuleContourWidget::~ModuleContourWidget() = default; - -void ModuleContourWidget::setIsoRange(double range[2]) -{ - m_ui->sliValue->setMinimum(range[0]); - m_ui->sliValue->setMaximum(range[1]); -} - -void ModuleContourWidget::setColorByArrayOptions(const QStringList& options) -{ - m_ui->comboColorByArray->clear(); - - // Save and use the text in the item data - for (const auto& opt : options) - m_ui->comboColorByArray->addItem(opt, opt); -} - -void ModuleContourWidget::setContourByArrayOptions(DataSource* ds, - Module* module) -{ - m_ui->comboContourByArray->setOptions(ds, module); -} - -void ModuleContourWidget::setColorMapData(const bool state) -{ - m_ui->cbColorMapData->setChecked(state); -} - -void ModuleContourWidget::setAmbient(const double value) -{ - m_uiLighting->sliAmbient->setValue(value); -} - -void ModuleContourWidget::setDiffuse(const double value) -{ - m_uiLighting->sliDiffuse->setValue(value); -} - -void ModuleContourWidget::setSpecular(const double value) -{ - m_uiLighting->sliSpecular->setValue(value); -} - -void ModuleContourWidget::setSpecularPower(const double value) -{ - m_uiLighting->sliSpecularPower->setValue(value); -} - -void ModuleContourWidget::setIso(const double value) -{ - m_ui->sliValue->setValue(value); -} - -void ModuleContourWidget::setRepresentation(const QString& representation) -{ - for (int i = 0; i < m_ui->cbRepresentation->count(); ++i) { - if (m_ui->cbRepresentation->itemData(i).toString() == representation) { - m_ui->cbRepresentation->setCurrentIndex(i); - return; - } - } - - qCritical() << "Could not find" << representation - << "in representation options"; -} - -void ModuleContourWidget::setOpacity(const double value) -{ - m_ui->sliOpacity->setValue(value); -} - -void ModuleContourWidget::setColor(const QColor& color) -{ - m_ui->colorChooser->setChosenColor(color); -} - -void ModuleContourWidget::setUseSolidColor(const bool state) -{ - m_ui->cbSelectColor->setChecked(state); -} - -void ModuleContourWidget::setColorByArray(const bool state) -{ - m_ui->cbColorByArray->setChecked(state); -} - -void ModuleContourWidget::setColorByArrayName(const QString& name) -{ - for (int i = 0; i < m_ui->comboColorByArray->count(); ++i) { - if (m_ui->comboColorByArray->itemData(i).toString() == name) { - m_ui->comboColorByArray->setCurrentIndex(i); - return; - } - } - - qCritical() << "Could not find" << name << "in ColorByArray options"; -} - -void ModuleContourWidget::setContourByArrayValue(int val) -{ - for (int i = 0; i < m_ui->comboContourByArray->count(); ++i) { - if (m_ui->comboContourByArray->itemData(i).toInt() == val) { - m_ui->comboContourByArray->setCurrentIndex(i); - return; - } - } - - qCritical() << "Could not find" << val << "in ContourByArray options"; -} - -void ModuleContourWidget::onContourByArrayIndexChanged(int i) -{ - emit contourByArrayValueChanged( - m_ui->comboContourByArray->itemData(i).toInt()); -} - -void ModuleContourWidget::onColorByArrayIndexChanged(int i) -{ - emit colorByArrayNameChanged(m_ui->comboColorByArray->itemData(i).toString()); -} - -void ModuleContourWidget::onRepresentationIndexChanged(int i) -{ - emit representationChanged(m_ui->cbRepresentation->itemData(i).toString()); -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleContourWidget.h b/tomviz/legacy/modules/ModuleContourWidget.h deleted file mode 100644 index 1b68dc36a..000000000 --- a/tomviz/legacy/modules/ModuleContourWidget.h +++ /dev/null @@ -1,93 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleContourWidget_h -#define tomvizModuleContourWidget_h - -#include -#include - -/** - * \brief UI layer of ModuleContour. - * - * Signals are forwarded to ModuleContour or the proxies. This class is - * intended - * to contain only logic related to UI actions. - */ - -namespace Ui { -class ModuleContourWidget; -class LightingParametersForm; -} // namespace Ui - -namespace tomviz { - -class DataSource; -class Module; - -class ModuleContourWidget : public QWidget -{ - Q_OBJECT - -public: - ModuleContourWidget(QWidget* parent_ = nullptr); - ~ModuleContourWidget() override; - - void setIsoRange(double range[2]); - void setContourByArrayOptions(DataSource* ds, Module* module); - void setColorByArrayOptions(const QStringList& options); - - //@{ - /** - * UI update methods. The actual model state is stored in ModuleContour for - * these parameters, so the UI needs to be updated if the state changes or - * when constructing the UI. - */ - void setColorMapData(const bool state); - void setAmbient(const double value); - void setDiffuse(const double value); - void setSpecular(const double value); - void setSpecularPower(const double value); - void setIso(const double value); - void setRepresentation(const QString& representation); - void setOpacity(const double value); - void setColor(const QColor& color); - void setUseSolidColor(const bool state); - void setContourByArrayValue(int i); - void setColorByArray(const bool state); - void setColorByArrayName(const QString& name); - //@} - -signals: - //@{ - /** - * Forwarded signals. - */ - void colorMapDataToggled(const bool state); - void ambientChanged(const double value); - void diffuseChanged(const double value); - void specularChanged(const double value); - void specularPowerChanged(const double value); - void isoChanged(const double value); - void representationChanged(const QString& representation); - void opacityChanged(const double value); - void colorChanged(const QColor& color); - void useSolidColorToggled(const bool state); - void contourByArrayValueChanged(int i); - void colorByArrayToggled(const bool state); - void colorByArrayNameChanged(const QString& name); - //@} - -private: - void onContourByArrayIndexChanged(int i); - void onColorByArrayIndexChanged(int i); - void onRepresentationIndexChanged(int i); - - ModuleContourWidget(const ModuleContourWidget&) = delete; - void operator=(const ModuleContourWidget&) = delete; - - QScopedPointer m_ui; - QScopedPointer m_uiLighting; -}; -} // namespace tomviz -#endif diff --git a/tomviz/legacy/modules/ModuleContourWidget.ui b/tomviz/legacy/modules/ModuleContourWidget.ui deleted file mode 100644 index f4b928d0a..000000000 --- a/tomviz/legacy/modules/ModuleContourWidget.ui +++ /dev/null @@ -1,210 +0,0 @@ - - - ModuleContourWidget - - - - 0 - 0 - 246 - 225 - - - - Form - - - - - - - - Qt::LeftToRight - - - Color Map Data - - - true - - - - - - - - - - - Qt::LeftToRight - - - Select Color - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Contour by - - - - - - - - - - - - - - Color by - - - - - - - false - - - - - - - - - - - Value - - - - - - - - - - Mode - - - - - - - - - - Opacity - - - - - - - - - - - - - tomviz::DoubleSliderWidget - QWidget -
DoubleSliderWidget.h
- 1 -
- - pqColorChooserButton - QWidget -
pqColorChooserButton.h
- 1 -
- - tomviz::ScalarsComboBox - QComboBox -
legacy/modules/ScalarsComboBox.h
-
-
- - cbColorMapData - cbSelectColor - comboContourByArray - cbColorByArray - comboColorByArray - cbRepresentation - - - - - cbColorByArray - toggled(bool) - comboColorByArray - setEnabled(bool) - - - 71 - 84 - - - 187 - 84 - - - - - cbColorByArray - toggled(bool) - cbSelectColor - setDisabled(bool) - - - 71 - 84 - - - 63 - 52 - - - - - cbColorByArray - toggled(bool) - colorChooser - setDisabled(bool) - - - 71 - 84 - - - 122 - 52 - - - - -
diff --git a/tomviz/legacy/modules/ModuleFactory.cxx b/tomviz/legacy/modules/ModuleFactory.cxx deleted file mode 100644 index e35cf3b17..000000000 --- a/tomviz/legacy/modules/ModuleFactory.cxx +++ /dev/null @@ -1,264 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleFactory.h" - -#include "../DataSource.h" -#include "ModuleClip.h" -#include "ModuleContour.h" -#include "ModuleMolecule.h" -#include "ModuleOutline.h" -#include "ModulePlot.h" -#include "ModuleRuler.h" -#include "ModuleScaleCube.h" -#include "ModuleSegment.h" -#include "ModuleSlice.h" -#include "ModuleThreshold.h" -#include "ModuleVolume.h" -#include "../operators/OperatorResult.h" -#include "Utilities.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace tomviz { - -ModuleFactory::ModuleFactory() {} - -ModuleFactory::~ModuleFactory() {} - -QList ModuleFactory::moduleTypes() -{ - QList reply; - reply << "Outline" - << "Slice" - << "Ruler" - << "Scale Cube" - << "Contour" - << "Volume" - << "Threshold" - << "Molecule" - << "Clip" - << "Plot"; - std::sort(reply.begin(), reply.end()); - return reply; -} - -bool ModuleFactory::moduleApplicable(const QString& moduleName, - DataSource* dataSource, - vtkSMViewProxy* view) -{ - Q_UNUSED(view); - - if (moduleName == "Molecule" || moduleName == "Plot") { - return false; - } - - if (dataSource) { - if (dataSource->getNumberOfComponents() > 1) { - if (moduleName == "Contour" || moduleName == "Threshold") { - return false; - } - } - return true; - } - return false; -} - -bool ModuleFactory::moduleApplicable(const QString& moduleName, - MoleculeSource* moleculeSource, - vtkSMViewProxy* view) -{ - Q_UNUSED(view); - - if (moduleName == "Molecule") { - if (moleculeSource) { - return true; - } - } - return false; -} - -bool ModuleFactory::moduleApplicable(const QString& moduleName, - OperatorResult* operatorResult, - vtkSMViewProxy* view) -{ - Q_UNUSED(view); - - if (moduleName == "Plot") { - return ( - operatorResult && - vtkTable::SafeDownCast(operatorResult->dataObject()) - ); - } else if (moduleName == "Molecule") { - return ( - operatorResult && - vtkMolecule::SafeDownCast(operatorResult->dataObject()) - ); - } - - return false; -} - -Module* ModuleFactory::allocateModule(const QString& type) -{ - Module* module = nullptr; - if (type == "Outline") { - module = new ModuleOutline(); - } else if (type == "Contour") { - module = new ModuleContour(); - } else if (type == "Volume") { - module = new ModuleVolume(); - } else if (type == "Slice") { - module = new ModuleSlice(); - } else if (type == "Orthogonal Slice") { - // Keep this to be able to open older state files. - module = new ModuleSlice(); - } else if (type == "Threshold") { - module = new ModuleThreshold(); - } else if (type == "Ruler") { - module = new ModuleRuler(); - } else if (type == "Scale Cube") { - module = new ModuleScaleCube(); - } else if (type == "Molecule") { - module = new ModuleMolecule(); - } else if (type== "Clip") { - module = new ModuleClip(); - } else if (type == "Plot") { - module = new ModulePlot(); - } - return module; -} - -Module* ModuleFactory::createModule(const QString& type, DataSource* dataSource, - vtkSMViewProxy* view) -{ - auto module = allocateModule(type); - if (module) { - // sanity check. - Q_ASSERT(type == moduleType(module)); - if (dataSource == nullptr && view == nullptr) { - // don't initialize module if args are NULL. - return module; - } - - bool success; - success = module->initialize(dataSource, view); - if (!success) { - delete module; - return nullptr; - } - pqView* pqview = tomviz::convert(view); - pqview->render(); - } - return module; -} - -Module* ModuleFactory::createModule(const QString& type, - MoleculeSource* moleculeSource, - vtkSMViewProxy* view) -{ - auto module = allocateModule(type); - if (module) { - // sanity check. - Q_ASSERT(type == moduleType(module)); - if (moleculeSource == nullptr && view == nullptr) { - // don't initialize module if args are NULL. - return module; - } - - bool success; - success = module->initialize(moleculeSource, view); - if (!success) { - delete module; - return nullptr; - } - pqView* pqview = tomviz::convert(view); - pqview->render(); - } - return module; -} - -Module* ModuleFactory::createModule(const QString& type, OperatorResult* result, - vtkSMViewProxy* view) -{ - auto module = allocateModule(type); - if (module) { - // sanity check. - Q_ASSERT(type == moduleType(module)); - if (result == nullptr && view == nullptr) { - // don't initialize module if args are NULL. - return module; - } - - bool success; - success = module->initialize(result, view); - if (!success) { - delete module; - return nullptr; - } - pqView* pqview = tomviz::convert(view); - pqview->render(); - } - return module; -} - -QIcon ModuleFactory::moduleIcon(const QString& type) -{ - QIcon icon; - Module* mdl = ModuleFactory::allocateModule(type); - if (mdl) { - icon = mdl->icon(); - delete mdl; - } - return icon; -} - -const char* ModuleFactory::moduleType(const Module* module) -{ - // WARNING: to ensure the most useful result is returned from this - // function, the if statements should be ordered so that child - // classes are checked before parent classes. Otherwise, the module - // type may be reported to be a class's parent. - - if (qobject_cast(module)) { - return "Outline"; - } - if (qobject_cast(module)) { - return "Contour"; - } - if (qobject_cast(module)) { - return "Volume"; - } - if (qobject_cast(module)) { - return "Slice"; - } - if (qobject_cast(module)) { - return "Threshold"; - } - if (qobject_cast(module)) { - return "Ruler"; - } - if (qobject_cast(module)) { - return "Scale Cube"; - } - if (qobject_cast(module)) { - return "Molecule"; - } - if (qobject_cast(module)) { - return "Plot"; - } - if (qobject_cast(module)) { - return "Clip"; - } - return nullptr; -} - -} // end of namespace tomviz diff --git a/tomviz/legacy/modules/ModuleFactory.h b/tomviz/legacy/modules/ModuleFactory.h deleted file mode 100644 index a28e3b6ce..000000000 --- a/tomviz/legacy/modules/ModuleFactory.h +++ /dev/null @@ -1,61 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleFactory_h -#define tomvizModuleFactory_h - -#include -#include - -class vtkSMViewProxy; - -namespace tomviz { -class DataSource; -class MoleculeSource; -class Module; -class OperatorResult; - -class ModuleFactory -{ - typedef QObject Superclass; - -public: - /// Returns a list of module types that can be created for the data source - /// in the provided view. - static QList moduleTypes(); - - /// Returns whether the module of the given name is applicable to the - /// DataSource and View. - static bool moduleApplicable(const QString& moduleName, - DataSource* dataSource, vtkSMViewProxy* view); - static bool moduleApplicable(const QString& moduleName, - MoleculeSource* moleculeSource, - vtkSMViewProxy* view); - static bool moduleApplicable(const QString& moduleName, - OperatorResult* operatorResult, - vtkSMViewProxy* view); - - /// Creates a module of the given type to show the dataSource in the view. - static Module* createModule(const QString& type, DataSource* dataSource, - vtkSMViewProxy* view); - static Module* createModule(const QString& type, - MoleculeSource* moleculeSource, - vtkSMViewProxy* view); - static Module* createModule(const QString& type, OperatorResult* result, - vtkSMViewProxy* view); - - /// Returns the type for a module instance. - static const char* moduleType(const Module* module); - - /// Returns the icon for a module. - static QIcon moduleIcon(const QString& type); - -private: - ModuleFactory(); - ~ModuleFactory(); - Q_DISABLE_COPY(ModuleFactory) - static Module* allocateModule(const QString& type); -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleManager.cxx b/tomviz/legacy/modules/ModuleManager.cxx deleted file mode 100644 index 1a769d1e9..000000000 --- a/tomviz/legacy/modules/ModuleManager.cxx +++ /dev/null @@ -1,1294 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleManager.h" - -#include "ActiveObjects.h" -#include "ViewsLayoutsSerializer.h" -#include "../DataSource.h" -#include "../operators/Operator.h" -#include "LoadDataReaction.h" -#include "ModuleFactory.h" -#include "MoleculeSource.h" -#include "../Pipeline.h" -#include "../PythonGeneratedDatasetReaction.h" - -#include "pipeline/PortData.h" -#include "pipeline/PortType.h" -#include "pipeline/SourceNode.h" -#include "pipeline/data/VolumeData.h" -#include "Utilities.h" -#include "tomvizConfig.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace tomviz { - -static QList toObjectList(const QJsonArray& array) -{ - QList ret; - for (const auto& item : array) { - ret.append(item.toObject()); - } - return ret; -} - -class ModuleManager::MMInternals -{ -public: - // TODO Should only hold top level roots of pipeline - QList> DataSources; - QList> MoleculeSources; - QList> ChildDataSources; - QList> Modules; - QMap> RenderViewCameras; - - // Map from view proxies to modules. Used to keep track of how many modules - // have been added to a view. - QMultiMap ViewModules; - - // State for the "state finished loading signal" - int RemaningPipelinesToWaitFor; - bool LastStateLoadSuccess; - - // Ensure all pipelines created when restoring the state are not executed - bool ExecutePipelinesOnLoad = true; - - // Only used by onPVStateLoaded for the second half of deserialize - QDir dir; - QMap ViewIdMap; - void relativeFilePaths(DataSource* ds, const QDir& stateDir, - QJsonObject& dataSourceState) - { - QJsonObject readerProps; - if (dataSourceState.contains("reader") && - dataSourceState["reader"].isObject()) { - readerProps = dataSourceState["reader"].toObject(); - } - - // Make any reader fileName properties relative to the state file being - // written. - if (readerProps.contains("fileNames")) { - // Exclude transient data sources. - // ( ones without a file. i.e. output data sources ) - if (!ds->isTransient()) { - auto fileNames = readerProps["fileNames"].toArray(); - QJsonArray relativeNames; - for (const QJsonValue& name : fileNames) { - relativeNames.append(stateDir.relativeFilePath(name.toString())); - } - readerProps["fileNames"] = relativeNames; - } - dataSourceState["reader"] = readerProps; - } - } - - void relativeFilePaths(MoleculeSource*, const QDir& stateDir, - QJsonObject& dataSourceState) - { - QJsonObject readerProps; - if (dataSourceState.contains("reader") && - dataSourceState["reader"].isObject()) { - readerProps = dataSourceState["reader"].toObject(); - } - - // Make any reader fileName properties relative to the state file being - // written. - if (readerProps.contains("fileName")) { - auto fileName = readerProps["fileName"].toString(); - readerProps["fileName"] = stateDir.relativeFilePath(fileName); - dataSourceState["reader"] = readerProps; - } - } - - void absoluteFilePaths(QJsonObject& dataSourceState) - { - - std::function absolute = [this](QString path) { - if (!path.isEmpty()) { - path = QDir::cleanPath(this->dir.absoluteFilePath(path)); - } - - return path; - }; - - if (dataSourceState.contains("reader") && - dataSourceState["reader"].isObject()) { - auto reader = dataSourceState["reader"].toObject(); - if (reader.contains("fileNames") && reader["fileNames"].isArray()) { - auto fileNames = reader["fileNames"].toArray(); - QJsonArray absoluteFileNames; - for (const QJsonValue& path : fileNames) { - absoluteFileNames.append(absolute(path.toString())); - } - reader["fileNames"] = absoluteFileNames; - } - if (reader.contains("fileName") && reader["fileName"].isString()) { - QString absoluteFileName = absolute(reader["fileName"].toString()); - reader["fileName"] = absoluteFileName; - } - dataSourceState["reader"] = reader; - } - } - - QStringList dataSourceDependencies(const QJsonObject& ds) - { - // Traverse recursively through the operators and find any - // data source dependencies. Returns a list of their ids. - QStringList deps; - for (auto opVal : ds.value("operators").toArray()) { - auto op = opVal.toObject(); - auto args = op.value("arguments").toObject(); - for (auto& key : args.keys()) { - auto value = args[key]; - if (value.isString() && value.toString().startsWith("0x")) { - // This is a dependency, add it. - deps.append(value.toString()); - } - } - - for (auto childVal : op.value("dataSources").toArray()) { - // Recursively add child dependencies. - deps.append(dataSourceDependencies(childVal.toObject())); - } - } - - return deps; - } - - QJsonObject rootDataSourceDependency(QString dep, - QList& dataSources) - { - // Find the root data source of this dependency. - // Searches through the list of dataSources to find this information. - for (auto& ds : dataSources) { - if (ds.value("id").toString() == dep) { - return ds; - } - - for (auto opVal : ds.value("operators").toArray()) { - auto op = opVal.toObject(); - auto children = toObjectList(op.value("dataSources").toArray()); - - // Recursively search for it in the children - auto result = rootDataSourceDependency(dep, children); - if (!result.isEmpty()) { - return ds; - } - } - } - - // Didn't find it... - return {}; - } - - bool isChildDataSourceDependency(QString dep, QList& dataSources) - { - auto root = rootDataSourceDependency(dep, dataSources); - return root.value("id").toString() != dep; - } - - void removeInvalidDataSources() - { - int i = 0; - while (i < DataSources.size()) { - if (DataSources[i]) { - // Valid - i += 1; - continue; - } else { - // Invalid. Remove. - DataSources.removeAt(i); - } - } - } - - void removeInvalidChildDataSources() - { - int i = 0; - while (i < ChildDataSources.size()) { - if (ChildDataSources[i]) { - // Valid - i += 1; - continue; - } else { - // Invalid. Remove. - ChildDataSources.removeAt(i); - } - } - } -}; - -ModuleManager::ModuleManager(QObject* parentObject) - : Superclass(parentObject), d(new ModuleManager::MMInternals()) -{ - connect(pqApplicationCore::instance()->getServerManagerModel(), - &pqServerManagerModel::viewRemoved, this, - &ModuleManager::onViewRemoved); -} - -ModuleManager::~ModuleManager() = default; - -ModuleManager& ModuleManager::instance() -{ - static ModuleManager theInstance; - return theInstance; -} - -void ModuleManager::reset() -{ - removeAllModules(); - removeAllDataSources(); - removeAllMoleculeSources(); - pqDeleteReaction::deleteAll(); -} - -bool ModuleManager::hasRunningOperators() -{ - d->removeInvalidDataSources(); - for (QPointer dsource : d->DataSources) { - if (dsource->pipeline()->isRunning()) { - return true; - } - } - return false; -} - -QList ModuleManager::dataSources() -{ - // Somehow we have invalid data sources around. Make sure - // those are removed. - d->removeInvalidDataSources(); - QList ret; - std::copy(d->DataSources.begin(), d->DataSources.end(), - std::back_inserter(ret)); - return ret; -} - -QList ModuleManager::childDataSources() -{ - // Somehow we have invalid data sources around. Make sure - // those are removed. - d->removeInvalidChildDataSources(); - QList ret; - std::copy(d->ChildDataSources.begin(), d->ChildDataSources.end(), - std::back_inserter(ret)); - return ret; -} - -QList ModuleManager::allDataSources() -{ - return dataSources() + childDataSources(); -} - -QList ModuleManager::allDataSourcesDepthFirst() -{ - // Return the data sources in a Depth First Search (DFS) order, - // so that child data sources will come immediately after their - // parent data source in the list. - // This *should* match the order of data sources in the pipeline view. - QList result; - for (auto* rootSource : dataSources()) { - result.append(rootSource); - if (rootSource->operators().isEmpty()) { - // If there are no operators, there are no child data sources... - continue; - } - - auto* lastOperator = rootSource->operators().back(); - auto* childSource = lastOperator->childDataSource(); - if (childSource) { - result.append(childSource); - } - } - - return result; -} - -QStringList ModuleManager::createUniqueLabels(const QList& sources) -{ - QStringList ret; - for (auto* ds : sources) { - auto label = ds->label(); - if (ret.contains(label)) { - auto original = label; - int index = 1; - do { - label = original + QString(" (%1)").arg(++index); - } while (ret.contains(label)); - } - ret.append(label); - } - return ret; -} - -void ModuleManager::addDataSource(DataSource* dataSource) -{ - d->removeInvalidDataSources(); - if (dataSource && !d->DataSources.contains(dataSource)) { - d->DataSources.push_back(dataSource); - emit dataSourceAdded(dataSource); - } -} - -void ModuleManager::addChildDataSource(DataSource* dataSource) -{ - d->removeInvalidChildDataSources(); - if (dataSource && !d->ChildDataSources.contains(dataSource)) { - d->ChildDataSources.push_back(dataSource); - emit childDataSourceAdded(dataSource); - } -} - -void ModuleManager::removeDataSource(DataSource* dataSource) -{ - d->removeInvalidDataSources(); - d->removeInvalidChildDataSources(); - if (d->DataSources.removeOne(dataSource) || - d->ChildDataSources.removeOne(dataSource)) { - emit dataSourceRemoved(dataSource); - dataSource->deleteLater(); - } -} - -void ModuleManager::removeChildDataSource(DataSource* dataSource) -{ - d->removeInvalidChildDataSources(); - if (d->ChildDataSources.removeOne(dataSource)) { - emit childDataSourceRemoved(dataSource); - dataSource->deleteLater(); - } -} - -void ModuleManager::removeAllDataSources() -{ - d->removeInvalidDataSources(); - foreach (DataSource* dataSource, d->DataSources) { - emit dataSourceRemoved(dataSource); - dataSource->deleteLater(); - } - d->DataSources.clear(); -} - -void ModuleManager::removeAllMoleculeSources() -{ - foreach (MoleculeSource* moleculeSource, d->MoleculeSources) { - emit moleculeSourceRemoved(moleculeSource); - moleculeSource->deleteLater(); - } - d->MoleculeSources.clear(); -} - -void ModuleManager::addMoleculeSource(MoleculeSource* moleculeSource) -{ - if (moleculeSource && !d->MoleculeSources.contains(moleculeSource)) { - d->MoleculeSources.push_back(moleculeSource); - emit moleculeSourceAdded(moleculeSource); - } -} - -void ModuleManager::removeMoleculeSource(MoleculeSource* moleculeSource) -{ - if (d->MoleculeSources.removeOne(moleculeSource)) { - emit moleculeSourceRemoved(moleculeSource); - } -} - -void ModuleManager::removeOperator(Operator* op) -{ - if (op) { - emit operatorRemoved(op); - } -} - -bool ModuleManager::isChild(DataSource* source) const -{ - d->removeInvalidChildDataSources(); - return (d->ChildDataSources.indexOf(source) >= 0); -} - -void ModuleManager::addModule(Module* module) -{ - if (!d->Modules.contains(module)) { - module->setParent(this); - d->Modules.push_back(module); - - // Reset display if this is the first module in the view. - if (d->ViewModules.count(module->view()) == 0) { - auto pqview = tomviz::convert(module->view()); - pqview->resetDisplay(); - pqview->render(); - } - d->ViewModules.insert(module->view(), module); - - // Prevent the user from adding more than 6 clipping planes - // to a data source - int count = 0; - foreach (Module* m, d->Modules) { - if (m->dataSource() == module->dataSource()) { - if (strcmp(ModuleFactory::moduleType(m), "Clip") == 0) { - ++count; - } - } - } - if (count > 6) { - QMessageBox::warning(tomviz::mainWidget(), - tr("Max Clipping Planes Reached"), - tr("No more than 6 clipping planes can be added to " - "a single data source."), - QMessageBox::Ok); - removeModule(module); - return; - } - - emit moduleAdded(module); - connect(module, &Module::renderNeeded, this, &ModuleManager::render); - connect(module, &Module::updateClientSideViewNeeded, this, - &ModuleManager::updateClientSideView); - if (strcmp(ModuleFactory::moduleType(module), "Slice") == 0) { - connect(module, &Module::mouseOverVoxel, this, - &ModuleManager::mouseOverVoxel); - } - connect(module, &Module::visibilityChanged, this, &ModuleManager::visibilityChanged); - } -} - -void ModuleManager::removeModule(Module* module) -{ - if (d->Modules.removeOne(module)) { - d->ViewModules.remove(module->view(), module); - emit moduleRemoved(module); - module->deleteLater(); - } -} - -void ModuleManager::removeAllModules() -{ - foreach (Module* module, d->Modules) { - emit moduleRemoved(module); - module->deleteLater(); - } - d->Modules.clear(); -} - -void ModuleManager::removeAllModules(DataSource* source) -{ - Q_ASSERT(source); - QList modules; - foreach (Module* module, d->Modules) { - if (module->dataSource() == source) { - modules.push_back(module); - } - } - foreach (Module* module, modules) { - removeModule(module); - } -} - -Module* ModuleManager::createAndAddModule(const QString& type, - DataSource* dataSource, - vtkSMViewProxy* view) -{ - if (!view || !dataSource) { - return nullptr; - } - - // Create an outline module for the source in the active view. - auto module = ModuleFactory::createModule(type, dataSource, view); - if (module) { - addModule(module); - } - return module; -} - -Module* ModuleManager::createAndAddModule(const QString& type, - MoleculeSource* moleculeSource, - vtkSMViewProxy* view) -{ - if (!view || !moleculeSource) { - return nullptr; - } - - // Create an outline module for the source in the active view. - auto module = ModuleFactory::createModule(type, moleculeSource, view); - if (module) { - addModule(module); - } - return module; -} - -Module* ModuleManager::createAndAddModule(const QString& type, - OperatorResult* result, - vtkSMViewProxy* view) -{ - if (!view || !result) { - return nullptr; - } - - // Create an outline module for the source in the active view. - auto module = ModuleFactory::createModule(type, result, view); - if (module) { - addModule(module); - } - return module; -} - -QList ModuleManager::findModulesGeneric(const DataSource* dataSource, - const vtkSMViewProxy* view) -{ - QList modules; - foreach (Module* module, d->Modules) { - if (module && module->dataSource() == dataSource && - (view == nullptr || view == module->view()) && - module->label() != "Molecule") { - modules.push_back(module); - } - } - return modules; -} - -QList ModuleManager::findModulesGeneric( - const MoleculeSource* dataSource, const vtkSMViewProxy* view) -{ - QList modules; - foreach (Module* module, d->Modules) { - if (module && module->moleculeSource() == dataSource && - (view == nullptr || view == module->view())) { - modules.push_back(module); - } - } - return modules; -} - -QJsonArray jsonArrayFromXml(pugi::xml_node node) -{ - // Simple function, just iterates through the elements and fills the array. - // This assumes the ParaView Element nodes, values stored in value attributes. - QJsonArray array; - for (auto element = node.child("Element"); element; - element = element.next_sibling("Element")) { - array.append(element.attribute("value").as_int(-1)); - } - return array; -} - -QJsonArray jsonArrayFromXmlDouble(pugi::xml_node node) -{ - // Simple function, just iterates through the elements and fills the array. - // This assumes the ParaView Element nodes, values stored in value attributes. - QJsonArray array; - for (auto element = node.child("Element"); element; - element = element.next_sibling("Element")) { - array.append(element.attribute("value").as_double(-1)); - } - return array; -} - -bool ModuleManager::serialize(QJsonObject& doc, const QDir& stateDir, - bool interactive) const -{ - d->removeInvalidDataSources(); - d->removeInvalidChildDataSources(); - - QJsonObject tvObj; - tvObj["version"] = QString(TOMVIZ_VERSION); - if (QString(TOMVIZ_VERSION_EXTRA).size() > 0) { - tvObj["versionExtra"] = QString(TOMVIZ_VERSION_EXTRA); - } - - auto pvVer = QString("%1.%2.%3") - .arg(vtkSMProxyManager::GetVersionMajor()) - .arg(vtkSMProxyManager::GetVersionMinor()) - .arg(vtkSMProxyManager::GetVersionPatch()); - tvObj["paraViewVersion"] = pvVer; - - doc["tomviz"] = tvObj; - - if (interactive) { - // Iterate over all data sources and check is there are any that are not - // currently saved. - int modified = 0; - foreach (const QPointer& ds, d->DataSources) { - if (ds != nullptr && - ds->persistenceState() == DataSource::PersistenceState::Modified) { - ++modified; - } - } - foreach (const QPointer& ds, d->ChildDataSources) { - if (ds != nullptr && - ds->persistenceState() == DataSource::PersistenceState::Modified) { - ++modified; - } - } - - if (modified > 0) { - QMessageBox modifiedMessageBox; - modifiedMessageBox.setIcon(QMessageBox::Warning); - QString text = QString("Warning: unsaved data - %1 data source%2") - .arg(modified) - .arg(modified > 1 ? "s" : ""); - QString infoText = - "Unsaved data is marked in the pipeline italic text " - "with an asterisk. You may continue to save the state, " - "and any unsaved data (along with operators/modules) " - "will be skipped."; - modifiedMessageBox.setText(text); - modifiedMessageBox.setInformativeText(infoText); - modifiedMessageBox.setStandardButtons(QMessageBox::Save | - QMessageBox::Cancel); - modifiedMessageBox.setDefaultButton(QMessageBox::Save); - - if (modifiedMessageBox.exec() == QMessageBox::Cancel) { - return false; - } - } - } - - QJsonArray jDataSources; - foreach (DataSource* ds, d->DataSources) { - auto jDataSource = ds->serialize(); - d->relativeFilePaths(ds, stateDir, jDataSource); - jDataSources.append(jDataSource); - } - doc["dataSources"] = jDataSources; - - QJsonArray jMoleculeSources; - foreach (MoleculeSource* ms, d->MoleculeSources) { - auto jMoleculeSource = ms->serialize(); - // LEGACY STUB: activeMoleculeSource() removed from ActiveObjects - if (false) { - jMoleculeSource["active"] = true; - } - - d->relativeFilePaths(ms, stateDir, jMoleculeSource); - - jMoleculeSources.append(jMoleculeSource); - } - doc["moleculeSources"] = jMoleculeSources; - - // Palette color, views, and layouts share the same emitter between the - // legacy and new state-file formats. - ViewsLayoutsSerializer::saveActive(doc); - - return true; -} - -void createXmlProperty(pugi::xml_node& n, const char* name, int id) -{ - n.set_name("Property"); - n.append_attribute("name").set_value(name); - QString idStr = QString::number(id) + "." + name; - n.append_attribute("id").set_value(idStr.toStdString().c_str()); -} - -template -void createXmlProperty(pugi::xml_node& n, const char* name, int id, T value) -{ - createXmlProperty(n, name, id); - n.append_attribute("number_of_elements").set_value(1); - auto element = n.append_child("Element"); - element.append_attribute("index").set_value(0); - element.append_attribute("value").set_value(value); -} - -void createXmlProperty(pugi::xml_node& n, const char* name, int id, - QJsonArray arr) -{ - createXmlProperty(n, name, id); - n.append_attribute("number_of_elements").set_value(arr.size()); - for (int i = 0; i < arr.size(); ++i) { - auto element = n.append_child("Element"); - element.append_attribute("index").set_value(i); - element.append_attribute("value").set_value(arr[i].toDouble(-1)); - } -} - -void createXmlLayout(pugi::xml_node& n, QJsonArray arr) -{ - n.set_name("Layout"); - n.append_attribute("number_of_elements").set_value(arr.size()); - for (int i = 0; i < arr.size(); ++i) { - auto obj = arr[i].toObject(); - auto item = n.append_child("Item"); - item.append_attribute("direction").set_value(obj["direction"].toInt()); - item.append_attribute("fraction").set_value(obj["fraction"].toDouble()); - item.append_attribute("view").set_value(obj["viewId"].toInt()); - } -} - -bool ModuleManager::deserialize(const QJsonObject& doc, const QDir& stateDir, - bool loadDataSources) -{ - m_isDeserializing = true; - - // Get back to a known state. - reset(); - d->LastStateLoadSuccess = true; - - // Disable the contour module's dialog, re-enable it when the state loading is - // finished. - QSettings* settings = pqApplicationCore::instance()->settings(); - bool userConfirmInitialValue = - settings->value("ContourSettings.UserConfirmInitialValue", true).toBool(); - settings->setValue("ContourSettings.UserConfirmInitialValue", false); - connect(this, &ModuleManager::stateDoneLoading, this, - [settings, userConfirmInitialValue]() { - settings->setValue("ContourSettings.UserConfirmInitialValue", - userConfirmInitialValue); - }); - - // High level game plan - construct some XML for ParaView, restore the - // layouts, the views, links, etc. Once they are ready then restore the data - // pipeline, using the nested layout to assure the order is correct. - QJsonArray views = doc["views"].toArray(); - QJsonArray layouts = doc["layouts"].toArray(); - QJsonArray links = doc["links"].toArray(); - - // ParaView must load all views and layouts first. - pugi::xml_document document; - auto pvxml = document.append_child("ParaView"); - auto pvState = pvxml.append_child("ServerManagerState"); - // Hardwire the ParaView version to avoid issues with a hardwired check for - // versions less than 4.0.1 in the state version controller in ParaView. - pvState.append_attribute("version").set_value("5.5.0"); - auto pvViews = pvState.append_child("ProxyCollection"); - pvViews.append_attribute("name").set_value("views"); - auto pvLayouts = pvState.append_child("ProxyCollection"); - pvLayouts.append_attribute("name").set_value("layouts"); - int numViews = 0, numLayouts = 0; - - // Check for a palette color - if (doc.contains("paletteColor")) { - auto paletteColorArray = doc["paletteColor"].toArray(); - double paletteColor[3] = { paletteColorArray[0].toDouble(), - paletteColorArray[1].toDouble(), - paletteColorArray[2].toDouble() }; - - auto* pxm = ActiveObjects::instance().proxyManager(); - auto* paletteProxy = pxm->GetProxy("settings", "ColorPalette"); - vtkSMPropertyHelper(paletteProxy->GetProperty("BackgroundColor")) - .Set(paletteColor, 3); - paletteProxy->UpdateVTKObjects(); - } - - // First see if we have views, and unpack them. - for (int i = 0; i < views.size(); ++i) { - QJsonObject view = views[i].toObject(); - auto viewId = view["id"].toInt(); - auto proxyNode = pvState.append_child("Proxy"); - proxyNode.append_attribute("group").set_value("views"); - auto xmlName = view["xmlName"].toString("RenderView"); - proxyNode.append_attribute("type").set_value( - xmlName.toStdString().c_str()); - proxyNode.append_attribute("id").set_value(viewId); - proxyNode.append_attribute("servers").set_value(view["servers"].toInt()); - - auto propNode = proxyNode.append_child("Property"); - createXmlProperty(propNode, "CenterOfRotation", viewId, - view["centerOfRotation"].toArray()); - // Let's do the camera now... - QJsonObject camera = view["camera"].toObject(); - propNode = proxyNode.append_child("Property"); - createXmlProperty(propNode, "CameraFocalPoint", viewId, - camera["focalPoint"].toArray()); - - if (view.contains("useColorPaletteForBackground")) { - propNode = proxyNode.append_child("Property"); - createXmlProperty(propNode, "UseColorPaletteForBackground", viewId, - view["useColorPaletteForBackground"].toInt()); - } - - if (view.contains("backgroundColor")) { - if (!view.contains("useColorPaletteForBackground")) { - // Assume this is an older state file where we should turn this off - propNode = proxyNode.append_child("Property"); - createXmlProperty(propNode, "UseColorPaletteForBackground", viewId, 0); - } - - auto backgroundColor = view["backgroundColor"].toArray(); - // Restore the background color - propNode = proxyNode.append_child("Property"); - createXmlProperty(propNode, "Background", viewId, - backgroundColor.at(0).toArray()); - - // If we have more than one element, we have a gradient so also restore - // Background2 and set UseGradientBackground. - if (backgroundColor.size() > 1) { - propNode = proxyNode.append_child("Property"); - createXmlProperty(propNode, "Background2", viewId, - backgroundColor.at(1).toArray()); - propNode = proxyNode.append_child("Property"); - createXmlProperty(propNode, "UseGradientBackground", viewId, 1); - } - } - if (view.contains("isOrthographic")) { - auto parallelProjection = view["isOrthographic"].toBool() ? 1 : 0; - propNode = proxyNode.append_child("Property"); - createXmlProperty(propNode, "CameraParallelProjection", viewId, - parallelProjection); - } - if (view.contains("interactionMode")) { - propNode = proxyNode.append_child("Property"); - auto modeString = view["interactionMode"].toString(); - int mode = 0; // default to 3D - if (modeString == "2D") { - mode = 1; - } else if (modeString == "selection") { - mode = 2; - } - createXmlProperty(propNode, "InteractionMode", viewId, mode); - } - - // Create an entry in the views thing... - pugi::xml_node viewSummary = pvViews.append_child("Item"); - viewSummary.append_attribute("id").set_value(viewId); - viewSummary.append_attribute("name").set_value( - QString("View%1").arg(++numViews).toStdString().c_str()); - } - // Now the layouts - should only ever be one, but go through the motions... - for (int i = 0; i < layouts.size(); ++i) { - QJsonObject layout = layouts[i].toObject(); - auto layoutId = layout["id"].toInt(); - auto proxyNode = pvState.append_child("Proxy"); - proxyNode.append_attribute("group").set_value("misc"); - proxyNode.append_attribute("type").set_value("ViewLayout"); - proxyNode.append_attribute("id").set_value(layoutId); - proxyNode.append_attribute("servers").set_value(layout["servers"].toInt()); - - QJsonArray items = layout["items"].toArray(); - for (int j = 0; j < items.size(); ++j) { - auto layoutNode = proxyNode.append_child("Layout"); - createXmlLayout(layoutNode, items[j].toArray()); - } - - // Create an entry in the layouts thing... - auto layoutSummary = pvLayouts.append_child("Item"); - layoutSummary.append_attribute("id").set_value(layoutId); - layoutSummary.append_attribute("name").set_value( - QString("Layout%1").arg(++numLayouts).toStdString().c_str()); - } - - d->dir = stateDir; - m_stateObject = doc; - m_loadDataSources = loadDataSources; - connect(pqApplicationCore::instance(), - &pqApplicationCore::stateLoaded, this, - &ModuleManager::onPVStateLoaded); - // Set up call to ParaView to load state - std::ostringstream stream; - document.first_child().print(stream); - - // qDebug() << "\nPV XML:" << stream.str().c_str() << "\n"; - vtkNew parser; - if (!parser->Parse(stream.str().c_str())) { - d->LastStateLoadSuccess = false; - return false; - } - pqActiveObjects* activeObjects = &pqActiveObjects::instance(); - pqServer* server = activeObjects->activeServer(); - - pqApplicationCore::instance()->loadState(parser->GetRootElement(), server); - // Clean up the state -- since the Qt slot call should be synchronous - // it should be done before the code returns to here. - disconnect(pqApplicationCore::instance(), - &pqApplicationCore::stateLoaded, this, - &ModuleManager::onPVStateLoaded); - - d->dir = QDir(); - m_stateObject = QJsonObject(); - - // Restore the views to their state before the modules were added - setViews(views); - - d->LastStateLoadSuccess = true; - - if (d->RemaningPipelinesToWaitFor == 0) { - emit stateDoneLoading(); - } - return true; -} - -void ModuleManager::setViews(const QJsonArray& views) -{ - // This sets all views according to their settings in the view proxy. - // It should be called after the views have been deserialized. - for (int i = 0; i < views.size(); ++i) { - auto view = views[i].toObject(); - auto viewProxy = - vtkSMRenderViewProxy::SafeDownCast(lookupView(view["id"].toInt())); - if (!viewProxy) { - continue; - } - double tmp[3]; - auto camera = view["camera"].toObject(); - auto jsonArray = camera["position"].toArray(); - for (int j = 0; j < jsonArray.size(); ++j) { - tmp[j] = jsonArray[j].toDouble(); - } - vtkSMPropertyHelper(viewProxy, "CameraPosition").Set(tmp, 3); - jsonArray = camera["focalPoint"].toArray(); - for (int j = 0; j < jsonArray.size(); ++j) { - tmp[j] = jsonArray[j].toDouble(); - } - vtkSMPropertyHelper(viewProxy, "CameraFocalPoint").Set(tmp, 3); - jsonArray = camera["viewUp"].toArray(); - for (int j = 0; j < jsonArray.size(); ++j) { - tmp[j] = jsonArray[j].toDouble(); - } - vtkSMPropertyHelper(viewProxy, "CameraViewUp").Set(tmp, 3); - vtkSMPropertyHelper(viewProxy, "CameraViewAngle") - .Set(camera["viewAngle"].toDouble()); - vtkSMPropertyHelper(viewProxy, "EyeAngle") - .Set(camera["EyeAngle"].toDouble()); - vtkSMPropertyHelper(viewProxy, "CameraParallelScale") - .Set(camera["parallelScale"].toDouble()); - - // restore axis grid visibility - if (viewProxy->GetProperty("AxesGrid")) { - vtkSMPropertyHelper axesGridProp(viewProxy, "AxesGrid"); - vtkSMProxy* proxy = axesGridProp.GetAsProxy(); - if (!proxy) { - vtkSMSessionProxyManager* pxm = viewProxy->GetSessionProxyManager(); - proxy = pxm->NewProxy("annotations", "GridAxes3DActor"); - axesGridProp.Set(proxy); - proxy->Delete(); - } - vtkSMPropertyHelper(proxy, "Visibility") - .Set(view["axesGridVisibility"].toBool() ? 1 : 0); - proxy->UpdateVTKObjects(); - } - if (view.contains("centerAxesVisible")) { - vtkSMPropertyHelper(viewProxy, "CenterAxesVisibility") - .Set(view["centerAxesVisible"].toBool() ? 1 : 0); - } - if (view.contains("orientationAxesVisible")) { - vtkSMPropertyHelper(viewProxy, "OrientationAxesVisibility") - .Set(view["orientationAxesVisible"].toBool() ? 1 : 0); - } - viewProxy->UpdateVTKObjects(); - } - // force the view menu to update its state based on the settings we have - // restored to the view - ActiveObjects::instance().viewChanged(ActiveObjects::instance().activeView()); -} - -bool ModuleManager::lastLoadStateSucceeded() -{ - return d->LastStateLoadSuccess; -} - -void ModuleManager::onPVStateLoaded(vtkPVXMLElement*, - vtkSMProxyLocator* locator) -{ - auto pxm = ActiveObjects::instance().proxyManager(); - Q_ASSERT(pxm); - - // Populate the view id map, needed to create modules with the restored views. - d->ViewIdMap.clear(); - if (m_stateObject["views"].isArray()) { - auto viewArray = m_stateObject["views"].toArray(); - for (int i = 0; i < viewArray.size(); ++i) { - auto view = viewArray[i].toObject(); - auto viewId = view["id"].toInt(); - d->ViewIdMap[viewId] = - vtkSMViewProxy::SafeDownCast(locator->LocateProxy(viewId)); - } - } - - // Load up all of the data sources. - if (m_loadDataSources && m_stateObject["dataSources"].isArray()) { - auto dataSources = m_stateObject["dataSources"].toArray(); - loadDataSources(dataSources); - } - - m_isDeserializing = false; - - if (!executePipelinesOnLoad() || d->RemaningPipelinesToWaitFor == 0) { - // If there are no pipelines left to wait for, most likely, the - // pipelines finished before deserialization finished. Go ahead and - // emit the signal. - emit stateDoneLoading(); - } -} - -void ModuleManager::incrementPipelinesToWaitFor() -{ - ++d->RemaningPipelinesToWaitFor; -} - -void ModuleManager::onPipelineFinished() -{ - --d->RemaningPipelinesToWaitFor; - - // It could still be deserializing if the pipeline finishes early. - // In this case, the signal will be emitted after deserialization - // finishes. - if (d->RemaningPipelinesToWaitFor == 0 && !m_isDeserializing) { - emit stateDoneLoading(); - } - if (d->RemaningPipelinesToWaitFor <= 0) { - Pipeline* p = qobject_cast(sender()); - disconnect(p, &Pipeline::finished, this, - &ModuleManager::onPipelineFinished); - } -} - -void ModuleManager::executePipelinesOnLoad(bool execute) -{ - d->ExecutePipelinesOnLoad = execute; -} - -bool ModuleManager::executePipelinesOnLoad() const -{ - return d->ExecutePipelinesOnLoad; -} - -void ModuleManager::onViewRemoved(pqView* view) -{ - Q_ASSERT(view); - auto viewProxy = view->getViewProxy(); - QList modules; - foreach (Module* module, d->Modules) { - if (module->view() == viewProxy) { - modules.push_back(module); - } - } - foreach (Module* module, modules) { - removeModule(module); - } -} - -void ModuleManager::render() -{ - auto view = tomviz::convert(ActiveObjects::instance().activeView()); - if (view) { - view->render(); - } -} - -void ModuleManager::updateClientSideView() -{ - auto* view = ActiveObjects::instance().activeView(); - if (!view) { - return; - } - - auto* clientView = vtkPVRenderView::SafeDownCast(view->GetClientSideView()); - if (!clientView) { - return; - } - - clientView->Update(); -} - -vtkSMViewProxy* ModuleManager::lookupView(int id) -{ - return d->ViewIdMap.value(id); -} - -bool ModuleManager::hasDataSources() -{ - d->removeInvalidDataSources(); - return !d->DataSources.empty(); -} - -bool ModuleManager::hasMoleculeSources() -{ - return !d->MoleculeSources.empty(); -} - -void ModuleManager::loadDataSources(const QJsonArray& dataSourcesArray) -{ - // Data source arguments in operators make it so that we have to - // load in the data sources in a proper order, or else the arguments - // will not be able to be resolved for the operator to use. - - auto dataSources = toObjectList(dataSourcesArray); - QMap alreadyLoaded; - - // This local load data source function ensures we load dependencies - // before we load in the requested data source. It also ensures that - // we do not load in any data sources twice. - std::function localLoadDataSource; - localLoadDataSource = [&dataSources, &alreadyLoaded, &localLoadDataSource, - this](QJsonObject ds) { - auto id = ds.value("id").toString(); - if (alreadyLoaded.contains(id)) { - // Already have this one - return; - } - - // First, see if it has any dependencies. - auto deps = d->dataSourceDependencies(ds); - - // Load the dependencies first. - for (auto& dep : deps) { - // Load the root data source of this dependency - auto rootObj = d->rootDataSourceDependency(dep, dataSources); - localLoadDataSource(rootObj); - - // If it is a child dependency, wait for the pipeline to finish - if (d->isChildDataSourceDependency(dep, dataSources)) { - auto rootId = rootObj.value("id").toString(); - auto* depDataSource = alreadyLoaded[rootId]; - if (depDataSource->pipeline()->isRunning()) { - QEventLoop loop; - connect(depDataSource->pipeline(), &Pipeline::finished, &loop, - &QEventLoop::quit); - loop.exec(); - }; - } - } - - // Now that the dependencies are loaded, we can load the data source... - alreadyLoaded[id] = loadDataSource(ds); - }; - - for (auto& ds : dataSources) { - localLoadDataSource(ds); - } -} - -DataSource* ModuleManager::loadDataSource(QJsonObject& dsObject) -{ - QJsonObject options; - options["defaultModules"] = false; - options["addToRecent"] = false; - options["child"] = false; - d->absoluteFilePaths(dsObject); - - QStringList fileNames; - if (dsObject.contains("reader")) { - auto reader = dsObject["reader"].toObject(); - - if (reader.contains("fileNames")) { - const auto fileNamesArray = reader["fileNames"].toArray(); - for (const QJsonValue& value : fileNamesArray) { - // Verify the file exists before adding it to the list. - if (QFileInfo::exists(value.toString())) { - fileNames << value.toString(); - } else { - // If the file cannot be found in the path relative to the state - // file, make another attempt to locate it in the same directory - QString altLocation = - d->dir.absoluteFilePath(QFileInfo(value.toString()).fileName()); - if (QFileInfo::exists(altLocation)) { - fileNames << altLocation; - } else { - qCritical() << "File" << value.toString() << "not found, skipping."; - } - } - } - reader["fileNames"] = QJsonArray::fromStringList(fileNames); - } else { - qCritical() << "Unable to locate file name(s)."; - } - if (reader.contains("name")) { - options["reader"] = reader; - } - if (reader.contains("subsampleSettings")) { - options["subsampleSettings"] = reader["subsampleSettings"]; - } - if (reader.contains("tvh5NodePath")) { - options["tvh5NodePath"] = reader["tvh5NodePath"]; - } - } - - if (!options.contains("subsampleSettings")) { - if (dsObject.contains("subsampleSettings")) { - // Make sure subsample settings get communicated to the readers - options["subsampleSettings"] = dsObject["subsampleSettings"]; - } else { - // Never prompt the user for subsample setting when loading state - // files. This will prevent the prompt from happening. - options["subsampleSettings"] = QJsonObject(); - } - } - - // TODO: Legacy state restoration — this path creates SourceNodes via - // LoadDataReaction but cannot fully deserialize DataSource state. - // Full serialization support for the new pipeline is a separate concern. - pipeline::SourceNode* sourceNode = nullptr; - if (dsObject.find("sourceInformation") != dsObject.end()) { - DataSource* ds = PythonGeneratedDatasetReaction::createDataSource( - dsObject["sourceInformation"].toObject()); - if (ds) { - auto* sn = new pipeline::SourceNode(); - sn->setLabel(ds->label()); - sn->addOutput("volume", pipeline::PortType::ImageData); - vtkSmartPointer img = ds->imageData(); - auto vol = std::make_shared(img); - vol->setLabel(ds->label()); - sn->setOutputData( - "volume", - pipeline::PortData(vol, pipeline::PortType::ImageData)); - LoadDataReaction::sourceNodeAdded(sn, false, false); - sourceNode = sn; - delete ds; - } - } else if (fileNames.size() > 0) { - sourceNode = LoadDataReaction::loadData(fileNames, options); - } else { - qCritical() << "Files not found on disk for data source, check paths."; - } - - Q_UNUSED(sourceNode); - // TODO: The following legacy code is not yet migrated: - // - Pipeline::finished connection for operators - // - dataSource->deserialize(dsObject) - // - setPersistenceState for transient sources - // - setActiveDataSource - - return nullptr; -} - -void ModuleManager::setMostRecentStateFile(const QString& s) -{ - m_mostRecentStateFile = s; - emit mostRecentStateFileChanged(m_mostRecentStateFile); -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleManager.h b/tomviz/legacy/modules/ModuleManager.h deleted file mode 100644 index e14af7436..000000000 --- a/tomviz/legacy/modules/ModuleManager.h +++ /dev/null @@ -1,219 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleManager_h -#define tomvizModuleManager_h - -#include - -#include "Module.h" - -#include -#include -#include - -class pqView; -class vtkSMSourceProxy; -class vtkSMViewProxy; -class vtkPVXMLElement; -class vtkSMProxyLocator; -class vtkSMRepresentationProxy; -class QDir; - -namespace tomviz { -class DataSource; -class MoleculeSource; -class Module; -class Operator; -class Pipeline; - -/// Singleton akin to ProxyManager, but to keep track (and -/// serialize/deserialze) modules. -class ModuleManager : public QObject -{ - Q_OBJECT - - typedef QObject Superclass; - -public: - static ModuleManager& instance(); - - /// Returns a list of modules of the specified type showing the dataSource in - /// the give view. If view is NULL, all modules for the dataSource will be - /// returned. - template - QList findModules(DataSource* dataSource, vtkSMViewProxy* view) - { - QList modulesT; - QList modules = this->findModulesGeneric(dataSource, view); - foreach (Module* module, modules) { - // Don't include modules that are actually children of an OperatorResult - if (module->operatorResult() == nullptr) { - if (T moduleT = qobject_cast(module)) { - modulesT.push_back(moduleT); - } - } - } - return modulesT; - } - - QList dataSources(); - QList childDataSources(); - QList allDataSources(); - - // Return the data sources in a Depth First Search (DFS) order, - // so that child data sources will come immediately after their - // parent data source in the list. - // This *should* match the order of data sources in the pipeline view. - QList allDataSourcesDepthFirst(); - - // Generate a list of unique labels from the list of data sources. - // Duplicate labels will have a space and number appended to them, - // such as "Label", "Label (2)", "Label (3)", etc. - // The labels will be in the same order as the input DataSource list. - static QStringList createUniqueLabels(const QList& sources); - - QList findModulesGeneric(const DataSource* dataSource, - const vtkSMViewProxy* view); - - QList findModulesGeneric(const MoleculeSource* dataSource, - const vtkSMViewProxy* view); - - /// Save the application state as JSON, use stateDir as the base for relative - /// paths. - bool serialize(QJsonObject& doc, const QDir& stateDir, - bool interactive = true) const; - bool deserialize(const QJsonObject& doc, const QDir& stateDir, - bool loadDataSources = true); - - /// Set the views from the provided state. - /// Usually, this should be done after ModuleManager::deserialize(). - void setViews(const QJsonArray& views); - - /// Test if any data source has running operators - bool hasRunningOperators(); - - /// Return whether a DataSource is a child DataSource - bool isChild(DataSource*) const; - - /// Used to lookup a view by id, only intended for use during deserialization. - vtkSMViewProxy* lookupView(int id); - - /// Used to test if there is data loaded (i.e. not an empty session) - bool hasDataSources(); - bool hasMoleculeSources(); - - /// Used when loading a model. If there are additional child pipelines that - /// need to finish processing before stateDoneLoading is emitted, then this - /// must be called for each of them so the module manager knows how many - /// pipelineFinsihed signals to wait for. - void incrementPipelinesToWaitFor(); - - bool lastLoadStateSucceeded(); - - void executePipelinesOnLoad(bool execute); - bool executePipelinesOnLoad() const; - DataSource* loadDataSource(QJsonObject& ds); - - /// Used to keep track of the most recent state file - void setMostRecentStateFile(const QString& s); - QString mostRecentStateFile() const { return m_mostRecentStateFile; } - - // Keep a record of state ids to data sources - void addStateIdToDataSource(QString id, DataSource* dataSource) - { - m_stateIdToDataSource[id] = dataSource; - } - - DataSource* dataSourceForStateId(QString id) - { - return m_stateIdToDataSource.value(id, nullptr); - } - -public slots: - void addModule(Module*); - - /// Use these methods to delete/remove modules. - void removeModule(Module*); - void removeAllModules(); - void removeAllModules(DataSource* source); - - /// Creates and add a new module. - Module* createAndAddModule(const QString& type, DataSource* dataSource, - vtkSMViewProxy* view); - Module* createAndAddModule(const QString& type, MoleculeSource* dataSource, - vtkSMViewProxy* view); - Module* createAndAddModule(const QString& type, OperatorResult* result, - vtkSMViewProxy* view); - - /// Register/Unregister data sources with the ModuleManager. - void addDataSource(DataSource*); - void addMoleculeSource(MoleculeSource*); - void addChildDataSource(DataSource*); - void removeDataSource(DataSource*); - void removeMoleculeSource(MoleculeSource*); - void removeChildDataSource(DataSource*); - void removeAllDataSources(); - void removeAllMoleculeSources(); - void removeOperator(Operator*); - - /// Removes all modules and data sources. - void reset(); - -private slots: - /// Used when loading state - void onPVStateLoaded(vtkPVXMLElement*, vtkSMProxyLocator*); - - /// Delete modules when the view that they are in is removed. - void onViewRemoved(pqView*); - - void render(); - void updateClientSideView(); - - void onPipelineFinished(); - -signals: - void moduleAdded(Module*); - void moduleRemoved(Module*); - - void dataSourceAdded(DataSource*); - void childDataSourceAdded(DataSource*); - void dataSourceRemoved(DataSource*); - void moleculeSourceRemoved(MoleculeSource*); - void childDataSourceRemoved(DataSource*); - - void moleculeSourceAdded(MoleculeSource*); - - void operatorRemoved(Operator*); - - void stateDoneLoading(); - - void enablePythonConsole(bool enable); - - void mostRecentStateFileChanged(const QString& s); - - void visibilityChanged(bool); - - void mouseOverVoxel(const vtkVector3i& ijk, double v); - - void pipelineViewRenderNeeded(); - -private: - Q_DISABLE_COPY(ModuleManager) - ModuleManager(QObject* parent = nullptr); - ~ModuleManager(); - - void loadDataSources(const QJsonArray& dataSources); - - class MMInternals; - QScopedPointer d; - - QMap m_stateIdToDataSource; - QString m_mostRecentStateFile = ""; - QJsonObject m_stateObject; - bool m_loadDataSources = true; - bool m_isDeserializing = false; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleMenu.cxx b/tomviz/legacy/modules/ModuleMenu.cxx deleted file mode 100644 index d6e14112f..000000000 --- a/tomviz/legacy/modules/ModuleMenu.cxx +++ /dev/null @@ -1,215 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleMenu.h" - -#include "ActiveObjects.h" -#include "ModuleFactory.h" -#include "ModuleManager.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace tomviz { - -static vtkSMViewProxy* resolveView(const QString& moduleType) -{ - // Determine which view type this module needs. - bool needsChart = (moduleType == "Plot"); - QString viewTypeName = needsChart ? "XYChartView" : "RenderView"; - - // Check if the active view is already the right type. - auto* activeView = ActiveObjects::instance().activeView(); - if (activeView) { - bool activeIsChart = vtkSMContextViewProxy::SafeDownCast(activeView); - bool activeIsRender = vtkSMRenderViewProxy::SafeDownCast(activeView); - if ((needsChart && activeIsChart) || (!needsChart && activeIsRender)) { - return activeView; - } - } - - // Enumerate all views and find matching ones. - auto* smModel = - pqApplicationCore::instance()->getServerManagerModel(); - QList allViews = smModel->findItems(); - - QList matching; - for (auto* v : allViews) { - auto* proxy = v->getViewProxy(); - bool isChart = vtkSMContextViewProxy::SafeDownCast(proxy); - bool isRender = vtkSMRenderViewProxy::SafeDownCast(proxy); - if ((needsChart && isChart) || (!needsChart && isRender)) { - matching.append(v); - } - } - - if (matching.isEmpty()) { - // No matching view exists — ask to create one. - QString label = needsChart ? "Line Chart View" : "Render View"; - auto answer = QMessageBox::question( - nullptr, "Create View?", - QString("No %1 is available. Create one?").arg(label), - QMessageBox::Yes | QMessageBox::No); - if (answer != QMessageBox::Yes) { - return nullptr; - } - // Split the current layout to make room for the new view. - int emptyCell = -1; - vtkSMViewLayoutProxy* layout = nullptr; - if (activeView) { - layout = vtkSMViewLayoutProxy::FindLayout(activeView); - if (layout) { - int location = layout->GetViewLocation(activeView); - // Split returns index of left child; existing view moves there. - // The empty cell is at leftChild + 1. - int leftChild = layout->Split( - location, vtkSMViewLayoutProxy::HORIZONTAL, 0.5); - if (leftChild >= 0) { - emptyCell = leftChild + 1; - } - } - } - - auto* builder = pqApplicationCore::instance()->getObjectBuilder(); - auto* server = pqApplicationCore::instance()->getActiveServer(); - auto* newView = builder->createView(viewTypeName, server); - if (!newView) { - return nullptr; - } - auto* proxy = newView->getViewProxy(); - - // Explicitly assign the new view to the empty cell we created. - if (layout && emptyCell >= 0) { - layout->AssignView(emptyCell, proxy); - } - - ActiveObjects::instance().setActiveView(proxy); - return proxy; - } - - if (matching.size() == 1) { - auto* proxy = matching.first()->getViewProxy(); - ActiveObjects::instance().setActiveView(proxy); - return proxy; - } - - // Multiple matches — let the user pick. - QStringList names; - for (auto* v : matching) { - names << v->getSMName(); - } - bool ok = false; - QString chosen = QInputDialog::getItem( - nullptr, "Select View", - QString("Multiple views available. Select one:"), - names, 0, false, &ok); - if (!ok) { - return nullptr; - } - int idx = names.indexOf(chosen); - auto* proxy = matching[idx]->getViewProxy(); - ActiveObjects::instance().setActiveView(proxy); - return proxy; -} - -ModuleMenu::ModuleMenu(QToolBar* toolBar, QMenu* menu, QObject* parentObject) - : QObject(parentObject), m_menu(menu), m_toolBar(toolBar) -{ - Q_ASSERT(menu); - Q_ASSERT(toolBar); - connect(menu, &QMenu::triggered, this, &ModuleMenu::triggered); - // LEGACY STUB: removed signal connections to deleted ActiveObjects signals - // (dataSourceChanged, moleculeSourceChanged, resultChanged) - connect(&ActiveObjects::instance(), QOverload::of(&ActiveObjects::viewChanged), this, &ModuleMenu::updateActions); - updateActions(); -} - -ModuleMenu::~ModuleMenu() {} - -void ModuleMenu::updateActions() -{ - QMenu* menu = m_menu; - QToolBar* toolBar = m_toolBar; - Q_ASSERT(menu); - Q_ASSERT(toolBar); - - menu->clear(); - toolBar->clear(); - - // LEGACY STUB: activeDataSource/activeMoleculeSource/activeOperatorResult - // removed from ActiveObjects - DataSource* activeDataSource = nullptr; - MoleculeSource* activeMoleculeSource = nullptr; - OperatorResult* activeOperatorResult = nullptr; - auto activeView = ActiveObjects::instance().activeView(); - QList modules = ModuleFactory::moduleTypes(); - - if (modules.size() > 0) { - foreach (const QString& txt, modules) { - auto actn = menu->addAction(ModuleFactory::moduleIcon(txt), txt); - actn->setEnabled( - ModuleFactory::moduleApplicable(txt, activeDataSource, activeView) || - ModuleFactory::moduleApplicable(txt, activeMoleculeSource, activeView) || - ModuleFactory::moduleApplicable(txt, activeOperatorResult, activeView)); - toolBar->addAction(actn); - actn->setData(txt); - } - } else { - auto action = menu->addAction("No modules available"); - action->setEnabled(false); - toolBar->addAction(action); - } -} - -void ModuleMenu::triggered(QAction* maction) -{ - auto type = maction->data().toString(); - // LEGACY STUB: activeDataSource/activeMoleculeSource/activeOperatorResult - // removed from ActiveObjects - DataSource* dataSource = nullptr; - MoleculeSource* moleculeSource = nullptr; - OperatorResult* operatorResult = nullptr; - - auto* view = resolveView(type); - if (!view) { - return; - } - - Module* module; - if (type == "Molecule") { - if (operatorResult) { - module = - ModuleManager::instance().createAndAddModule(type, operatorResult, view); - } else { - module = - ModuleManager::instance().createAndAddModule(type, moleculeSource, view); - } - } else if (type == "Plot") { - module = - ModuleManager::instance().createAndAddModule(type, operatorResult, view); - } else { - module = - ModuleManager::instance().createAndAddModule(type, dataSource, view); - } - - if (module) { - // LEGACY STUB: setActiveModule() removed from ActiveObjects - ActiveObjects::instance().setActiveNode(nullptr); - } else { - qCritical("Failed to create requested module."); - } -} - -} // end of namespace tomviz diff --git a/tomviz/legacy/modules/ModuleMenu.h b/tomviz/legacy/modules/ModuleMenu.h deleted file mode 100644 index b6df17075..000000000 --- a/tomviz/legacy/modules/ModuleMenu.h +++ /dev/null @@ -1,38 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleMenu_h -#define tomvizModuleMenu_h - -#include -#include - -class QAction; -class QMenu; -class QToolBar; - -namespace tomviz { - -/// ModuleMenu is manager for the Modules menu. It fills it up with actions -/// and handles their triggers based on available modules reported by -/// ModuleFactory. -class ModuleMenu : public QObject -{ - Q_OBJECT - -public: - ModuleMenu(QToolBar* toolBar, QMenu* parentMenu, QObject* parent = nullptr); - virtual ~ModuleMenu(); - -private slots: - void updateActions(); - void triggered(QAction* maction); - -private: - Q_DISABLE_COPY(ModuleMenu) - QPointer m_menu; - QPointer m_toolBar; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleMolecule.cxx b/tomviz/legacy/modules/ModuleMolecule.cxx deleted file mode 100644 index 0e5d3ecdc..000000000 --- a/tomviz/legacy/modules/ModuleMolecule.cxx +++ /dev/null @@ -1,194 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleMolecule.h" - -#include "../DataSource.h" -#include "DoubleSliderWidget.h" -#include "MoleculeSource.h" -#include "../operators/OperatorResult.h" -#include "Utilities.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -namespace tomviz { - -ModuleMolecule::ModuleMolecule(QObject* parentObject) : Module(parentObject) -{ -} - -ModuleMolecule::~ModuleMolecule() -{ - finalize(); -} - -QIcon ModuleMolecule::icon() const -{ - return QIcon(":/pqWidgets/Icons/pqGroup.svg"); -} - -bool ModuleMolecule::initialize(DataSource*, vtkSMViewProxy*) -{ - return false; -} - -bool ModuleMolecule::initialize(OperatorResult* result, vtkSMViewProxy* view) -{ - if (!Module::initialize(result, view)) { - return false; - } - - m_molecule = vtkMolecule::SafeDownCast(result->dataObject()); - if (m_molecule == nullptr) { - return false; - } - - addMoleculeToView(view); - - return true; -} - -bool ModuleMolecule::initialize(MoleculeSource* moleculeSource, - vtkSMViewProxy* view) -{ - if (!Module::initialize(moleculeSource, view)) { - return false; - } - - m_molecule = vtkMolecule::SafeDownCast(moleculeSource->molecule()); - if (m_molecule == nullptr) { - return false; - } - - addMoleculeToView(view); - - return true; -} - -void ModuleMolecule::addMoleculeToView(vtkSMViewProxy* view) -{ - m_moleculeMapper->SetInputData(m_molecule); - m_moleculeActor->SetMapper(m_moleculeMapper); - - m_view = vtkPVRenderView::SafeDownCast(view->GetClientSideView()); - m_view->GetRenderer()->AddActor(m_moleculeActor); - m_view->Update(); -} - -bool ModuleMolecule::finalize() -{ - if (m_view) { - m_view->GetRenderer()->RemoveActor(m_moleculeActor); - } - return true; -} - -bool ModuleMolecule::setVisibility(bool val) -{ - m_moleculeActor->SetVisibility(val); - Module::setVisibility(val); - return true; -} - -bool ModuleMolecule::visibility() const -{ - return m_moleculeActor->GetVisibility(); -} - -void ModuleMolecule::addToPanel(QWidget* panel) -{ - if (panel->layout()) { - delete panel->layout(); - } - - QFormLayout* layout = new QFormLayout; - - auto ballSlider = new DoubleSliderWidget(true); - ballSlider->setLineEditWidth(50); - ballSlider->setMaximum(4.0); - ballSlider->setValue(m_moleculeMapper->GetAtomicRadiusScaleFactor()); - layout->addRow("Ball Radius", ballSlider); - - auto stickSlider = new DoubleSliderWidget(true); - stickSlider->setLineEditWidth(50); - stickSlider->setMaximum(2.0); - stickSlider->setValue(m_moleculeMapper->GetBondRadius()); - layout->addRow("Stick Radius", stickSlider); - - panel->setLayout(layout); - - connect(ballSlider, &DoubleSliderWidget::valueEdited, this, - &ModuleMolecule::ballRadiusChanged); - - connect(stickSlider, &DoubleSliderWidget::valueEdited, this, - &ModuleMolecule::bondRadiusChanged); -} - -void ModuleMolecule::ballRadiusChanged(double val) -{ - m_moleculeMapper->SetAtomicRadiusScaleFactor(val); - if (m_view) { - m_view->GetRenderer()->Render(); - } -} - -void ModuleMolecule::bondRadiusChanged(double val) -{ - m_moleculeMapper->SetBondRadius(val); - if (m_view) { - m_view->GetRenderer()->Render(); - } -} - -QJsonObject ModuleMolecule::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - props["ballRadius"] = m_moleculeMapper->GetAtomicRadiusScaleFactor(); - props["stickRadius"] = m_moleculeMapper->GetBondRadius(); - - json["properties"] = props; - return json; -} - -bool ModuleMolecule::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - ballRadiusChanged(props["ballRadius"].toDouble()); - bondRadiusChanged(props["stickRadius"].toDouble()); - return true; - } - return false; -} - -void ModuleMolecule::dataSourceMoved(double, double, double) -{ -} - -void ModuleMolecule::dataSourceRotated(double, double, double) -{ -} - -vtkDataObject* ModuleMolecule::dataToExport() -{ - return m_molecule; -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleMolecule.h b/tomviz/legacy/modules/ModuleMolecule.h deleted file mode 100644 index 451a8205e..000000000 --- a/tomviz/legacy/modules/ModuleMolecule.h +++ /dev/null @@ -1,64 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleMolecule_h -#define tomvizModuleMolecule_h - -#include "Module.h" - -class QCheckBox; - -class vtkActor; -class vtkMolecule; -class vtkMoleculeMapper; - -class vtkPVRenderView; - -namespace tomviz { - -class MoleculeSource; -class OperatorResult; - -class ModuleMolecule : public Module -{ - Q_OBJECT - -public: - ModuleMolecule(QObject* parent = nullptr); - ~ModuleMolecule() override; - - QString label() const override { return "Molecule"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* dataSource, - vtkSMViewProxy* view) override; - bool initialize(MoleculeSource* moleculeSource, - vtkSMViewProxy* view) override; - bool initialize(OperatorResult* result, vtkSMViewProxy* view) override; - bool finalize() override; - bool setVisibility(bool val) override; - bool visibility() const override; - void addToPanel(QWidget*) override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - - QString exportDataTypeString() override { return "Molecule"; } - vtkDataObject* dataToExport() override; - - void dataSourceMoved(double newX, double newY, double newZ) override; - void dataSourceRotated(double newX, double newY, double newZ) override; - -private slots: - void ballRadiusChanged(double val); - void bondRadiusChanged(double val); - -private: - Q_DISABLE_COPY(ModuleMolecule) - void addMoleculeToView(vtkSMViewProxy* view); - vtkWeakPointer m_view; - vtkMolecule* m_molecule; - vtkNew m_moleculeMapper; - vtkNew m_moleculeActor; -}; -} // namespace tomviz -#endif diff --git a/tomviz/legacy/modules/ModuleOutline.cxx b/tomviz/legacy/modules/ModuleOutline.cxx deleted file mode 100644 index 393b3ee26..000000000 --- a/tomviz/legacy/modules/ModuleOutline.cxx +++ /dev/null @@ -1,434 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleOutline.h" - -#include "../DataSource.h" -#include "Utilities.h" -#include "pqProxiesWidget.h" -#include "vtkNew.h" -#include "vtkSMParaViewPipelineControllerWithRendering.h" -#include "vtkSMProperty.h" -#include "vtkSMPropertyHelper.h" -#include "vtkSMSessionProxyManager.h" -#include "vtkSMSourceProxy.h" -#include "vtkSMViewProxy.h" -#include "vtkSmartPointer.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -ModuleOutline::ModuleOutline(QObject* parentObject) : Module(parentObject) {} - -ModuleOutline::~ModuleOutline() -{ - finalize(); -} - -QIcon ModuleOutline::icon() const -{ - return QIcon(":/pqWidgets/Icons/pqProbeLocation.svg"); -} - -bool ModuleOutline::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - if (!Module::initialize(data, vtkView)) { - return false; - } - - vtkNew controller; - - auto pxm = data->proxy()->GetSessionProxyManager(); - - // Create the outline filter. - vtkSmartPointer proxy; - proxy.TakeReference(pxm->NewProxy("filters", "OutlineFilter")); - - m_outlineFilter = vtkSMSourceProxy::SafeDownCast(proxy); - Q_ASSERT(m_outlineFilter); - controller->PreInitializeProxy(m_outlineFilter); - vtkSMPropertyHelper(m_outlineFilter, "Input").Set(data->proxy()); - controller->PostInitializeProxy(m_outlineFilter); - controller->RegisterPipelineProxy(m_outlineFilter); - - // Create the representation for it. - m_outlineRepresentation = controller->Show(m_outlineFilter, 0, vtkView); - vtkSMPropertyHelper(m_outlineRepresentation, "Translation") - .Set(data->displayPosition(), 3); - vtkSMPropertyHelper(m_outlineRepresentation, "Orientation") - .Set(data->displayOrientation(), 3); - Q_ASSERT(m_outlineRepresentation); - // vtkSMPropertyHelper(OutlineRepresentation, - // "Representation").Set("Outline"); - m_outlineRepresentation->UpdateVTKObjects(); - - // Give the proxy a friendly name for the GUI/Python world. - if (auto p = convert(proxy)) { - p->rename(label()); - } - - // Init the grid axes - initializeGridAxes(data, vtkView); - updateGridAxesColor(offWhite); - - return true; -} - -bool ModuleOutline::finalize() -{ - vtkNew controller; - controller->UnRegisterProxy(m_outlineRepresentation); - controller->UnRegisterProxy(m_outlineFilter); - - if (m_view) { - m_view->GetRenderer()->RemoveActor(m_gridAxes); - } - - m_outlineFilter = nullptr; - m_outlineRepresentation = nullptr; - return true; -} - -QJsonObject ModuleOutline::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - props["gridVisibility"] = m_gridAxes->GetVisibility() > 0; - props["gridLines"] = m_gridAxes->GetGenerateGrid(); - - QJsonArray color; - double rgb[3]; - m_gridAxes->GetProperty()->GetDiffuseColor(rgb); - color << rgb[0] << rgb[1] << rgb[2]; - props["gridColor"] = color; - - props["useCustomAxesTitles"] = m_useCustomAxesTitles; - props["customXTitle"] = m_customXTitle; - props["customYTitle"] = m_customYTitle; - props["customZTitle"] = m_customZTitle; - - json["properties"] = props; - return json; -} - -bool ModuleOutline::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - m_gridAxes->SetVisibility(props["gridVisibility"].toBool() ? 1 : 0); - m_gridAxes->SetGenerateGrid(props["gridLines"].toBool()); - auto color = props["gridColor"].toArray(); - double rgb[3] = { color[0].toDouble(), color[1].toDouble(), - color[2].toDouble() }; - updateGridAxesColor(rgb); - - m_useCustomAxesTitles = props["useCustomAxesTitles"].toBool(); - if (props.contains("customXTitle")) { - m_customXTitle = props["customXTitle"].toString(); - } - if (props.contains("customYTitle")) { - m_customYTitle = props["customYTitle"].toString(); - } - if (props.contains("customZTitle")) { - m_customZTitle = props["customZTitle"].toString(); - } - updateGridAxesTitles(); - return true; - } - return false; -} - -bool ModuleOutline::setVisibility(bool val) -{ - Q_ASSERT(m_outlineRepresentation); - vtkSMPropertyHelper(m_outlineRepresentation, "Visibility").Set(val ? 1 : 0); - m_outlineRepresentation->UpdateVTKObjects(); - if (!val || m_axesVisibility) { - m_gridAxes->SetVisibility(val ? 1 : 0); - } - Module::setVisibility(val); - return true; -} - -bool ModuleOutline::visibility() const -{ - if (m_outlineRepresentation) { - return vtkSMPropertyHelper(m_outlineRepresentation, "Visibility") - .GetAsInt() != 0; - } else { - return false; - } -} - -void ModuleOutline::addToPanel(QWidget* panel) -{ - Q_ASSERT(panel && m_outlineRepresentation); - - if (panel->layout()) { - delete panel->layout(); - } - - QHBoxLayout* layout = new QHBoxLayout; - QLabel* label = new QLabel("Color"); - layout->addWidget(label); - layout->addStretch(); - pqColorChooserButton* colorSelector = new pqColorChooserButton(panel); - colorSelector->setShowAlphaChannel(false); - layout->addWidget(colorSelector); - - // Show Grid? - QHBoxLayout* showGridLayout = new QHBoxLayout; - QCheckBox* showGrid = new QCheckBox(QString("Show Grid")); - showGrid->setChecked(m_gridAxes->GetGenerateGrid()); - - connect(showGrid, &QCheckBox::checkStateChanged, this, [this](int state) { - m_gridAxes->SetGenerateGrid(state == Qt::Checked); - emit renderNeeded(); - }); - - showGridLayout->addWidget(showGrid); - - // Custom axes titles - auto* customAxesTitlesGroupBox = new QGroupBox(panel); - customAxesTitlesGroupBox->setVisible(m_useCustomAxesTitles); - auto* customAxesTitlesLayout = new QVBoxLayout(customAxesTitlesGroupBox); - - auto* customXTitleLayout = new QHBoxLayout; - auto* customYTitleLayout = new QHBoxLayout; - auto* customZTitleLayout = new QHBoxLayout; - customAxesTitlesLayout->addLayout(customXTitleLayout); - customAxesTitlesLayout->addLayout(customYTitleLayout); - customAxesTitlesLayout->addLayout(customZTitleLayout); - - customXTitleLayout->addWidget(new QLabel("X:")); - customYTitleLayout->addWidget(new QLabel("Y:")); - customZTitleLayout->addWidget(new QLabel("Z:")); - - auto* customXTitleEditor = new QLineEdit(m_customXTitle); - auto* customYTitleEditor = new QLineEdit(m_customYTitle); - auto* customZTitleEditor = new QLineEdit(m_customZTitle); - - customXTitleLayout->addWidget(customXTitleEditor); - customYTitleLayout->addWidget(customYTitleEditor); - customZTitleLayout->addWidget(customZTitleEditor); - - connect(customXTitleEditor, &QLineEdit::textChanged, this, - [this](const QString& text) { - m_customXTitle = text; - updateGridAxesTitles(); - emit renderNeeded(); - }); - - connect(customYTitleEditor, &QLineEdit::textChanged, this, - [this](const QString& text) { - m_customYTitle = text; - updateGridAxesTitles(); - emit renderNeeded(); - }); - - connect(customZTitleEditor, &QLineEdit::textChanged, this, - [this](const QString& text) { - m_customZTitle = text; - updateGridAxesTitles(); - emit renderNeeded(); - }); - - // Use custom axes titles? - auto* useCustomAxesTitlesLayout = new QHBoxLayout; - auto* useCustomAxesTitles = new QCheckBox("Custom Axes Titles"); - useCustomAxesTitles->setChecked(m_useCustomAxesTitles); - useCustomAxesTitlesLayout->addWidget(useCustomAxesTitles); - connect(useCustomAxesTitles, &QCheckBox::toggled, this, - [this, customAxesTitlesGroupBox](bool b) { - m_useCustomAxesTitles = b; - customAxesTitlesGroupBox->setVisible(b); - updateGridAxesTitles(); - emit renderNeeded(); - }); - - // Show Axes? - QHBoxLayout* showAxesLayout = new QHBoxLayout; - QCheckBox* showAxes = new QCheckBox(QString("Show Axes")); - showAxes->setChecked(m_gridAxes->GetVisibility()); - // Disable "Show Grid" if axes not enabled - if (!showAxes->isChecked()) { - showGrid->setEnabled(false); - useCustomAxesTitles->setEnabled(false); - customAxesTitlesGroupBox->setVisible(false); - } - connect(showAxes, &QCheckBox::checkStateChanged, this, - [this, showGrid, useCustomAxesTitles](int state) { - m_gridAxes->SetVisibility(state == Qt::Checked); - m_axesVisibility = state == Qt::Checked; - // Uncheck "Show Grid" and disable it - if (state == Qt::Unchecked) { - showGrid->setChecked(false); - useCustomAxesTitles->setChecked(false); - } - showGrid->setEnabled(state == Qt::Checked); - useCustomAxesTitles->setEnabled(state == Qt::Checked); - - emit renderNeeded(); - }); - showAxesLayout->addWidget(showAxes); - - QVBoxLayout* panelLayout = new QVBoxLayout; - panelLayout->addItem(layout); - panelLayout->addItem(showAxesLayout); - panelLayout->addItem(showGridLayout); - panelLayout->addItem(useCustomAxesTitlesLayout); - panelLayout->addWidget(customAxesTitlesGroupBox); - panelLayout->addStretch(); - panel->setLayout(panelLayout); - - m_links.addPropertyLink(colorSelector, "chosenColorRgbF", - SIGNAL(chosenColorChanged(const QColor&)), - m_outlineRepresentation, - m_outlineRepresentation->GetProperty("DiffuseColor")); - - connect(colorSelector, &pqColorChooserButton::chosenColorChanged, - [this](const QColor& color) { - double rgb[3]; - rgb[0] = color.redF(); - rgb[1] = color.greenF(); - rgb[2] = color.blueF(); - updateGridAxesColor(rgb); - }); - connect(colorSelector, &pqColorChooserButton::chosenColorChanged, this, - &ModuleOutline::dataUpdated); -} - -void ModuleOutline::dataUpdated() -{ - m_links.accept(); - emit renderNeeded(); -} - -void ModuleOutline::dataSourceMoved(double newX, double newY, double newZ) -{ - double pos[3] = { newX, newY, newZ }; - vtkSMPropertyHelper(m_outlineRepresentation, "Translation").Set(pos, 3); - m_outlineRepresentation->UpdateVTKObjects(); - m_gridAxes->SetPosition(newX, newY, newZ); -} - -void ModuleOutline::dataSourceRotated(double newX, double newY, double newZ) -{ - double orientation[3] = { newX, newY, newZ }; - vtkSMPropertyHelper(m_outlineRepresentation, "Orientation") - .Set(orientation, 3); - m_outlineRepresentation->UpdateVTKObjects(); - m_gridAxes->SetOrientation(newX, newY, newZ); -} - -void ModuleOutline::updateGridAxesBounds(DataSource* dataSource) -{ - Q_ASSERT(dataSource); - double bounds[6]; - dataSource->getBounds(bounds); - m_gridAxes->SetGridBounds(bounds); -} - -void ModuleOutline::initializeGridAxes(DataSource* data, - vtkSMViewProxy* vtkView) -{ - updateGridAxesBounds(data); - m_gridAxes->SetVisibility(0); - m_gridAxes->SetGenerateGrid(false); - - // Work around a bug in vtkGridAxesActor3D. GetProperty() returns the - // vtkProperty associated with a single face, so to get a property associated - // with all the faces, we need to create a new one and set it. - vtkNew prop; - prop->DeepCopy(m_gridAxes->GetProperty()); - m_gridAxes->SetProperty(prop); - - // Set mask to show labels on all axes - m_gridAxes->SetLabelMask(vtkGridAxesHelper::LabelMasks::MIN_X | - vtkGridAxesHelper::LabelMasks::MIN_Y | - vtkGridAxesHelper::LabelMasks::MIN_Z | - vtkGridAxesHelper::LabelMasks::MAX_X | - vtkGridAxesHelper::LabelMasks::MAX_Y | - vtkGridAxesHelper::LabelMasks::MAX_Z); - - // Set mask to render all faces - m_gridAxes->SetFaceMask(vtkGridAxesHelper::Faces::MAX_XY | - vtkGridAxesHelper::Faces::MAX_YZ | - vtkGridAxesHelper::Faces::MAX_ZX | - vtkGridAxesHelper::Faces::MIN_XY | - vtkGridAxesHelper::Faces::MIN_YZ | - vtkGridAxesHelper::Faces::MIN_ZX); - - // Enable front face culling - prop->SetFrontfaceCulling(1); - - // Disable back face culling - prop->SetBackfaceCulling(0); - - // Set the titles - updateGridAxesTitles(); - - m_view = vtkPVRenderView::SafeDownCast(vtkView->GetClientSideView()); - m_view->GetRenderer()->AddActor(m_gridAxes); - - connect(data, &DataSource::dataPropertiesChanged, this, [this]() { - auto dataSource = qobject_cast(sender()); - updateGridAxesBounds(dataSource); - updateGridAxesTitles(); - dataSource->proxy()->MarkModified(nullptr); - dataSource->proxy()->UpdatePipeline(); - emit renderNeeded(); - }); -} - -void ModuleOutline::updateGridAxesColor(double* color) -{ - for (int i = 0; i < 6; i++) { - vtkNew prop; - prop->SetColor(color); - m_gridAxes->SetTitleTextProperty(i, prop); - m_gridAxes->SetLabelTextProperty(i, prop); - } - m_gridAxes->GetProperty()->SetDiffuseColor(color); - vtkSMPropertyHelper(m_outlineRepresentation, "DiffuseColor").Set(color, 3); - m_outlineRepresentation->UpdateVTKObjects(); -} - -void ModuleOutline::updateGridAxesTitles() -{ - QString xTitle, yTitle, zTitle; - if (m_useCustomAxesTitles) { - xTitle = m_customXTitle; - yTitle = m_customYTitle; - zTitle = m_customZTitle; - } else { - auto units = dataSource()->getUnits(); - xTitle = QString("X (%1)").arg(units); - yTitle = QString("Y (%1)").arg(units); - zTitle = QString("Z (%1)").arg(units); - } - - m_gridAxes->SetXTitle(xTitle.toUtf8().data()); - m_gridAxes->SetYTitle(yTitle.toUtf8().data()); - m_gridAxes->SetZTitle(zTitle.toUtf8().data()); -} - -} // end of namespace tomviz diff --git a/tomviz/legacy/modules/ModuleOutline.h b/tomviz/legacy/modules/ModuleOutline.h deleted file mode 100644 index 50692d270..000000000 --- a/tomviz/legacy/modules/ModuleOutline.h +++ /dev/null @@ -1,67 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleOutline_h -#define tomvizModuleOutline_h - -#include "Module.h" -#include - -#include - -class vtkSMSourceProxy; -class vtkSMProxy; -class vtkPVRenderView; -class vtkGridAxesActor3D; - -namespace tomviz { - -/// A simple module to show the outline for any dataset. -class ModuleOutline : public Module -{ - Q_OBJECT - -public: - ModuleOutline(QObject* parent = nullptr); - ~ModuleOutline() override; - - QString label() const override { return "Outline"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - bool finalize() override; - bool setVisibility(bool val) override; - bool visibility() const override; - void addToPanel(QWidget*) override; - - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - - void dataSourceMoved(double newX, double newY, double newZ) override; - void dataSourceRotated(double newX, double newY, double newZ) override; - -private slots: - void dataUpdated(); - - void initializeGridAxes(DataSource* dataSource, vtkSMViewProxy* vtkView); - void updateGridAxesBounds(DataSource* dataSource); - void updateGridAxesColor(double* color); - void updateGridAxesTitles(); - -private: - Q_DISABLE_COPY(ModuleOutline) - vtkWeakPointer m_outlineFilter; - vtkWeakPointer m_outlineRepresentation; - vtkWeakPointer m_view; - vtkNew m_gridAxes; - pqPropertyLinks m_links; - - bool m_axesVisibility = false; - bool m_useCustomAxesTitles = false; - QString m_customXTitle = "X"; - QString m_customYTitle = "Y"; - QString m_customZTitle = "Z"; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModulePlot.cxx b/tomviz/legacy/modules/ModulePlot.cxx deleted file mode 100644 index 337da1f52..000000000 --- a/tomviz/legacy/modules/ModulePlot.cxx +++ /dev/null @@ -1,341 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModulePlot.h" - -#include "../operators/OperatorResult.h" -#include "Utilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -ModulePlot::ModulePlot(QObject* parentObject) - : Module(parentObject) - , m_visible(true) - , m_view(nullptr) - , m_table(nullptr) - , m_chart(nullptr) - , m_producer(nullptr) -{ - m_result_modified_cb->SetCallback(&ModulePlot::onResultModified); - m_result_modified_cb->SetClientData(this); -} - -ModulePlot::~ModulePlot() -{ - finalize(); -} - -QIcon ModulePlot::icon() const -{ - return QIcon(":/pqWidgets/Icons/pqLineChart16.png"); -} - -bool ModulePlot::initialize(DataSource* data, vtkSMViewProxy* view) -{ - Q_UNUSED(data); - Q_UNUSED(view); - - return false; -} - -bool ModulePlot::initialize(MoleculeSource* data, vtkSMViewProxy* view) -{ - Q_UNUSED(data); - Q_UNUSED(view); - - return false; -} - -bool ModulePlot::initialize(OperatorResult* result, vtkSMViewProxy* view) -{ - Module::initialize(result, view); - - m_table = vtkTable::SafeDownCast(result->dataObject()); - m_producer = vtkTrivialProducer::SafeDownCast(result->producerProxy()->GetClientSideObject()); - m_view = vtkPVContextView::SafeDownCast(view->GetClientSideView()); - m_chart = nullptr; - - if (m_table == nullptr || m_producer == nullptr || m_view == nullptr) { - return false; - } - - auto context_view = m_view->GetContextView(); - - m_chart = vtkChartXY::SafeDownCast(context_view->GetScene()->GetItem(0)); - - if (m_chart == nullptr) { - return false; - } - - // Detect when the result dataobject changes, i.e. when the pipeline re-runs - m_producer->AddObserver(vtkCommand::ModifiedEvent, m_result_modified_cb); - - addAllPlots(); - - return true; -} - -bool ModulePlot::finalize() -{ - if (m_producer) { - m_producer->RemoveObserver(m_result_modified_cb); - } - - removeAllPlots(); - - m_plots.clear(); - - return true; -} - -void ModulePlot::addAllPlots() -{ - removeAllPlots(); - - if (m_table == nullptr || m_chart == nullptr) { - return; - } - - vtkIdType num_cols = m_table->GetNumberOfColumns(); - - auto fieldData = m_table->GetFieldData(); - auto labelsArray = vtkStringArray::SafeDownCast( - fieldData->GetAbstractArray("axes_labels")); - if (labelsArray && labelsArray->GetNumberOfTuples() >= 2) { - auto x_axis = m_chart->GetAxis(vtkAxis::BOTTOM); - auto y_axis = m_chart->GetAxis(vtkAxis::LEFT); - x_axis->SetTitle(labelsArray->GetValue(0)); - y_axis->SetTitle(labelsArray->GetValue(1)); - } - - auto logScaleArray = vtkUnsignedCharArray::SafeDownCast( - fieldData->GetAbstractArray("axes_log_scale")); - if (logScaleArray && logScaleArray->GetNumberOfTuples() >= 2) { - auto x_axis = m_chart->GetAxis(vtkAxis::BOTTOM); - auto y_axis = m_chart->GetAxis(vtkAxis::LEFT); - x_axis->SetLogScale(logScaleArray->GetValue(0) != 0); - y_axis->SetLogScale(logScaleArray->GetValue(1) != 0); - } - - // Start color index from the number of plots already in the chart - // so that multiple modules sharing a view get distinct colors. - int colorOffset = m_chart->GetNumberOfPlots(); - - for (vtkIdType col = 1; col < num_cols; col++) { - auto line = vtkSmartPointer::New(); - int idx = colorOffset + col - 1; - // Golden angle spacing in hue for maximum color separation - int hue = (idx * 137) % 360; - QColor color = QColor::fromHsv(hue, 200, 200); - line->SetInputData(m_table, 0, col); - line->SetColor(color.red(), color.green(), color.blue(), 255); - line->SetWidth(3.0); - m_chart->AddPlot(line); - m_plots.append(line); - } -} - -void ModulePlot::removeAllPlots() -{ - if (m_chart == nullptr) { - return; - } - - for (auto iter = m_plots.begin(); iter != m_plots.end(); iter++) { - m_chart->RemovePlotInstance(*iter); - } - - m_plots.clear(); -} - -bool ModulePlot::setVisibility(bool val) -{ - m_visible = val; - - if (val) { - addAllPlots(); - } else { - removeAllPlots(); - } - - Module::setVisibility(val); - - return true; -} - -bool ModulePlot::visibility() const -{ - return m_visible; -} - -void ModulePlot::addToPanel(QWidget* panel) -{ - if (panel->layout()) { - delete panel->layout(); - } - - QFormLayout* layout = new QFormLayout; - - QString xLabel, yLabel; - bool xLogScale = false; - bool yLogScale = false; - - if (m_chart) { - xLabel = m_chart->GetAxis(vtkAxis::BOTTOM)->GetTitle().c_str(); - yLabel = m_chart->GetAxis(vtkAxis::LEFT)->GetTitle().c_str(); - xLogScale = m_chart->GetAxis(vtkAxis::BOTTOM)->GetLogScale(); - yLogScale = m_chart->GetAxis(vtkAxis::LEFT)->GetLogScale(); - } - - m_xLabelEdit = new QLineEdit(xLabel); - m_yLabelEdit = new QLineEdit(yLabel); - layout->addRow("X Label", m_xLabelEdit); - layout->addRow("Y Label", m_yLabelEdit); - - m_xLogCheckBox = new QCheckBox("X Log Scale"); - m_yLogCheckBox = new QCheckBox("Y Log Scale"); - m_xLogCheckBox->setChecked(xLogScale); - m_yLogCheckBox->setChecked(yLogScale); - layout->addRow(m_xLogCheckBox); - layout->addRow(m_yLogCheckBox); - - connect(m_xLabelEdit, &QLineEdit::textChanged, this, - &ModulePlot::onXLabelChanged); - connect(m_yLabelEdit, &QLineEdit::textChanged, this, - &ModulePlot::onYLabelChanged); - connect(m_xLogCheckBox, &QCheckBox::toggled, this, - &ModulePlot::onXLogScaleChanged); - connect(m_yLogCheckBox, &QCheckBox::toggled, this, - &ModulePlot::onYLogScaleChanged); - - panel->setLayout(layout); -} - -QJsonObject ModulePlot::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - // Save the operator result name so the module can be recreated on - // deserialization by finding the matching OperatorResult. - if (operatorResult()) { - json["operatorResultName"] = operatorResult()->name(); - } - - json["properties"] = props; - return json; -} - -bool ModulePlot::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - return true; - } - return false; -} - -void ModulePlot::dataSourceMoved(double, double, double) -{ -} - -void ModulePlot::dataSourceRotated(double, double, double) -{ -} - -vtkDataObject* ModulePlot::dataToExport() -{ - return nullptr; -} - -void ModulePlot::onResultModified(vtkObject* caller, long unsigned int eventId, void* clientData, void*callData) -{ - Q_UNUSED(caller); - Q_UNUSED(eventId); - Q_UNUSED(callData); - - auto self = reinterpret_cast(clientData); - auto result = self->operatorResult(); - self->m_table = vtkTable::SafeDownCast(result->dataObject()); - - self->removeAllPlots(); - self->m_plots.clear(); - - if (self->visibility()) { - self->addAllPlots(); - } -} - -void ModulePlot::onXLogScaleChanged(bool logScale) -{ - if (m_chart == nullptr) { - return; - } - - auto x_axis = m_chart->GetAxis(vtkAxis::BOTTOM); - x_axis->SetLogScale(logScale); - - m_view->Update(); -} - -void ModulePlot::onYLogScaleChanged(bool logScale) -{ - if (m_chart == nullptr) { - return; - } - - auto y_axis = m_chart->GetAxis(vtkAxis::LEFT); - y_axis->SetLogScale(logScale); - - m_view->Update(); -} - -void ModulePlot::onXLabelChanged(const QString& label) -{ - if (m_chart == nullptr) { - return; - } - - auto x_axis = m_chart->GetAxis(vtkAxis::BOTTOM); - x_axis->SetTitle(label.toStdString()); - - m_view->Update(); -} - -void ModulePlot::onYLabelChanged(const QString& label) -{ - if (m_chart == nullptr) { - return; - } - - auto y_axis = m_chart->GetAxis(vtkAxis::LEFT); - y_axis->SetTitle(label.toStdString()); - - m_view->Update(); -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModulePlot.h b/tomviz/legacy/modules/ModulePlot.h deleted file mode 100644 index 7dd29f212..000000000 --- a/tomviz/legacy/modules/ModulePlot.h +++ /dev/null @@ -1,78 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModulePlot_h -#define tomvizModulePlot_h - -#include "Module.h" - -class vtkCallbackCommand; -class vtkChartXY; -class vtkPlot; -class vtkPVContextView; -class vtkTable; -class vtkTrivialProducer; - -class QCheckBox; -class QLineEdit; - - -namespace tomviz { - -class MoleculeSource; -class OperatorResult; - -class ModulePlot : public Module -{ - Q_OBJECT - -public: - ModulePlot(QObject* parent = nullptr); - ~ModulePlot() override; - - QString label() const override { return "Plot"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* data, vtkSMViewProxy* vtkView) override; - bool initialize(MoleculeSource* data, vtkSMViewProxy* vtkView) override; - bool initialize(OperatorResult* result, vtkSMViewProxy* view) override; - bool finalize() override; - bool setVisibility(bool val) override; - bool visibility() const override; - void addToPanel(QWidget*) override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - - QString exportDataTypeString() override { return ""; } - vtkDataObject* dataToExport() override; - - void dataSourceMoved(double newX, double newY, double newZ) override; - void dataSourceRotated(double newX, double newY, double newZ) override; - -private slots: - void onXLogScaleChanged(bool); - void onYLogScaleChanged(bool); - void onXLabelChanged(const QString& label); - void onYLabelChanged(const QString& label); - -private: - static void onResultModified(vtkObject* caller, long unsigned int eventId, void* clientData, void*callData); - void addAllPlots(); - void removeAllPlots(); - - Q_DISABLE_COPY(ModulePlot) - bool m_visible; - vtkWeakPointer m_view; - vtkNew m_result_modified_cb; - vtkWeakPointer m_table; - vtkWeakPointer m_chart; - vtkWeakPointer m_producer; - QList> m_plots; - QPointer m_xLogCheckBox; - QPointer m_yLogCheckBox; - QPointer m_xLabelEdit; - QPointer m_yLabelEdit; - -}; -} // namespace tomviz -#endif diff --git a/tomviz/legacy/modules/ModuleRuler.cxx b/tomviz/legacy/modules/ModuleRuler.cxx deleted file mode 100644 index 3ec5015c7..000000000 --- a/tomviz/legacy/modules/ModuleRuler.cxx +++ /dev/null @@ -1,246 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleRuler.h" - -#include "ActiveObjects.h" -#include "../DataSource.h" -#include "Utilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace tomviz { - -ModuleRuler::ModuleRuler(QObject* p) : Module(p) {} - -ModuleRuler::~ModuleRuler() -{ - finalize(); -} - -QIcon ModuleRuler::icon() const -{ - return QIcon(":/pqWidgets/Icons/pqRuler.svg"); -} - -bool ModuleRuler::initialize(DataSource* data, vtkSMViewProxy* view) -{ - if (!Module::initialize(data, view)) { - return false; - } - vtkNew controller; - - auto pxm = data->proxy()->GetSessionProxyManager(); - auto alg = vtkAlgorithm::SafeDownCast(data->producer()); - double bounds[6]; - vtkDataSet::SafeDownCast(alg->GetOutputDataObject(0))->GetBounds(bounds); - double boundsMin[3] = { bounds[0], bounds[2], bounds[4] }; - double boundsMax[3] = { bounds[1], bounds[3], bounds[5] }; - - m_rulerSource.TakeReference( - vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("sources", "Ruler"))); - vtkSMPropertyHelper(m_rulerSource, "Point1").Set(boundsMin, 3); - vtkSMPropertyHelper(m_rulerSource, "Point2").Set(boundsMax, 3); - m_rulerSource->UpdateVTKObjects(); - controller->RegisterPipelineProxy(m_rulerSource); - - m_representation = controller->Show(m_rulerSource, 0, view); - - m_representation->UpdateVTKObjects(); - - updateUnits(); - - connect(data, &DataSource::dataChanged, this, &ModuleRuler::updateUnits); - - return m_representation && m_rulerSource; -} - -bool ModuleRuler::finalize() -{ - vtkNew controller; - controller->UnRegisterProxy(m_representation); - controller->UnRegisterProxy(m_rulerSource); - m_representation = nullptr; - m_rulerSource = nullptr; - return true; -} - -void ModuleRuler::addToPanel(QWidget* panel) -{ - if (panel->layout()) { - delete panel->layout(); - } - QVBoxLayout* layout = new QVBoxLayout; - - m_widget = new pqLinePropertyWidget( - m_rulerSource, m_rulerSource->GetPropertyGroup(0), panel); - layout->addWidget(m_widget); - m_widget->setView( - tomviz::convert(ActiveObjects::instance().activeView())); - m_widget->select(); - m_widget->setWidgetVisible(m_showLine); - layout->addStretch(); - connect(m_widget.data(), &pqPropertyWidget::changeFinished, m_widget.data(), - &pqPropertyWidget::apply); - connect(m_widget.data(), &pqPropertyWidget::changeFinished, this, - &ModuleRuler::endPointsUpdated); - connect(m_widget, &pqInteractivePropertyWidgetAbstract::widgetVisibilityUpdated, - this, &ModuleRuler::updateShowLine); - - m_widget->setWidgetVisible(m_showLine); - - QLabel* label0 = new QLabel("Point 0 data value: "); - QLabel* label1 = new QLabel("Point 1 data value: "); - connect(this, &ModuleRuler::newEndpointData, label0, - [label0, label1](double val0, double val1) { - label0->setText(QString("Point 0 data value: %1").arg(val0)); - label1->setText(QString("Point 1 data value: %1").arg(val1)); - }); - layout->addWidget(label0); - layout->addWidget(label1); - panel->setLayout(layout); -} - -void ModuleRuler::prepareToRemoveFromPanel(QWidget* vtkNotUsed(panel)) -{ - // Disconnect before the panel is removed to avoid m_showLine always being set - // to false when the signal widgetVisibilityUpdated(bool) is emitted during - // the tear down of the pqLinePropertyWidget. - disconnect(m_widget, &pqInteractivePropertyWidgetAbstract::widgetVisibilityUpdated, - this, &ModuleRuler::updateShowLine); -} - -bool ModuleRuler::setVisibility(bool val) -{ - vtkSMPropertyHelper(m_representation, "Visibility").Set(val ? 1 : 0); - m_representation->UpdateVTKObjects(); - if (m_widget) { - // calling setWidgetVisible triggers the signal that updates the value of - // m_showLine. But in this case the user is toggling the whole module so - // we don't want m_showLine to update and we cache it locally and restore - // it after calling setWidgetVisible. - bool oldValue = m_showLine; - m_widget->setWidgetVisible(val && m_showLine); - m_showLine = oldValue; - } - - Module::setVisibility(val); - - return true; -} - -bool ModuleRuler::visibility() const -{ - if (m_representation) { - return vtkSMPropertyHelper(m_representation, "Visibility").GetAsInt() != 0; - } else { - return false; - } -} - -QJsonObject ModuleRuler::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - props["showLine"] = m_showLine; - double p1[3]; - double p2[3]; - vtkSMPropertyHelper(m_rulerSource, "Point1").Get(p1, 3); - vtkSMPropertyHelper(m_rulerSource, "Point2").Get(p2, 3); - QJsonArray point1 = { p1[0], p1[1], p1[2] }; - QJsonArray point2 = { p2[0], p2[1], p2[2] }; - props["point1"] = point1; - props["point2"] = point2; - - json["properties"] = props; - return json; -} - -bool ModuleRuler::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - m_showLine = props["showLine"].toBool(); - auto point1 = props["point1"].toArray(); - auto point2 = props["point2"].toArray(); - double p1[3] = { point1[0].toDouble(), point1[1].toDouble(), - point1[2].toDouble() }; - double p2[3] = { point2[0].toDouble(), point2[1].toDouble(), - point2[2].toDouble() }; - vtkSMPropertyHelper(m_rulerSource, "Point1").Set(p1, 3); - vtkSMPropertyHelper(m_rulerSource, "Point2").Set(p2, 3); - m_rulerSource->UpdateVTKObjects(); - return true; - } - return false; -} - -void ModuleRuler::updateUnits() -{ - DataSource* source = dataSource(); - QString units = source->getUnits(); - vtkRulerSourceRepresentation* rep = - vtkRulerSourceRepresentation::SafeDownCast( - m_representation->GetClientSideObject()); - QString labelFormat = "%-#6.3g %1"; - rep->SetLabelFormat(labelFormat.arg(units).toLatin1().data()); -} - -void ModuleRuler::updateShowLine(bool show) -{ - m_showLine = show; -} - -void ModuleRuler::endPointsUpdated() -{ - double point1[3]; - double point2[3]; - vtkSMPropertyHelper(m_rulerSource, "Point1").Get(point1, 3); - vtkSMPropertyHelper(m_rulerSource, "Point2").Get(point2, 3); - DataSource* source = dataSource(); - auto* algo = - vtkAlgorithm::SafeDownCast(source->proxy()->GetClientSideObject()); - if (!algo) { - return; - } - vtkImageData* img = - vtkImageData::SafeDownCast(algo->GetOutputDataObject(0)); - if (!img) { - return; - } - vtkDataArray* scalars = img->GetPointData()->GetScalars(); - if (!scalars) { - return; - } - vtkIdType p1 = img->FindPoint(point1); - vtkIdType p2 = img->FindPoint(point2); - double v1 = scalars->GetTuple1(p1); - double v2 = scalars->GetTuple1(p2); - emit newEndpointData(v1, v2); - renderNeeded(); -} -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleRuler.h b/tomviz/legacy/modules/ModuleRuler.h deleted file mode 100644 index 194d61ecd..000000000 --- a/tomviz/legacy/modules/ModuleRuler.h +++ /dev/null @@ -1,64 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleRuler_h -#define tomvizModuleRuler_h - -#include "Module.h" - -#include - -class vtkSMSourceProxy; -class vtkSMProxy; - -class pqLinePropertyWidget; - -namespace tomviz { - -class ModuleRuler : public Module -{ - Q_OBJECT - -public: - ModuleRuler(QObject* parent = nullptr); - virtual ~ModuleRuler(); - - QString label() const override { return "Ruler"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - bool finalize() override; - void addToPanel(QWidget*) override; - void prepareToRemoveFromPanel(QWidget* panel) override; - bool setVisibility(bool val) override; - bool visibility() const override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - bool isColorMapNeeded() const override { return false; } - - void dataSourceMoved(double, double, double) override {} - void dataSourceRotated(double, double, double) override {} - -protected slots: - void updateUnits(); - void updateShowLine(bool show); - void endPointsUpdated(); - -signals: - void newEndpointData(double val1, double val2); - -protected: - void updateColorMap() override {} - - vtkSmartPointer m_rulerSource; - vtkSmartPointer m_representation; - QPointer m_widget; - -private: - Q_DISABLE_COPY(ModuleRuler) - - bool m_showLine = true; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleScaleCube.cxx b/tomviz/legacy/modules/ModuleScaleCube.cxx deleted file mode 100644 index 6757ab317..000000000 --- a/tomviz/legacy/modules/ModuleScaleCube.cxx +++ /dev/null @@ -1,348 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleScaleCube.h" -#include "ModuleScaleCubeWidget.h" - -#include "../DataSource.h" -#include "Utilities.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -ModuleScaleCube::ModuleScaleCube(QObject* parentObject) : Module(parentObject) -{ - // Connect to m_cubeRep's "modified" signal, and emit it as our own - // "onPositionChanged" signal - m_observedPositionId = - pqCoreUtilities::connect(m_cubeRep, vtkCommand::ModifiedEvent, this, - SIGNAL(onPositionChanged())); - - // Connect to our "onPositionChanged" signal and emit it with arguments - connect(this, - (void (ModuleScaleCube::*)())(&ModuleScaleCube::onPositionChanged), - this, [&]() { - double p[3]; - m_cubeRep->GetWorldPosition(p); - onPositionChanged(p[0], p[1], p[2]); - }); - - connect(this, - QOverload::of(&ModuleScaleCube::onPositionChanged), - this, &ModuleScaleCube::updateOffset); - - // Connect to m_cubeRep's "modified" signal, and emit it as our own - // "onSideLengthChanged" signal - m_observedSideLengthId = - pqCoreUtilities::connect(m_cubeRep, vtkCommand::ModifiedEvent, this, - SIGNAL(onSideLengthChanged())); - - // Connect to our "onSideLengthChanged" signal and emit it with arguments - connect(this, - (void (ModuleScaleCube::*)())(&ModuleScaleCube::onSideLengthChanged), - this, [&]() { onSideLengthChanged(m_cubeRep->GetSideLength()); }); -} - -ModuleScaleCube::~ModuleScaleCube() -{ - m_cubeRep->RemoveObserver(m_observedPositionId); - m_cubeRep->RemoveObserver(m_observedSideLengthId); - - finalize(); -} - -QIcon ModuleScaleCube::icon() const -{ - return QIcon(":/icons/pqMeasurementCube.png"); -} - -bool ModuleScaleCube::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - if (!Module::initialize(data, vtkView)) { - return false; - } - - connect(data, &DataSource::dataPropertiesChanged, this, - &ModuleScaleCube::dataPropertiesChanged); - - m_view = vtkPVRenderView::SafeDownCast(vtkView->GetClientSideView()); - m_handleWidget->SetInteractor(m_view->GetInteractor()); - - double bounds[6]; - dataSource()->proxy()->GetDataInformation()->GetBounds(bounds); - double length = std::max(floor((bounds[1] - bounds[0]) * .1), 1.); - m_cubeRep->SetSideLength(length); - m_cubeRep->SetAdaptiveScaling(0); - m_cubeRep->SetLengthUnit(data->getUnits().toStdString().c_str()); - - m_offset[0] = 0.5 * length; - m_offset[1] = 0.5 * length; - m_offset[2] = 0.5 * length; - - const double* displayPosition = dataSource()->displayPosition(); - dataSourceMoved(displayPosition[0], displayPosition[1], displayPosition[2]); - - m_handleWidget->SetRepresentation(m_cubeRep); - m_handleWidget->EnabledOn(); - - return true; -} - -bool ModuleScaleCube::finalize() -{ - return true; -} - -bool ModuleScaleCube::visibility() const -{ - return m_cubeRep->GetHandleVisibility() == 1; -} - -bool ModuleScaleCube::setVisibility(bool choice) -{ - m_cubeRep->SetHandleVisibility(choice ? 1 : 0); - if (!choice || m_annotationVisibility) { - m_cubeRep->SetLabelVisibility(choice ? 1 : 0); - } - - Module::setVisibility(choice); - - return true; -} -QJsonObject ModuleScaleCube::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - props["adaptiveScaling"] = m_cubeRep->GetAdaptiveScaling() == 1; - props["sideLength"] = m_cubeRep->GetSideLength(); - double p[3]; - m_cubeRep->GetWorldPosition(p); - QJsonArray position = { p[0], p[1], p[2] }; - props["position"] = position; - props["annotation"] = m_cubeRep->GetLabelVisibility() == 1; - double c[3]; - m_cubeRep->GetProperty()->GetDiffuseColor(c); - QJsonArray color = { c[0], c[1], c[2] }; - props["color"] = color; - - m_cubeRep->GetLabelText()->GetTextProperty()->GetColor(c); - color = { c[0], c[1], c[2] }; - props["textColor"] = color; - - json["properties"] = props; - - return json; -} - -bool ModuleScaleCube::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - m_cubeRep->SetAdaptiveScaling(props["adaptiveScaling"].toBool() ? 1 : 0); - m_cubeRep->SetSideLength(props["sideLength"].toDouble()); - auto p = props["position"].toArray(); - double pos[3] = { p[0].toDouble(), p[1].toDouble(), p[2].toDouble() }; - m_cubeRep->SetWorldPosition(pos); - m_cubeRep->SetLabelVisibility(props["annotation"].toBool() ? 1 : 0); - auto c = props["color"].toArray(); - double color[3] = { c[0].toDouble(), c[1].toDouble(), c[2].toDouble() }; - m_cubeRep->GetProperty()->SetDiffuseColor(color); - - if (props["textColor"].isArray()) { - // This property was added later on... - c = props["textColor"].toArray(); - double textColor[3] = { c[0].toDouble(), c[1].toDouble(), - c[2].toDouble() }; - m_cubeRep->GetLabelText()->GetTextProperty()->SetColor(textColor); - if (m_controllers) { - QColor qTextColor(static_cast(textColor[0] * 255.0 + 0.5), - static_cast(textColor[1] * 255.0 + 0.5), - static_cast(textColor[2] * 255.0 + 0.5)); - m_controllers->setTextColor(qTextColor); - } - } - - if (m_controllers) { - QColor qColor(static_cast(color[0] * 255.0 + 0.5), - static_cast(color[1] * 255.0 + 0.5), - static_cast(color[2] * 255.0 + 0.5)); - m_controllers->setBoxColor(qColor); - m_controllers->setAdaptiveScaling(props["adaptiveScaling"].toBool()); - } - return true; - } - return false; -} - -void ModuleScaleCube::addToPanel(QWidget* panel) -{ - if (panel->layout()) { - delete panel->layout(); - } - - QVBoxLayout* layout = new QVBoxLayout; - panel->setLayout(layout); - - // Create, update and connect - m_controllers = new ModuleScaleCubeWidget; - layout->addWidget(m_controllers); - - // Set initial parameters - m_controllers->setAdaptiveScaling( - static_cast(m_cubeRep->GetAdaptiveScaling())); - m_controllers->setSideLength(m_cubeRep->GetSideLength()); - m_controllers->setAnnotation( - static_cast(m_cubeRep->GetLabelVisibility())); - m_controllers->setLengthUnit(QString(m_cubeRep->GetLengthUnit())); - double worldPosition[3]; - m_cubeRep->GetWorldPosition(worldPosition); - m_controllers->setPosition(worldPosition[0], worldPosition[1], - worldPosition[2]); - m_controllers->setPositionUnit(QString(m_cubeRep->GetLengthUnit())); - double color[3]; - m_cubeRep->GetProperty()->GetDiffuseColor(color); - m_controllers->setBoxColor(QColor(static_cast(color[0] * 255.0 + 0.5), - static_cast(color[1] * 255.0 + 0.5), - static_cast(color[2] * 255.0 + 0.5))); - - m_cubeRep->GetLabelText()->GetTextProperty()->GetColor(color); - m_controllers->setTextColor(QColor(static_cast(color[0] * 255.0 + 0.5), - static_cast(color[1] * 255.0 + 0.5), - static_cast(color[2] * 255.0 + 0.5))); - - // Connect the widget's signals to this class' slots - connect(m_controllers, &ModuleScaleCubeWidget::adaptiveScalingToggled, this, - &ModuleScaleCube::setAdaptiveScaling); - connect(m_controllers, &ModuleScaleCubeWidget::sideLengthChanged, this, - &ModuleScaleCube::setSideLength); - connect(m_controllers, &ModuleScaleCubeWidget::annotationToggled, this, - &ModuleScaleCube::setAnnotation); - connect(m_controllers, &ModuleScaleCubeWidget::boxColorChanged, this, - &ModuleScaleCube::onBoxColorChanged); - connect(m_controllers, &ModuleScaleCubeWidget::textColorChanged, this, - &ModuleScaleCube::onTextColorChanged); - - // Connect this class' signals to the widget's slots - connect(this, &ModuleScaleCube::onLengthUnitChanged, m_controllers, - &ModuleScaleCubeWidget::setLengthUnit); - connect(this, &ModuleScaleCube::onPositionUnitChanged, m_controllers, - &ModuleScaleCubeWidget::setPositionUnit); - connect(this, QOverload::of(&ModuleScaleCube::onSideLengthChanged), - m_controllers, &ModuleScaleCubeWidget::setSideLength); - connect(this, - QOverload::of( - &ModuleScaleCube::onPositionChanged), - m_controllers, &ModuleScaleCubeWidget::setPosition); -} - -void ModuleScaleCube::setAdaptiveScaling(const bool val) -{ - m_cubeRep->SetAdaptiveScaling(val ? 1 : 0); -} - -void ModuleScaleCube::setSideLength(const double length) -{ - m_cubeRep->SetSideLength(length); - emit renderNeeded(); -} - -void ModuleScaleCube::setAnnotation(const bool val) -{ - m_cubeRep->SetLabelVisibility(val ? 1 : 0); - m_annotationVisibility = val; - emit renderNeeded(); -} - -void ModuleScaleCube::setLengthUnit() -{ - DataSource* data = qobject_cast(sender()); - if (!data) { - return; - } - QString s = data->getUnits(); - m_cubeRep->SetLengthUnit(s.toStdString().c_str()); - emit onLengthUnitChanged(s); -} - -void ModuleScaleCube::setPositionUnit() -{ - DataSource* data = qobject_cast(sender()); - if (!data) { - return; - } - emit onLengthUnitChanged(data->getUnits()); -} - -void ModuleScaleCube::dataPropertiesChanged() -{ - DataSource* data = qobject_cast(sender()); - if (!data) { - return; - } - m_cubeRep->SetLengthUnit(data->getUnits().toStdString().c_str()); - - emit onLengthUnitChanged(data->getUnits()); - emit onPositionUnitChanged(data->getUnits()); -} - -void ModuleScaleCube::dataSourceMoved(double newX, double newY, double newZ) -{ - double position[3]; - position[0] = newX + m_offset[0]; - position[1] = newY + m_offset[1]; - position[2] = newZ + m_offset[2]; - - m_cubeRep->PlaceWidget(position); - m_cubeRep->SetWorldPosition(position); -} - -void ModuleScaleCube::onBoxColorChanged(const QColor& color) -{ - m_cubeRep->GetProperty()->SetDiffuseColor( - color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0); - emit renderNeeded(); -} - -void ModuleScaleCube::onTextColorChanged(const QColor& color) -{ - m_cubeRep->GetLabelText()->GetTextProperty()->SetColor( - color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0); - emit renderNeeded(); -} - -void ModuleScaleCube::updateOffset(double x, double y, double z) -{ - const double* displayPosition = dataSource()->displayPosition(); - m_offset[0] = x - displayPosition[0]; - m_offset[1] = y - displayPosition[1]; - m_offset[2] = z - displayPosition[2]; -} - -} // end of namespace tomviz diff --git a/tomviz/legacy/modules/ModuleScaleCube.h b/tomviz/legacy/modules/ModuleScaleCube.h deleted file mode 100644 index c5cf8ad02..000000000 --- a/tomviz/legacy/modules/ModuleScaleCube.h +++ /dev/null @@ -1,92 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleScaleCube_h -#define tomvizModuleScaleCube_h - -#include "Module.h" - -#include -#include - -class vtkPVRenderView; - -class vtkHandleWidget; -class vtkMeasurementCubeHandleRepresentation3D; - -namespace tomviz { - -class ModuleScaleCubeWidget; - -class ModuleScaleCube : public Module -{ - Q_OBJECT - -public: - ModuleScaleCube(QObject* parent = nullptr); - ~ModuleScaleCube() override; - - QString label() const override { return "Scale Cube"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - bool finalize() override; - bool visibility() const override; - bool setVisibility(bool choice) override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - void addToPanel(QWidget* panel) override; - - void dataSourceMoved(double, double, double) override; - void dataSourceRotated(double, double, double) override {} - -protected slots: - void dataPropertiesChanged(); - -private: - Q_DISABLE_COPY(ModuleScaleCube) - - vtkWeakPointer m_view; - vtkNew m_handleWidget; - vtkNew m_cubeRep; - ModuleScaleCubeWidget* m_controllers = nullptr; - unsigned long m_observedPositionId; - unsigned long m_observedSideLengthId; - bool m_annotationVisibility = true; - double m_offset[3]; - -signals: - /** - * relaying changes from m_cubeRep. - */ - void onPositionChanged(); - void onPositionChanged(const double, const double, const double); - - void onSideLengthChanged(); - void onSideLengthChanged(const double); - - /** - * Relaying changes from the data - */ - void onLengthUnitChanged(const QString); - void onPositionUnitChanged(const QString); - -private slots: - /** - * Actuator methods for m_cubeRep. These slots should be connected to the - * appropriate UI signals. - */ - void setAdaptiveScaling(const bool); - void setSideLength(const double); - void setAnnotation(const bool); - - void setLengthUnit(); - void setPositionUnit(); - void onBoxColorChanged(const QColor& color); - void onTextColorChanged(const QColor& color); - - void updateOffset(double, double, double); -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleScaleCubeWidget.cxx b/tomviz/legacy/modules/ModuleScaleCubeWidget.cxx deleted file mode 100644 index b214bf569..000000000 --- a/tomviz/legacy/modules/ModuleScaleCubeWidget.cxx +++ /dev/null @@ -1,92 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleScaleCubeWidget.h" -#include "ui_ModuleScaleCubeWidget.h" - -#include -#include - -namespace tomviz { - -ModuleScaleCubeWidget::ModuleScaleCubeWidget(QWidget* parent_) - : QWidget(parent_), m_ui(new Ui::ModuleScaleCubeWidget) -{ - m_ui->setupUi(this); - m_ui->leSideLength->setValidator(new QDoubleValidator(this)); - - connect(m_ui->chbAdaptiveScaling, &QCheckBox::toggled, this, - &ModuleScaleCubeWidget::adaptiveScalingToggled); - connect(m_ui->leSideLength, &QLineEdit::editingFinished, this, - [&] { sideLengthChanged(m_ui->leSideLength->text().toDouble()); }); - connect(m_ui->chbAnnotation, &QCheckBox::toggled, this, - &ModuleScaleCubeWidget::annotationToggled); - connect(m_ui->colorChooserButton, &pqColorChooserButton::chosenColorChanged, - this, &ModuleScaleCubeWidget::boxColorChanged); - connect(m_ui->textColorChooserButton, - &pqColorChooserButton::chosenColorChanged, this, - &ModuleScaleCubeWidget::textColorChanged); -} - -ModuleScaleCubeWidget::~ModuleScaleCubeWidget() = default; - -void ModuleScaleCubeWidget::setAdaptiveScaling(const bool choice) -{ - m_ui->chbAdaptiveScaling->setChecked(choice); -} - -void ModuleScaleCubeWidget::setSideLength(const double length) -{ - m_ui->leSideLength->setText(QString::number(length)); -} - -void ModuleScaleCubeWidget::setAnnotation(const bool choice) -{ - m_ui->chbAnnotation->setChecked(choice); -} - -void ModuleScaleCubeWidget::setLengthUnit(const QString unit) -{ - m_ui->tlLengthUnit->setText(unit); -} - -void ModuleScaleCubeWidget::setPosition(const double x, const double y, - const double z) -{ - QString s; - QTextStream(&s) << "(" << QString::number(x, 'f', 4) << ", " - << QString::number(y, 'f', 4) << ", " - << QString::number(z, 'f', 4) << ")"; - m_ui->tlPosition->setText(s); -} - -void ModuleScaleCubeWidget::setPositionUnit(const QString unit) -{ - m_ui->tlPositionUnit->setText(unit); -} - -void ModuleScaleCubeWidget::setBoxColor(const QColor& color) -{ - m_ui->colorChooserButton->setChosenColor(color); -} - -void ModuleScaleCubeWidget::setTextColor(const QColor& color) -{ - m_ui->textColorChooserButton->setChosenColor(color); -} - -void ModuleScaleCubeWidget::onAdaptiveScalingChanged(const bool state) -{ - emit adaptiveScalingToggled(state); -} - -void ModuleScaleCubeWidget::onSideLengthChanged(const double length) -{ - emit sideLengthChanged(length); -} - -void ModuleScaleCubeWidget::onAnnotationChanged(const bool state) -{ - emit annotationToggled(state); -} -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleScaleCubeWidget.h b/tomviz/legacy/modules/ModuleScaleCubeWidget.h deleted file mode 100644 index f1fae79fb..000000000 --- a/tomviz/legacy/modules/ModuleScaleCubeWidget.h +++ /dev/null @@ -1,72 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleScaleCubeWidget_h -#define tomvizModuleScaleCubeWidget_h - -#include -#include - -/** - * \brief UI layer of ModuleScaleCube. - * - * Signals are forwarded to the actual actuators in ModuleScaleCube. - * This class is intended to contain only logic related to UI actions. - */ - -namespace Ui { -class ModuleScaleCubeWidget; -} - -namespace tomviz { - -class ModuleScaleCubeWidget : public QWidget -{ - Q_OBJECT - -public: - ModuleScaleCubeWidget(QWidget* parent_ = nullptr); - ~ModuleScaleCubeWidget(); - -signals: - //@{ - /** - * Forwarded signals. - */ - void adaptiveScalingToggled(const bool state); - void sideLengthChanged(const double length); - void annotationToggled(const bool state); - void boxColorChanged(QColor color); - void textColorChanged(QColor color); - //@} - -public slots: - //@{ - /** - * UI update methods. The actual module state is stored in - * ModuleScaleCube, so the UI needs to be updated if the state changes - * or when constructing the UI. - */ - void setAdaptiveScaling(const bool choice); - void setAnnotation(const bool choice); - void setLengthUnit(const QString unit); - void setPositionUnit(const QString unit); - void setSideLength(const double length); - void setPosition(const double x, const double y, const double z); - void setBoxColor(const QColor& color); - void setTextColor(const QColor& color); - //@} - -private: - ModuleScaleCubeWidget(const ModuleScaleCubeWidget&) = delete; - void operator=(const ModuleScaleCubeWidget&) = delete; - - QScopedPointer m_ui; - -private slots: - void onAdaptiveScalingChanged(const bool state); - void onSideLengthChanged(const double length); - void onAnnotationChanged(const bool state); -}; -} // namespace tomviz -#endif diff --git a/tomviz/legacy/modules/ModuleScaleCubeWidget.ui b/tomviz/legacy/modules/ModuleScaleCubeWidget.ui deleted file mode 100644 index 4eaa58f0e..000000000 --- a/tomviz/legacy/modules/ModuleScaleCubeWidget.ui +++ /dev/null @@ -1,171 +0,0 @@ - - - ModuleScaleCubeWidget - - - - 0 - 0 - 818 - 728 - - - - - 0 - 0 - - - - Form - - - - - - Adaptive scaling - - - - - - - - - - - - - - 0 - - - - - Length of side ( - - - - - - - TextLabel - - - - - - - ) - - - - - - - - - - - - 0 - - - - - Position ( - - - - - - - TextLabel - - - - - - - ) - - - - - - - - - - 0 - 0 - - - - - 200 - 0 - - - - Qt::LeftToRight - - - TextLabel - - - - - - - - - - - - - - Annotation - - - - - - - Box Color - - - - - - - ... - - - - - - - Text Color - - - - - - - ... - - - - - - - - pqColorChooserButton - QToolButton -
pqColorChooserButton.h
-
-
- - -
diff --git a/tomviz/legacy/modules/ModuleSegment.cxx b/tomviz/legacy/modules/ModuleSegment.cxx deleted file mode 100644 index cc5a604eb..000000000 --- a/tomviz/legacy/modules/ModuleSegment.cxx +++ /dev/null @@ -1,261 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleSegment.h" - -#include "../DataSource.h" -#include "Utilities.h" -#include "pqCoreUtilities.h" -#include "pqProxiesWidget.h" -#include "vtkAlgorithm.h" -#include "vtkCommand.h" -#include "vtkNew.h" -#include "vtkSMParaViewPipelineControllerWithRendering.h" -#include "vtkSMPropertyHelper.h" -#include "vtkSMProxy.h" -#include "vtkSMSessionProxyManager.h" -#include "vtkSMSourceProxy.h" -#include "vtkSmartPointer.h" - -#include -#include - -namespace tomviz { - -class ModuleSegment::MSInternal -{ -public: - vtkSmartPointer SegmentationScript; - vtkSmartPointer ProgrammableFilter; - vtkSmartPointer ContourFilter; - vtkSmartPointer ContourRepresentation; - bool IsVisible; -}; - -ModuleSegment::ModuleSegment(QObject* p) : Module(p), d(new MSInternal) {} - -ModuleSegment::~ModuleSegment() -{ - finalize(); -} - -QString ModuleSegment::label() const -{ - return "Segmentation"; -} - -QIcon ModuleSegment::icon() const -{ - return QIcon(":/pqWidgets/Icons/pqCalculator.svg"); -} - -bool ModuleSegment::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - if (!Module::initialize(data, vtkView)) { - return false; - } - - vtkNew controller; - vtkSMSourceProxy* producer = data->proxy(); - vtkSMSessionProxyManager* pxm = producer->GetSessionProxyManager(); - - d->SegmentationScript.TakeReference( - pxm->NewProxy("tomviz_proxies", "PythonProgrammableSegmentation")); - vtkSMPropertyHelper(d->SegmentationScript, "Script") - .Set( - "def run_itk_segmentation(itk_image, itk_image_type):\n" - " # should return the result image and result image type like this:\n" - " # return outImage, outImageType\n" - " # An example segmentation script follows: \n\n" - " # Create a filter (ConfidenceConnectedImageFilter) for the input " - "image type\n" - " itk_filter = " - "itk.ConfidenceConnectedImageFilter[itk_image_type,itk.Image.SS3].New()" - "\n\n" - " # Set input parameters on the filter (these are copied from an " - "example in ITK.\n" - " itk_filter.SetInitialNeighborhoodRadius(3)\n" - " itk_filter.SetMultiplier(3)\n" - " itk_filter.SetNumberOfIterations(25)\n" - " itk_filter.SetReplaceValue(255)\n" - " itk_filter.SetSeed((24,65,37))\n\n" - " # Hand the input image to the filter\n" - " itk_filter.SetInput(itk_image)\n" - " # Run the filter\n" - " itk_filter.Update()\n\n" - " # Return the output and the output type (itk.Image.SS3 is one of " - "the valid output\n" - " # types for this filter and is the one we specified when we created " - "the filter above\n" - " return itk_filter.GetOutput(), itk.Image.SS3\n"); - - vtkSmartPointer proxy; - - proxy.TakeReference(pxm->NewProxy("filters", "ProgrammableFilter")); - d->ProgrammableFilter = vtkSMSourceProxy::SafeDownCast(proxy); - Q_ASSERT(d->ProgrammableFilter); - - pqCoreUtilities::connect(d->SegmentationScript, - vtkCommand::PropertyModifiedEvent, this, - SLOT(onPropertyChanged())); - - controller->PreInitializeProxy(d->ProgrammableFilter); - vtkSMPropertyHelper(d->ProgrammableFilter, "Input").Set(producer); - vtkSMPropertyHelper(d->ProgrammableFilter, "OutputDataSetType") - .Set(/*vtkImageData*/ 6); - vtkSMPropertyHelper(d->ProgrammableFilter, "Script") - .Set("self.GetOutput().ShallowCopy(self.GetInput())\n"); - controller->PostInitializeProxy(d->ProgrammableFilter); - controller->RegisterPipelineProxy(d->ProgrammableFilter); - - proxy.TakeReference(pxm->NewProxy("filters", "Contour")); - d->ContourFilter = vtkSMSourceProxy::SafeDownCast(proxy); - Q_ASSERT(d->ContourFilter); - - controller->PreInitializeProxy(d->ContourFilter); - vtkSMPropertyHelper(d->ContourFilter, "Input").Set(d->ProgrammableFilter); - vtkSMPropertyHelper(d->ContourFilter, "ComputeScalars", - /*quiet*/ true) - .Set(1); - - controller->PostInitializeProxy(d->ContourFilter); - controller->RegisterPipelineProxy(d->ContourFilter); - - vtkAlgorithm* alg = - vtkAlgorithm::SafeDownCast(d->ContourFilter->GetClientSideObject()); - alg->SetInputArrayToProcess(0, 0, 0, 0, "ImageScalars"); - - d->ContourRepresentation = controller->Show(d->ContourFilter, 0, vtkView); - Q_ASSERT(d->ContourRepresentation); - vtkSMPropertyHelper(d->ContourRepresentation, "Representation") - .Set("Surface"); - vtkSMPropertyHelper(d->ContourRepresentation, "Translation") - .Set(data->displayPosition(), 3); - - updateColorMap(); - - d->ProgrammableFilter->UpdateVTKObjects(); - d->ContourFilter->UpdateVTKObjects(); - d->ContourRepresentation->UpdateVTKObjects(); - - return true; -} - -bool ModuleSegment::finalize() -{ - vtkNew controller; - controller->UnRegisterProxy(d->ProgrammableFilter); - controller->UnRegisterProxy(d->ContourRepresentation); - controller->UnRegisterProxy(d->ContourFilter); - d->ProgrammableFilter = nullptr; - d->ContourFilter = nullptr; - d->ContourRepresentation = nullptr; - return true; -} - -bool ModuleSegment::visibility() const -{ - Q_ASSERT(d->ContourRepresentation); - return vtkSMPropertyHelper(d->ContourRepresentation, "Visibility") - .GetAsInt() != 0; -} - -bool ModuleSegment::setVisibility(bool val) -{ - Q_ASSERT(d->ContourRepresentation); - vtkSMPropertyHelper(d->ContourRepresentation, "Visibility").Set(val ? 1 : 0); - d->ContourRepresentation->UpdateVTKObjects(); - - Module::setVisibility(val); - - return true; -} - -void ModuleSegment::addToPanel(QWidget* panel) -{ - Q_ASSERT(d->ProgrammableFilter); - - if (panel->layout()) { - delete panel->layout(); - } - - QHBoxLayout* layout = new QHBoxLayout; - panel->setLayout(layout); - pqProxiesWidget* proxiesWidget = new pqProxiesWidget(panel); - layout->addWidget(proxiesWidget); - - QStringList properties; - properties << "Script"; - proxiesWidget->addProxy(d->SegmentationScript, "Script", properties, true); - - Q_ASSERT(d->ContourFilter); - Q_ASSERT(d->ContourRepresentation); - - QStringList contourProperties; - contourProperties << "ContourValues"; - proxiesWidget->addProxy(d->ContourFilter, "Contour", contourProperties, true); - - QStringList contourRepresentationProperties; - contourRepresentationProperties << "Representation" - << "Opacity" - << "Specular"; - proxiesWidget->addProxy(d->ContourRepresentation, "Appearance", - contourRepresentationProperties, true); - proxiesWidget->updateLayout(); - connect(proxiesWidget, &pqProxiesWidget::changeFinished, this, - &ModuleSegment::renderNeeded); -} - -void ModuleSegment::onPropertyChanged() -{ - std::cout << "Got property changed..." << std::endl; - QString userScript = - vtkSMPropertyHelper(d->SegmentationScript, "Script").GetAsString(); - QString segmentScript = - QString("import vtk\n" - "from tomviz import utils\n" - "import itk\n" - "\n" - "idi = self.GetInput()\n" - "ido = self.GetOutput()\n" - "ido.DeepCopy(idi)\n" - "\n" - "array = utils.get_array(idi)\n" - "itk_image_type = itk.Image.F3\n" - "itk_converter = itk.PyBuffer[itk_image_type]\n" - "itk_image = itk_converter.GetImageFromArray(array)\n" - "\n" - "%1\n" - "\n" - "output_itk_image, output_type = run_itk_segmentation(itk_image, " - "itk_image_type)\n" - "\n" - "output_array = " - "itk.PyBuffer[output_type].GetArrayFromImage(output_itk_image)\n" - "utils.set_array(ido, output_array)\n" - "" - "if array.shape == output_array.shape:\n" - " ido.SetOrigin(idi.GetOrigin())\n" - " ido.SetExtent(idi.GetExtent())\n" - " ido.SetSpacing(idi.GetSpacing())\n") - .arg(userScript); - vtkSMPropertyHelper(d->ProgrammableFilter, "Script") - .Set(segmentScript.toLatin1().data()); - d->ProgrammableFilter->UpdateVTKObjects(); - // TODO -} - -void ModuleSegment::updateColorMap() -{ - Q_ASSERT(d->ContourRepresentation); - vtkSMPropertyHelper(d->ContourRepresentation, "LookupTable").Set(colorMap()); - d->ContourRepresentation->UpdateVTKObjects(); -} - -void ModuleSegment::dataSourceMoved(double newX, double newY, double newZ) -{ - double pos[3] = { newX, newY, newZ }; - vtkSMPropertyHelper(d->ContourRepresentation, "Translation").Set(pos, 3); -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleSegment.h b/tomviz/legacy/modules/ModuleSegment.h deleted file mode 100644 index f68616c5c..000000000 --- a/tomviz/legacy/modules/ModuleSegment.h +++ /dev/null @@ -1,63 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleSegment_h -#define tomvizModuleSegment_h - -#include "Module.h" - -#include - -namespace tomviz { - -class ModuleSegment : public Module -{ - Q_OBJECT - -public: - ModuleSegment(QObject* parent = nullptr); - ~ModuleSegment(); - - /// Returns a label for this module. - QString label() const override; - - /// Returns an icon to use for this module. - QIcon icon() const override; - - using Module::initialize; - /// Initialize the module for the data source and view. This is called after a - /// new module is instantiated. Subclasses override this method to setup the - /// visualization pipeline for this module. - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - - /// Finalize the module. Subclasses should override this method to delete and - /// release all proxies (and data) created for this module. - bool finalize() override; - - /// Returns the visibility for the module. - bool visibility() const override; - - /// Set the visibility for this module. Subclasses should override this method - /// show/hide all representations created for this module. - bool setVisibility(bool val) override; - - /// This method is called add the proxies in this module to a - /// pqProxiesWidget instance. Default implementation simply adds the view - /// properties. Subclasses should override to add proxies and relevant - /// properties to the panel. - void addToPanel(QWidget* panel) override; - - void dataSourceMoved(double newX, double newY, double newZ) override; - -private slots: - void onPropertyChanged(); - -private: - void updateColorMap() override; - - class MSInternal; - QScopedPointer d; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleSlice.cxx b/tomviz/legacy/modules/ModuleSlice.cxx deleted file mode 100644 index acf1f2e91..000000000 --- a/tomviz/legacy/modules/ModuleSlice.cxx +++ /dev/null @@ -1,891 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleSlice.h" - -#include "ActiveObjects.h" -#include "../DataSource.h" -#include "DoubleSliderWidget.h" -#include "IntSliderWidget.h" -#include "ScalarsComboBox.h" -#include "Utilities.h" -#include "vtkActiveScalarsProducer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -ModuleSlice::ModuleSlice(QObject* parentObject) : Module(parentObject) {} - -ModuleSlice::~ModuleSlice() -{ - finalize(); -} - -QIcon ModuleSlice::icon() const -{ - return QIcon(":/icons/orthoslice.svg"); -} - -bool ModuleSlice::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - if (!Module::initialize(data, vtkView)) { - return false; - } - - const bool widgetSetup = setupWidget(vtkView); - - if (widgetSetup) { - m_widget->SetDisplayOffset(data->displayPosition()); - m_widget->SetDisplayOrientation(data->displayOrientation()); - m_widget->On(); - m_widget->InteractionOn(); - onDirectionChanged(m_direction); - onTextureInterpolateChanged(m_interpolate); - pqCoreUtilities::connect(m_widget, vtkCommand::InteractionEvent, this, - SLOT(onPlaneChanged())); - connect(data, &DataSource::dataChanged, this, &ModuleSlice::dataChanged); - connect(data, &DataSource::activeScalarsChanged, this, - &ModuleSlice::onScalarArrayChanged); - connect(data, &DataSource::dataPropertiesChanged, this, - &ModuleSlice::dataPropertiesChanged); - } - - Q_ASSERT(m_widget); - return widgetSetup; -} - -bool ModuleSlice::setupWidget(vtkSMViewProxy* vtkView) -{ - vtkRenderWindowInteractor* rwi = vtkView->GetRenderWindow()->GetInteractor(); - - if (!rwi) { - return false; - } - - m_widget = vtkSmartPointer::New(); - m_widget->SetVoxelValueFn( - [this](const vtkVector3i& ijk, double v) { emit mouseOverVoxel(ijk, v); }); - - // Set the interactor on the widget to be what the current - // render window is using. - m_widget->SetInteractor(rwi); - - // Set the thick slice mode to the current setting - m_widget->SetThickSliceMode(m_thickSliceMode); - - // Setup the color of the border of the widget. - { - double color[3] = { 1, 0, 0 }; - m_widget->GetPlaneProperty()->SetColor(color); - } - - // Turn texture interpolation to be linear. - m_widget->TextureInterpolateOn(); - m_widget->SetResliceInterpolateToLinear(); - - // Construct the transfer function proxy for the widget. - vtkSMProxy* lut = colorMap(); - - // Set the widgets lookup table to be the one that the transfer function - // manager is using. - vtkScalarsToColors* stc = - vtkScalarsToColors::SafeDownCast(lut->GetClientSideObject()); - m_widget->SetLookupTable(stc); - - // Lastly we set up the input connection. - m_producer->SetOutput(dataSource()->producer()->GetOutputDataObject(0)); - m_widget->SetInputConnection(m_producer->GetOutputPort()); - - Q_ASSERT(rwi); - onPlaneChanged(); - return true; -} - -void ModuleSlice::updateColorMap() -{ - Q_ASSERT(m_widget); - - // Construct the transfer function proxy for the widget - vtkSMProxy* lut = colorMap(); - - // set the widgets lookup table to be the one that the transfer function - // manager is using - vtkScalarsToColors* stc = - vtkScalarsToColors::SafeDownCast(lut->GetClientSideObject()); - m_widget->SetLookupTable(stc); -} - -void ModuleSlice::updateSliceWidget() -{ - if (!m_sliceSlider || !imageData()) - return; - - m_sliceSlider->setMinimum(0); - m_sliceSlider->setMaximum(maxSlice()); -} - -bool ModuleSlice::finalize() -{ - if (m_widget != nullptr) { - m_widget->Off(); - } - - return true; -} - -bool ModuleSlice::setVisibility(bool val) -{ - Q_ASSERT(m_widget); - m_widget->SetEnabled(val ? 1 : 0); - Module::setVisibility(val); - updateInteractionState(); - - return true; -} - -bool ModuleSlice::visibility() const -{ - return m_widget->GetEnabled() != 0; -} - -void ModuleSlice::addToPanel(QWidget* panel) -{ - if (panel->layout()) { - delete panel->layout(); - } - - QVBoxLayout* layout = new QVBoxLayout; - QFormLayout* formLayout = new QFormLayout; - - QWidget* container = new QWidget; - container->setLayout(formLayout); - layout->addWidget(container); - formLayout->setContentsMargins(0, 0, 0, 0); - - m_opacityCheckBox = new QCheckBox("Map Opacity"); - formLayout->addRow(m_opacityCheckBox); - - m_mapScalarsCheckBox = new QCheckBox("Color Map Data"); - m_mapScalarsCheckBox->setChecked(areScalarsMapped()); - formLayout->addRow(m_mapScalarsCheckBox); - connect(m_mapScalarsCheckBox, &QCheckBox::toggled, this, - &ModuleSlice::setMapScalars); - - auto line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - formLayout->addRow(line); - - m_scalarsCombo = new ScalarsComboBox(); - m_scalarsCombo->setOptions(dataSource(), this); - formLayout->addRow("Scalars", m_scalarsCombo); - - m_directionCombo = new QComboBox(); - m_directionCombo->addItem("XY Plane", QVariant(Direction::XY)); - m_directionCombo->addItem("YZ Plane", QVariant(Direction::YZ)); - m_directionCombo->addItem("XZ Plane", QVariant(Direction::XZ)); - m_directionCombo->addItem("Custom", QVariant(Direction::Custom)); - m_directionCombo->setCurrentIndex(static_cast(m_direction)); - formLayout->addRow("Direction", m_directionCombo); - - m_sliceSlider = new IntSliderWidget(true); - m_sliceSlider->setLineEditWidth(50); - m_sliceSlider->setPageStep(1); - m_sliceSlider->setMinimum(0); - if (isOrtho()) { - m_sliceSlider->setMaximum(maxSlice()); - } - - // Sanity check: make sure the slice value is within the bounds - if (m_slice < m_sliceSlider->minimum()) - m_slice = m_sliceSlider->minimum(); - else if (m_slice > m_sliceSlider->maximum()) - m_slice = m_sliceSlider->maximum(); - - m_sliceSlider->setValue(m_slice); - - formLayout->addRow("Slice", m_sliceSlider); - - m_thicknessSpin = new QSpinBox(); - m_thicknessSpin->setMaximum(m_sliceSlider->maximum()); - m_thicknessSpin->setMinimum(1); - m_thicknessSpin->setSingleStep(2); - m_thicknessSpin->setValue(m_sliceThickness); - formLayout->addRow("Slice Thickness", m_thicknessSpin); - - m_sliceCombo = new QComboBox(); - m_sliceCombo->addItem("Minimum", QVariant(Mode::Min)); - m_sliceCombo->addItem("Maximum", QVariant(Mode::Max)); - m_sliceCombo->addItem("Mean", QVariant(Mode::Mean)); - m_sliceCombo->addItem("Summation", QVariant(Mode::Sum)); - m_sliceCombo->setCurrentIndex(static_cast(m_thickSliceMode)); - formLayout->addRow("Aggregation", m_sliceCombo); - - m_opacitySlider = new DoubleSliderWidget(true); - m_opacitySlider->setLineEditWidth(50); - m_opacitySlider->setMinimum(0); - m_opacitySlider->setMaximum(1); - m_opacitySlider->setValue(m_opacity); - formLayout->addRow("Opacity", m_opacitySlider); - - m_interpolateCheckBox = new QCheckBox("Interpolate Texture"); - m_interpolateCheckBox->setChecked(m_interpolate); - formLayout->addRow(m_interpolateCheckBox); - - m_showArrowCheckBox = new QCheckBox("Show Arrow"); - m_showArrowCheckBox->setChecked(showArrow()); - formLayout->addRow(m_showArrowCheckBox); - connect(m_showArrowCheckBox, &QCheckBox::toggled, this, - &ModuleSlice::setShowArrow); - - QLabel* label = new QLabel("Point on Plane"); - layout->addWidget(label); - QHBoxLayout* row = new QHBoxLayout; - const char* labels[] = { "X:", "Y:", "Z:" }; - for (int i = 0; i < 3; ++i) { - label = new QLabel(labels[i]); - row->addWidget(label); - pqLineEdit* inputBox = new pqLineEdit; - inputBox->setEnabled(!isOrtho()); - inputBox->setValidator(new QDoubleValidator(inputBox)); - connect(inputBox, &pqLineEdit::textChangedAndEditingFinished, this, - &ModuleSlice::updatePointOnPlane); - row->addWidget(inputBox); - m_pointInputs[i] = inputBox; - } - layout->addItem(row); - - label = new QLabel("Plane Normal"); - layout->addWidget(label); - row = new QHBoxLayout; - for (int i = 0; i < 3; ++i) { - label = new QLabel(labels[i]); - row->addWidget(label); - pqLineEdit* inputBox = new pqLineEdit; - inputBox->setEnabled(!isOrtho()); - inputBox->setValidator(new QDoubleValidator(inputBox)); - connect(inputBox, &pqLineEdit::textChangedAndEditingFinished, this, - &ModuleSlice::updatePlaneNormal); - row->addWidget(inputBox); - m_normalInputs[i] = inputBox; - } - layout->addItem(row); - - // Update the Qt widget values - onPlaneChanged(); - - auto* normalToViewButton = new QPushButton("Set Normal to View"); - connect(normalToViewButton, &QPushButton::clicked, this, - &ModuleSlice::setNormalToView); - normalToViewButton->setToolTip("Set the plane normal to the view direction"); - layout->addWidget(normalToViewButton); - - layout->addStretch(); - - panel->setLayout(layout); - - connect(m_opacityCheckBox, &QCheckBox::toggled, this, [this](bool val) { - m_mapOpacity = val; - // Ensure the colormap is detached before applying opacity - if (val) { - setUseDetachedColorMap(val); - } - auto func = vtkPVDiscretizableColorTransferFunction::SafeDownCast( - colorMap()->GetClientSideObject()); - func->SetEnableOpacityMapping(val); - emit opacityEnforced(val); - updateColorMap(); - emit renderNeeded(); - }); - - m_opacityCheckBox->setChecked(m_mapOpacity); - - connect(m_scalarsCombo, QOverload::of(&QComboBox::currentIndexChanged), - this, [this](int idx) { - setActiveScalars(m_scalarsCombo->itemData(idx).toInt()); - onScalarArrayChanged(); - }); - - connect(m_directionCombo, QOverload::of(&QComboBox::currentIndexChanged), - this, [this](int idx) { - Direction dir = m_directionCombo->itemData(idx).value(); - onDirectionChanged(dir); - }); - - connect(m_interpolateCheckBox, &QCheckBox::toggled, this, - &ModuleSlice::onTextureInterpolateChanged); - - connect(m_sliceSlider, &IntSliderWidget::valueEdited, this, - QOverload::of(&ModuleSlice::onSliceChanged)); - connect(m_sliceSlider, &IntSliderWidget::valueChanged, this, - QOverload::of(&ModuleSlice::onSliceChanged)); - - connect(m_thicknessSpin, QOverload::of(&QSpinBox::valueChanged), - this, &ModuleSlice::onThicknessChanged); - connect(m_sliceCombo, QOverload::of(&QComboBox::currentIndexChanged), - this, &ModuleSlice::onThickSliceModeChanged); - - connect(m_opacitySlider, &DoubleSliderWidget::valueEdited, this, - &ModuleSlice::onOpacityChanged); - connect(m_opacitySlider, &DoubleSliderWidget::valueChanged, this, - &ModuleSlice::onOpacityChanged); -} - -void ModuleSlice::planeBounds(double b[6]) -{ - m_widget->GetPlaneBounds(b); -} - -void ModuleSlice::dataUpdated() -{ - // In case there are new slices, update min and max - updateSliceWidget(); - m_widget->UpdatePlacement(); - emit renderNeeded(); -} - -void ModuleSlice::dataChanged() -{ - // FIXME: Implementing the vtkActiveScalarsProducer as a producer breaks the - // vtk pipeline, requiring manual updates to keep in sync like here. - // It really should be implemented as a filter. - m_producer->SetOutput(dataSource()->dataObject()); - dataPropertiesChanged(); - dataUpdated(); -} - -void ModuleSlice::dataPropertiesChanged() -{ - onDirectionChanged(m_direction); -} - -void ModuleSlice::setMapScalars(bool b) -{ - if (b != areScalarsMapped()) { - m_widget->SetMapScalars(b); - emit renderNeeded(); - } -} - -void ModuleSlice::setShowArrow(bool b) -{ - if (b != showArrow()) { - if (m_showArrowCheckBox) { - m_showArrowCheckBox->setChecked(b); - } - m_widget->SetArrowVisibility(b); - updateInteractionState(); - emit renderNeeded(); - } -} - -bool ModuleSlice::showArrow() const -{ - return m_widget->GetArrowVisibility() != 0; -} - -void ModuleSlice::updateInteractionState() -{ - // We can only update the interaction if the widget is visible - if (visibility()) - m_widget->SetInteraction(showArrow()); -} - -void ModuleSlice::updatePointOnPlane() -{ - double point[3] = { 0, 0, 0 }; - - for (int i = 0; i < 3; ++i) { - const auto input = m_pointInputs[i]; - if (input->text().isEmpty()) - continue; - - point[i] = input->text().toDouble(); - } - m_widget->SetCenter(point); - m_widget->UpdatePlacement(); - emit renderNeeded(); -} - -void ModuleSlice::updatePlaneNormal() -{ - double normal[3] = { 0, 0, 0 }; - - for (int i = 0; i < 3; ++i) { - const auto input = m_normalInputs[i]; - if (input->text().isEmpty()) - continue; - - normal[i] = input->text().toDouble(); - } - m_widget->SetNormal(normal); - m_widget->UpdatePlacement(); - emit renderNeeded(); -} - -QJsonObject ModuleSlice::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - props["showArrow"] = showArrow(); - - // Serialize the plane - double point[3]; - m_widget->GetOrigin(point); - QJsonArray origin = { point[0], point[1], point[2] }; - m_widget->GetPoint1(point); - QJsonArray point1 = { point[0], point[1], point[2] }; - m_widget->GetPoint2(point); - QJsonArray point2 = { point[0], point[1], point[2] }; - - props["origin"] = origin; - props["point1"] = point1; - props["point2"] = point2; - props["mapScalars"] = areScalarsMapped(); - props["mapOpacity"] = m_mapOpacity; - - props["slice"] = m_slice; - props["sliceThickness"] = m_sliceThickness; - props["thickSliceMode"] = m_thickSliceMode; - QVariant qData; - qData.setValue(m_direction); - props["direction"] = qData.toString(); - props["interpolate"] = m_interpolate; - props["opacity"] = m_opacity; - - json["properties"] = props; - return json; -} - -bool ModuleSlice::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - setShowArrow(props["showArrow"].toBool()); - if (m_showArrowCheckBox) { - m_showArrowCheckBox->setChecked(showArrow()); - } - if (props.contains("origin") && props.contains("point1") && - props.contains("point2")) { - auto o = props["origin"].toArray(); - auto p1 = props["point1"].toArray(); - auto p2 = props["point2"].toArray(); - double origin[3] = { o[0].toDouble(), o[1].toDouble(), o[2].toDouble() }; - double point1[3] = { p1[0].toDouble(), p1[1].toDouble(), - p1[2].toDouble() }; - double point2[3] = { p2[0].toDouble(), p2[1].toDouble(), - p2[2].toDouble() }; - m_widget->SetOrigin(origin); - m_widget->SetPoint1(point1); - m_widget->SetPoint2(point2); - } - setMapScalars(props["mapScalars"].toBool()); - if (m_mapScalarsCheckBox) { - m_mapScalarsCheckBox->setChecked(areScalarsMapped()); - } - if (props.contains("mapOpacity")) { - m_mapOpacity = props["mapOpacity"].toBool(); - if (m_opacityCheckBox) { - m_opacityCheckBox->setChecked(m_mapOpacity); - } - } - m_widget->UpdatePlacement(); - if (m_scalarsCombo) { - m_scalarsCombo->setOptions(dataSource(), this); - } - // If deserializing a former OrthogonalSlice, the direction is encoded in - // the property "sliceMode" as an int - if (props.contains("sliceMode")) { - Direction direction = modeToDirection(props["sliceMode"].toInt()); - onDirectionChanged(direction); - } - if (props.contains("sliceThickness")) { - m_sliceThickness = props["sliceThickness"].toInt(); - onThicknessChanged(m_sliceThickness); - } - if (props.contains("thickSliceMode")) { - int mode = props["thickSliceMode"].toInt(); - onThickSliceModeChanged(mode); - } - if (props.contains("direction")) { - Direction direction = stringToDirection(props["direction"].toString()); - onDirectionChanged(direction); - } - if (props.contains("slice")) { - m_slice = props["slice"].toInt(); - onSliceChanged(m_slice); - } - if (props.contains("opacity")) { - m_opacity = props["opacity"].toDouble(); - onOpacityChanged(m_opacity); - if (m_opacitySlider) { - m_opacitySlider->setValue(m_opacity); - } - } - if (props.contains("interpolate")) { - m_interpolate = props["interpolate"].toBool(); - onTextureInterpolateChanged(m_interpolate); - if (m_interpolateCheckBox) { - m_interpolateCheckBox->setChecked(m_interpolate); - } - } - onScalarArrayChanged(); - onPlaneChanged(); - return true; - } - return false; -} - -void ModuleSlice::onPlaneChanged() -{ - // Avoid recursive clobbering of the plane position - if (m_ignoreSignals) { - return; - } - m_ignoreSignals = true; - - double* centerPoint = m_widget->GetCenter(); - double* normalVector = m_widget->GetNormal(); - for (int i = 0; i < 3; ++i) { - if (m_pointInputs[i]) { - QSignalBlocker b(m_pointInputs[i]); - m_pointInputs[i]->setText(QString::number(centerPoint[i])); - } - if (m_normalInputs[i]) { - QSignalBlocker b(m_normalInputs[i]); - m_normalInputs[i]->setText(QString::number(normalVector[i])); - } - } - - // Adjust the slice slider if the slice has changed from dragging the arrow - onSliceChanged(centerPoint); - - // I'm not sure why, but the slice plane moving doesn't cause a re-compute - // of the bounds automatically. Make sure the bounds get re-computed. - emit updateClientSideViewNeeded(); - - m_ignoreSignals = false; -} - -void ModuleSlice::dataSourceMoved(double newX, double newY, double newZ) -{ - double pos[3] = { newX, newY, newZ }; - m_widget->SetDisplayOffset(pos); -} - -void ModuleSlice::dataSourceRotated(double newX, double newY, double newZ) -{ - double orientation[3] = { newX, newY, newZ }; - m_widget->SetDisplayOrientation(orientation); -} - -vtkDataObject* ModuleSlice::dataToExport() -{ - return m_widget->GetResliceOutput(); -} - -bool ModuleSlice::areScalarsMapped() const -{ - return m_widget->GetMapScalars() != 0; -} - -vtkImageData* ModuleSlice::imageData() const -{ - vtkImageData* data = vtkImageData::SafeDownCast( - dataSource()->producer()->GetOutputDataObject(0)); - Q_ASSERT(data); - return data; -} - -void ModuleSlice::onScalarArrayChanged() -{ - // The scalar arrays may have been renamed - if (m_scalarsCombo) { - m_scalarsCombo->setOptions(dataSource(), this); - } - - QString arrayName; - if (activeScalars() == Module::defaultScalarsIdx()) { - arrayName = dataSource()->activeScalars(); - } else { - arrayName = dataSource()->scalarsName(activeScalars()); - } - m_producer->SetActiveScalars(arrayName.toLatin1().data()); - emit renderNeeded(); -} - -void ModuleSlice::onDirectionChanged(Direction direction) -{ - m_direction = direction; - int axis = directionAxis(direction); - - for (int i = 0; i < 3; ++i) { - if (m_pointInputs[i]) { - m_pointInputs[i]->setEnabled(!isOrtho()); - } - if (m_normalInputs[i]) { - m_normalInputs[i]->setEnabled(!isOrtho()); - } - } - if (m_sliceSlider) { - m_sliceSlider->setVisible(isOrtho()); - } - - m_widget->SetPlaneOrientation(axis); - - if (m_directionCombo) { - if (direction != m_directionCombo->currentData().value()) { - for (int i = 0; i < m_directionCombo->count(); ++i) { - Direction data = m_directionCombo->itemData(i).value(); - if (data == direction) { - m_directionCombo->setCurrentIndex(i); - } - } - } - } - - if (!isOrtho()) { - return; - } - - int dims[3]; - imageData()->GetDimensions(dims); - - double normal[3] = { 0, 0, 0 }; - int slice = 0; - - normal[axis] = 1; - slice = dims[axis] / 2; - - m_widget->SetNormal(normal); - if (m_sliceSlider) { - m_sliceSlider->setMinimum(0); - m_sliceSlider->setMaximum(maxSlice()); - } - onSliceChanged(slice); - onPlaneChanged(); - dataUpdated(); - - emit directionChanged(m_direction); -} - -bool ModuleSlice::isOrtho() const -{ - return directionAxis(m_direction) >= 0; -} - -int ModuleSlice::maxSlice() const -{ - if (!isOrtho()) { - return -1; - } - - int axis = directionAxis(m_direction); - int dims[3]; - imageData()->GetDimensions(dims); - return dims[axis] - 1; -} - -void ModuleSlice::onSliceChanged(int slice) -{ - m_slice = slice; - int axis = directionAxis(m_direction); - - if (axis < 0) { - return; - } - - m_widget->SetSliceIndex(slice); - if (m_sliceSlider) { - m_sliceSlider->setValue(slice); - } - onPlaneChanged(); - dataUpdated(); - - emit sliceChanged(slice); -} - -void ModuleSlice::onSliceChanged(double* point) -{ - int axis = directionAxis(m_direction); - if (axis < 0) { - return; - } - - int dims[3]; - imageData()->GetDimensions(dims); - double bounds[6]; - imageData()->GetBounds(bounds); - - // Due to changes from commit 43182619 the point on the slice plane could - // fall outside the bounds of the volume. - // This could yield slice numbers that are negative, or are larger than - // the number of slices. The next two lines ensure this never happens. - point[axis] = std::max(point[axis], bounds[2 * axis]); - point[axis] = std::min(point[axis], bounds[2 * axis + 1]); - - double slice = (dims[axis] - 1) * (point[axis] - bounds[2 * axis]) / - (bounds[2 * axis + 1] - bounds[2 * axis]); - - onSliceChanged(round(slice)); -} - -void ModuleSlice::onTextureInterpolateChanged(bool flag) -{ - m_interpolate = flag; - if (!m_widget) { - return; - } - int val = flag ? 1 : 0; - m_widget->SetTextureInterpolate(val); - m_widget->SetResliceInterpolate(val); - emit renderNeeded(); -} - -void ModuleSlice::onOpacityChanged(double opacity) -{ - m_opacity = opacity; - m_widget->SetOpacity(opacity); - emit renderNeeded(); -} - -void ModuleSlice::onThicknessChanged(int value) -{ - m_sliceThickness = value; - if(m_thicknessSpin) { - m_thicknessSpin->setValue(value); - } - m_widget->SetSliceThickness(value); - emit renderNeeded(); -} - -void ModuleSlice::onThickSliceModeChanged(int index) -{ - m_thickSliceMode = static_cast(index); - if (m_sliceCombo) { - m_sliceCombo->setCurrentIndex(index); - } - m_widget->SetThickSliceMode(index); - emit renderNeeded(); -} - -int ModuleSlice::directionAxis(Direction direction) const -{ - switch (direction) { - case Direction::XY: { - return 2; - } - case Direction::YZ: { - return 0; - } - case Direction::XZ: { - return 1; - } - default: { - return -1; - } - } -} - -ModuleSlice::Direction ModuleSlice::stringToDirection(const QString& name) -{ - if (name == "XY") { - return Direction::XY; - } else if (name == "YZ") { - return Direction::YZ; - } else if (name == "XZ") { - return Direction::XZ; - } else { - return Direction::Custom; - } -} - -ModuleSlice::Direction ModuleSlice::modeToDirection(int sliceMode) -{ - switch (sliceMode) { - case 5: { - return Direction::XY; - } - case 6: { - return Direction::YZ; - } - case 7: { - return Direction::XZ; - } - default: { - return Direction::Custom; - } - } -} - -bool ModuleSlice::updateClippingPlane(vtkPlane* plane, bool newFilter) -{ - - m_widget->GetResliceMapper(plane, newFilter); - emit renderNeeded(); - - return true; -} - -void ModuleSlice::setNormalToView() -{ - // First, make sure we have a custom direction - if (m_direction != Direction::Custom) { - onDirectionChanged(Direction::Custom); - } - - // Now set the normal to match that of the view direction - auto* renderView = - ActiveObjects::instance().activePqRenderView()->getRenderViewProxy(); - - double* position = renderView->GetActiveCamera()->GetPosition(); - double* focalPoint = renderView->GetActiveCamera()->GetFocalPoint(); - - double normal[3]; - - for (int i = 0; i < 3; ++i) { - normal[i] = focalPoint[i] - position[i]; - } - - m_widget->SetNormal(normal); - m_widget->UpdatePlacement(); - onPlaneChanged(); - emit renderNeeded(); -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleSlice.h b/tomviz/legacy/modules/ModuleSlice.h deleted file mode 100644 index 9f6594722..000000000 --- a/tomviz/legacy/modules/ModuleSlice.h +++ /dev/null @@ -1,163 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleSlice_h -#define tomvizModuleSlice_h - -#include "Module.h" - -#include -#include - -class QCheckBox; -class QComboBox; -class QSpinBox; -class pqLineEdit; -class vtkActiveScalarsProducer; -class vtkNonOrthoImagePlaneWidget; - -namespace tomviz { - -class ScalarsComboBox; -class DoubleSliderWidget; -class IntSliderWidget; - -class ModuleSlice : public Module -{ - Q_OBJECT - -public: - ModuleSlice(QObject* parent = nullptr); - virtual ~ModuleSlice(); - - QString label() const override { return "Slice"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - bool finalize() override; - bool setVisibility(bool val) override; - bool visibility() const override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - bool isColorMapNeeded() const override { return true; } - bool isOpacityMapped() const override { return m_mapOpacity; } - bool areScalarsMapped() const override; - void addToPanel(QWidget* panel) override; - - void planeBounds(double b[6]); - - void dataSourceMoved(double newX, double newY, double newZ) override; - void dataSourceRotated(double newX, double newY, double newZ) override; - - QString exportDataTypeString() override { return "Image"; } - - vtkDataObject* dataToExport() override; - - enum Direction - { - XY = 0, - YZ = 1, - XZ = 2, - Custom = 3 - }; - Q_ENUM(Direction) - - enum Mode - { - Min = 0, - Max = 1, - Mean = 2, - Sum = 3 - }; - Q_ENUM(Mode) - - bool showArrow() const; - - bool updateClippingPlane(vtkPlane* plane, bool newFilter) override; - - bool isOrtho() const; - int slice() const { return m_slice; } - int maxSlice() const; - - void onDirectionChanged(Direction direction); - void onSliceChanged(int slice); - void setShowArrow(bool b); - -signals: - - void sliceChanged(int slice); - - void directionChanged(Direction direction); - -protected: - void updateColorMap() override; - void updateSliceWidget(); - void updateInteractionState(); - static Direction stringToDirection(const QString& name); - static Direction modeToDirection(int sliceMode); - vtkImageData* imageData() const; - -private slots: - void onPlaneChanged(); - - void dataChanged(); - - void dataUpdated(); - - void dataPropertiesChanged(); - - void onScalarArrayChanged(); - - void setMapScalars(bool b); - - void updatePointOnPlane(); - void updatePlaneNormal(); - - void onSliceChanged(double* point); - void onThicknessChanged(int value); - void onThickSliceModeChanged(int index); - int directionAxis(Direction direction) const; - void onOpacityChanged(double opacity); - - void onTextureInterpolateChanged(bool flag); - - void setNormalToView(); - -private: - bool setupWidget(vtkSMViewProxy* view); - - Q_DISABLE_COPY(ModuleSlice) - - vtkSmartPointer m_widget; - bool m_ignoreSignals = false; - - QPointer m_opacityCheckBox; - bool m_mapOpacity = false; - - QPointer m_mapScalarsCheckBox; - QPointer m_directionCombo; - QPointer m_sliceCombo; - QPointer m_sliceSlider; - QPointer m_thicknessSpin; - QPointer m_scalarsCombo; - Direction m_direction = Direction::XY; - int m_slice = 0; - int m_sliceThickness = 1; - Mode m_thickSliceMode = Mode::Mean; - - QPointer m_interpolateCheckBox; - bool m_interpolate = false; - - QPointer m_showArrowCheckBox; - - QPointer m_opacitySlider; - double m_opacity = 1; - - QPointer m_pointInputs[3]; - QPointer m_normalInputs[3]; - - vtkNew m_producer; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleThreshold.cxx b/tomviz/legacy/modules/ModuleThreshold.cxx deleted file mode 100644 index 325b5326d..000000000 --- a/tomviz/legacy/modules/ModuleThreshold.cxx +++ /dev/null @@ -1,373 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleThreshold.h" - -#include "../DataSource.h" -#include "DoubleSliderWidget.h" -#include "Utilities.h" -#include "pqProxiesWidget.h" -#include "pqSignalAdaptors.h" -#include "pqStringVectorPropertyWidget.h" -#include "pqWidgetRangeDomain.h" - -#include "vtkDataObject.h" -#include "vtkNew.h" -#include "vtkSMPVRepresentationProxy.h" -#include "vtkSMParaViewPipelineControllerWithRendering.h" -#include "vtkSMPropertyHelper.h" -#include "vtkSMSessionProxyManager.h" -#include "vtkSMSourceProxy.h" -#include "vtkSMViewProxy.h" -#include "vtkSmartPointer.h" - -#include -#include -#include -#include - -namespace tomviz { - -ModuleThreshold::ModuleThreshold(QObject* parentObject) : Module(parentObject) -{} - -ModuleThreshold::~ModuleThreshold() -{ - finalize(); -} - -QIcon ModuleThreshold::icon() const -{ - return QIcon(":/pqWidgets/Icons/pqThreshold.svg"); -} - -bool ModuleThreshold::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - if (!Module::initialize(data, vtkView)) { - return false; - } - - auto producer = data->proxy(); - - vtkNew controller; - - vtkSMSessionProxyManager* pxm = producer->GetSessionProxyManager(); - - // Create the contour filter. - vtkSmartPointer proxy; - proxy.TakeReference(pxm->NewProxy("filters", "Threshold")); - - m_thresholdFilter = vtkSMSourceProxy::SafeDownCast(proxy); - Q_ASSERT(m_thresholdFilter); - controller->PreInitializeProxy(m_thresholdFilter); - vtkSMPropertyHelper(m_thresholdFilter, "Input").Set(producer); - controller->PostInitializeProxy(m_thresholdFilter); - controller->RegisterPipelineProxy(m_thresholdFilter); - - vtkSMPropertyHelper(m_thresholdFilter, "ThresholdMethod").Set("Between"); - - // Update min/max to avoid thresholding the full dataset. - vtkSMPropertyHelper lowerProp(m_thresholdFilter, "LowerThreshold"); - vtkSMPropertyHelper upperProp(m_thresholdFilter, "UpperThreshold"); - - double range[2], newRange[2]; - lowerProp.Get(&range[0]); - upperProp.Get(&range[1]); - - double delta = (range[1] - range[0]); - double mid = ((range[0] + range[1]) / 2.0); - newRange[0] = mid - 0.1 * delta; - newRange[1] = mid + 0.1 * delta; - - lowerProp.Set(newRange[0]); - upperProp.Set(newRange[1]); - - m_thresholdFilter->UpdateVTKObjects(); - - // Create the representation for it. - m_thresholdRepresentation = controller->Show(m_thresholdFilter, 0, vtkView); - Q_ASSERT(m_thresholdRepresentation); - vtkSMRepresentationProxy::SetRepresentationType(m_thresholdRepresentation, - "Surface"); - vtkSMPropertyHelper(m_thresholdRepresentation, "Translation") - .Set(data->displayPosition(), 3); - vtkSMPropertyHelper(m_thresholdRepresentation, "Orientation") - .Set(data->displayOrientation(), 3); - updateColorMap(); - m_thresholdRepresentation->UpdateVTKObjects(); - - // Give the proxy a friendly name for the GUI/Python world. - if (auto p = convert(proxy)) { - p->rename(label()); - } - - connect(data, &DataSource::activeScalarsChanged, this, - &ModuleThreshold::onScalarArrayChanged); - onScalarArrayChanged(); - - return true; -} - -void ModuleThreshold::updateColorMap() -{ - Q_ASSERT(m_thresholdRepresentation); - - // by default, use the data source's color/opacity maps. - vtkSMPropertyHelper(m_thresholdRepresentation, "LookupTable").Set(colorMap()); - vtkSMPropertyHelper(m_thresholdRepresentation, "ScalarOpacityFunction") - .Set(opacityMap()); - - m_thresholdRepresentation->UpdateVTKObjects(); -} - -bool ModuleThreshold::finalize() -{ - vtkNew controller; - controller->UnRegisterProxy(m_thresholdRepresentation); - controller->UnRegisterProxy(m_thresholdFilter); - m_thresholdFilter = nullptr; - m_thresholdRepresentation = nullptr; - return true; -} - -bool ModuleThreshold::setVisibility(bool val) -{ - Q_ASSERT(m_thresholdRepresentation); - vtkSMPropertyHelper(m_thresholdRepresentation, "Visibility").Set(val ? 1 : 0); - m_thresholdRepresentation->UpdateVTKObjects(); - - Module::setVisibility(val); - - return true; -} - -bool ModuleThreshold::visibility() const -{ - if (m_thresholdRepresentation) { - return vtkSMPropertyHelper(m_thresholdRepresentation, "Visibility") - .GetAsInt() != 0; - } else { - return false; - } -} - -void ModuleThreshold::addToPanel(QWidget* panel) -{ - Q_ASSERT(m_thresholdFilter); - Q_ASSERT(m_thresholdRepresentation); - - if (panel->layout()) { - delete panel->layout(); - } - - QVBoxLayout* layout = new QVBoxLayout; - - pqStringVectorPropertyWidget* arraySelection = - new pqStringVectorPropertyWidget( - m_thresholdFilter->GetProperty("SelectInputScalars"), - m_thresholdFilter); - layout->addWidget(arraySelection); - - auto lowerProp = m_thresholdFilter->GetProperty("LowerThreshold"); - auto upperProp = m_thresholdFilter->GetProperty("UpperThreshold"); - - auto* lowerSlider = new DoubleSliderWidget(true); - auto* upperSlider = new DoubleSliderWidget(true); - - auto clampLower = [lowerSlider, upperSlider]() { - if (lowerSlider->value() > upperSlider->value()) { - lowerSlider->setValue(upperSlider->value()); - } - }; - - auto clampUpper = [lowerSlider, upperSlider]() { - if (lowerSlider->value() > upperSlider->value()) { - upperSlider->setValue(lowerSlider->value()); - } - }; - - connect(lowerSlider, &DoubleSliderWidget::valueEdited, upperSlider, - clampUpper); - connect(upperSlider, &DoubleSliderWidget::valueEdited, lowerSlider, - clampLower); - - // Only update when the user releases the slider - lowerSlider->setSliderTracking(false); - upperSlider->setSliderTracking(false); - - lowerSlider->setKeyboardTracking(false); - upperSlider->setKeyboardTracking(false); - - lowerSlider->setLineEditWidth(50); - upperSlider->setLineEditWidth(50); - - QFormLayout* thresholdFormLayout = new QFormLayout; - thresholdFormLayout->setHorizontalSpacing(5); - layout->addItem(thresholdFormLayout); - - thresholdFormLayout->addRow("Minimum", lowerSlider); - thresholdFormLayout->addRow("Maximum", upperSlider); - - m_links.addPropertyLink(lowerSlider, "value", SIGNAL(valueEdited(double)), - m_thresholdRepresentation, lowerProp); - m_links.addPropertyLink(upperSlider, "value", SIGNAL(valueEdited(double)), - m_thresholdRepresentation, upperProp); - - // Keep the slider ranges up-to-date with the data - new pqWidgetRangeDomain(lowerSlider, "minimum", "maximum", lowerProp); - new pqWidgetRangeDomain(upperSlider, "minimum", "maximum", upperProp); - - QFormLayout* formLayout = new QFormLayout; - formLayout->setHorizontalSpacing(5); - layout->addItem(formLayout); - - QComboBox* representations = new QComboBox; - representations->addItem("Surface"); - representations->addItem("Wireframe"); - representations->addItem("Points"); - formLayout->addRow("Representation", representations); - - DoubleSliderWidget* opacitySlider = new DoubleSliderWidget(true); - opacitySlider->setLineEditWidth(50); - formLayout->addRow("Opacity", opacitySlider); - - DoubleSliderWidget* specularSlider = new DoubleSliderWidget(true); - specularSlider->setLineEditWidth(50); - formLayout->addRow("Specular", specularSlider); - - QCheckBox* mapScalarsCheckBox = new QCheckBox(); - formLayout->addRow("Color Map Data", mapScalarsCheckBox); - - layout->addStretch(); - panel->setLayout(layout); - - pqSignalAdaptorComboBox* adaptor = - new pqSignalAdaptorComboBox(representations); - - m_links.addPropertyLink( - adaptor, "currentText", SIGNAL(currentTextChanged(QString)), - m_thresholdRepresentation, - m_thresholdRepresentation->GetProperty("Representation")); - - m_links.addPropertyLink(opacitySlider, "value", SIGNAL(valueEdited(double)), - m_thresholdRepresentation, - m_thresholdRepresentation->GetProperty("Opacity"), 0); - m_links.addPropertyLink(specularSlider, "value", SIGNAL(valueEdited(double)), - m_thresholdRepresentation, - m_thresholdRepresentation->GetProperty("Specular"), - 0); - - m_links.addPropertyLink(mapScalarsCheckBox, "checked", SIGNAL(toggled(bool)), - m_thresholdRepresentation, - m_thresholdRepresentation->GetProperty("MapScalars"), - 0); - - connect(arraySelection, &pqPropertyWidget::changeFinished, arraySelection, - &pqPropertyWidget::apply); - connect(arraySelection, &pqPropertyWidget::changeFinished, this, - &Module::renderNeeded); - connect(lowerSlider, &DoubleSliderWidget::valueEdited, this, - &ModuleThreshold::dataUpdated); - connect(upperSlider, &DoubleSliderWidget::valueEdited, this, - &ModuleThreshold::dataUpdated); - connect(representations, &QComboBox::currentTextChanged, this, - &ModuleThreshold::dataUpdated); - connect(opacitySlider, &DoubleSliderWidget::valueEdited, this, - &ModuleThreshold::dataUpdated); - connect(specularSlider, &DoubleSliderWidget::valueEdited, this, - &ModuleThreshold::dataUpdated); - connect(mapScalarsCheckBox, &QCheckBox::toggled, this, - &ModuleThreshold::dataUpdated); -} - -void ModuleThreshold::dataUpdated() -{ - m_links.accept(); - // FIXME: why aren't threshold filter changes being pushed automatically? - m_thresholdFilter->UpdateVTKObjects(); - emit renderNeeded(); -} - -void ModuleThreshold::onScalarArrayChanged() -{ - QString arrayName = dataSource()->activeScalars(); - vtkSMPropertyHelper(m_thresholdRepresentation, "ColorArrayName") - .SetInputArrayToProcess(vtkDataObject::FIELD_ASSOCIATION_POINTS, - arrayName.toLatin1().data()); - m_thresholdRepresentation->UpdateVTKObjects(); - - emit renderNeeded(); -} - -QJsonObject ModuleThreshold::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - auto rep = m_thresholdRepresentation; - vtkSMPropertyHelper scalars(m_thresholdFilter, "SelectInputScalars"); - props["scalarArray"] = scalars.GetAsInt(); - double range[2]; - vtkSMPropertyHelper(m_thresholdFilter, "LowerThreshold").Get(&range[0]); - vtkSMPropertyHelper(m_thresholdFilter, "UpperThreshold").Get(&range[1]); - props["minimum"] = range[0]; - props["maximum"] = range[1]; - vtkSMPropertyHelper representationHelper(rep->GetProperty("Representation")); - props["representation"] = representationHelper.GetAsString(); - vtkSMPropertyHelper specular(rep->GetProperty("Specular")); - props["specular"] = specular.GetAsDouble(); - vtkSMPropertyHelper opacity(rep->GetProperty("Opacity")); - props["opacity"] = opacity.GetAsDouble(); - vtkSMPropertyHelper mapScalars(rep->GetProperty("MapScalars")); - props["mapScalars"] = mapScalars.GetAsInt() == 1; - - json["properties"] = props; - - return json; -} - -bool ModuleThreshold::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - auto rep = m_thresholdRepresentation; - vtkSMPropertyHelper scalars(m_thresholdFilter, "SelectInputScalars"); - scalars.Set(props["scalarArray"].toInt()); - vtkSMPropertyHelper(m_thresholdFilter, "LowerThreshold") - .Set(props["minimum"].toDouble()); - vtkSMPropertyHelper(m_thresholdFilter, "UpperThreshold") - .Set(props["maximum"].toDouble()); - vtkSMPropertyHelper repHelper(rep, "Representation"); - repHelper.Set(props["representation"].toString().toStdString().c_str()); - vtkSMPropertyHelper specular(rep, "Specular"); - specular.Set(props["specular"].toDouble()); - vtkSMPropertyHelper opacity(rep, "Opacity"); - opacity.Set(props["opacity"].toDouble()); - vtkSMPropertyHelper mapScalars(rep, "MapScalars"); - mapScalars.Set(props["mapScalars"].toBool() ? 1 : 0); - m_thresholdFilter->UpdateVTKObjects(); - rep->UpdateVTKObjects(); - return true; - } - return false; -} - -void ModuleThreshold::dataSourceMoved(double newX, double newY, double newZ) -{ - double pos[3] = { newX, newY, newZ }; - vtkSMPropertyHelper(m_thresholdRepresentation, "Translation").Set(pos, 3); - m_thresholdRepresentation->UpdateVTKObjects(); -} - -void ModuleThreshold::dataSourceRotated(double newX, double newY, double newZ) -{ - double orientation[3] = { newX, newY, newZ }; - vtkSMPropertyHelper(m_thresholdRepresentation, "Orientation") - .Set(orientation, 3); - m_thresholdRepresentation->UpdateVTKObjects(); -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleThreshold.h b/tomviz/legacy/modules/ModuleThreshold.h deleted file mode 100644 index a21b12f6f..000000000 --- a/tomviz/legacy/modules/ModuleThreshold.h +++ /dev/null @@ -1,56 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleThreshold_h -#define tomvizModuleThreshold_h - -#include "Module.h" - -#include -#include - -class vtkSMSourceProxy; - -namespace tomviz { - -class ModuleThreshold : public Module -{ - Q_OBJECT - -public: - ModuleThreshold(QObject* parent = nullptr); - ~ModuleThreshold() override; - - QString label() const override { return "Threshold"; } - QIcon icon() const override; - using Module::initialize; - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - bool finalize() override; - bool setVisibility(bool val) override; - bool visibility() const override; - void addToPanel(QWidget*) override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - bool isColorMapNeeded() const override { return true; } - - void dataSourceMoved(double newX, double newY, double newZ) override; - void dataSourceRotated(double newX, double newY, double newZ) override; - -protected: - void updateColorMap() override; - -private slots: - void dataUpdated(); - - void onScalarArrayChanged(); - -private: - Q_DISABLE_COPY(ModuleThreshold) - - pqPropertyLinks m_links; - vtkWeakPointer m_thresholdFilter; - vtkWeakPointer m_thresholdRepresentation; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleVolume.cxx b/tomviz/legacy/modules/ModuleVolume.cxx deleted file mode 100644 index adcda2c7b..000000000 --- a/tomviz/legacy/modules/ModuleVolume.cxx +++ /dev/null @@ -1,805 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleVolume.h" -#include "ModuleVolumeWidget.h" - -#include "../DataSource.h" -#include "HistogramManager.h" -#include "ScalarsComboBox.h" -#include "VolumeManager.h" -#include "vtkTransferFunctionBoxItem.h" -#include "vtkTriangleBar.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace tomviz { - -// Subclass vtkSmartVolumeMapper so we can have a little more customization -class SmartVolumeMapper : public vtkSmartVolumeMapper -{ -public: - SmartVolumeMapper() { SetRequestedRenderModeToGPU(); } - - static SmartVolumeMapper* New(); - - void UseJitteringOn() { GetGPUMapper()->UseJitteringOn(); } - void UseJitteringOff() { GetGPUMapper()->UseJitteringOff(); } - vtkTypeBool GetUseJittering() { return GetGPUMapper()->GetUseJittering(); } - void SetUseJittering(vtkTypeBool b) { GetGPUMapper()->SetUseJittering(b); } -}; - -vtkStandardNewMacro(SmartVolumeMapper) - - ModuleVolume::ModuleVolume(QObject* parentObject) - : Module(parentObject) -{ - // NOTE: Due to a bug in vtkMultiVolume, a gradient opacity function must be - // set or the shader will fail to compile. - // (likely fixed in https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8909) - m_gradientOpacity->AddPoint(0.0, 1.0); - connect(&HistogramManager::instance(), &HistogramManager::histogram2DReady, - this, [=](vtkSmartPointer image, - vtkSmartPointer histogram2D) { - // Force the transfer function 2D to update. - if (image == - vtkImageData::SafeDownCast(dataSource()->dataObject())) { - auto colorMap = vtkColorTransferFunction::SafeDownCast( - this->colorMap()->GetClientSideObject()); - auto opacityMap = vtkPiecewiseFunction::SafeDownCast( - this->opacityMap()->GetClientSideObject()); - vtkTransferFunctionBoxItem::rasterTransferFunction2DBox( - histogram2D, *this->transferFunction2DBox(), - transferFunction2D(), colorMap, opacityMap); - } - // Update the volume mapper. - this->updateColorMap(); - emit this->renderNeeded(); - }); - - // NOTE: Due to a bug in vtkMultiVolume, a gradient opacity function must - // be set or the shader will fail to compile. - // (likely fixed in https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8909) - connect(&VolumeManager::instance(), &VolumeManager::usingMultiVolumeChanged, - this, &ModuleVolume::updateColorMap); -} - -ModuleVolume::~ModuleVolume() -{ - finalize(); -} - -QIcon ModuleVolume::icon() const -{ - return QIcon(":/icons/pqVolumeData.png"); -} - -void ModuleVolume::initializeMapper(DataSource* data) -{ - updateMapperInput(data); - m_volumeMapper->SetScalarModeToUsePointFieldData(); - m_volumeMapper->SelectScalarArray(scalarsIndex()); - m_volume->SetMapper(m_volumeMapper); - m_volumeMapper->UseJitteringOn(); - m_volumeMapper->SetBlendMode(vtkVolumeMapper::COMPOSITE_BLEND); - if (m_view != nullptr) { - m_view->Update(); - } -} - -bool ModuleVolume::initialize(DataSource* data, vtkSMViewProxy* vtkView) -{ - if (!Module::initialize(data, vtkView)) { - return false; - } - - initializeMapper(data); - m_volume->SetProperty(m_volumeProperty); - auto* displayPosition = data->displayPosition(); - m_volume->SetPosition(displayPosition[0], displayPosition[1], - displayPosition[2]); - auto* displayOrientation = data->displayOrientation(); - m_volume->SetOrientation(displayOrientation[0], displayOrientation[1], - displayOrientation[2]); - m_volumeProperty->SetInterpolationType(VTK_LINEAR_INTERPOLATION); - m_volumeProperty->SetAmbient(0.0); - m_volumeProperty->SetDiffuse(1.0); - m_volumeProperty->SetSpecular(1.0); - m_volumeProperty->SetSpecularPower(100.0); - - resetRgbaMappingRanges(); - onRgbaMappingToggled(false); - onComponentNamesModified(); - updateColorMap(); - - m_view = vtkPVRenderView::SafeDownCast(vtkView->GetClientSideView()); - m_view->AddPropToRenderer(m_volume); - m_view->Update(); - - connect(data, &DataSource::dataChanged, this, &ModuleVolume::onDataChanged); - connect(data, &DataSource::activeScalarsChanged, this, - &ModuleVolume::onScalarArrayChanged); - connect(data, &DataSource::componentNamesModified, this, - &ModuleVolume::onComponentNamesModified); - - // Work around mapper bug on the mac, see the following issue for details: - // https://github.com/OpenChemistry/tomviz/issues/1776 - // Should be removed when this is fixed. -#if defined(Q_OS_MAC) - connect(data, &DataSource::dataChanged, this, - [this]() { this->initializeMapper(); }); -#endif - return true; -} - -void ModuleVolume::resetRgbaMappingRanges() -{ - // Combined range - computeRange(dataSource()->scalars(), m_rgbaMappingRangeAll.data()); - - // Individual ranges - m_rgbaMappingRanges.clear(); - - resetComponentNames(); - for (const auto& name : m_componentNames) { - m_rgbaMappingRanges[name] = computeRange(name); - } -} - -void ModuleVolume::resetComponentNames() -{ - m_componentNames = dataSource()->componentNames(); -} - -std::array ModuleVolume::computeRange(const QString& component) const -{ - std::array result; - auto index = m_componentNames.indexOf(component); - dataSource()->scalars()->GetRange(result.data(), index); - return result; -} - -std::array& ModuleVolume::rangeForComponent(const QString& component) -{ - return m_rgbaMappingRanges[component]; -} - -std::vector> ModuleVolume::activeRgbaRanges() -{ - std::vector> ret; - if (rgbaMappingCombineComponents()) { - // We are using one combined range - auto scalars = dataSource()->scalars(); - for (int i = 0; i < scalars->GetNumberOfComponents(); ++i) { - ret.push_back(m_rgbaMappingRangeAll); - } - } else { - for (auto& name : m_componentNames) { - ret.push_back(rangeForComponent(name)); - } - } - - return ret; -} - -void ModuleVolume::onRgbaMappingToggled(bool b) -{ - m_useRgbaMapping = b; - - updateMapperInput(); - updateVectorMode(); - if (useRgbaMapping()) { - updateRgbaMappingDataObject(); - m_volumeProperty->IndependentComponentsOff(); - if (m_view) { - m_view->AddPropToRenderer(m_triangleBar); - } - } else { - m_volumeProperty->IndependentComponentsOn(); - if (m_view) { - m_view->RemovePropFromRenderer(m_triangleBar); - } - } - updatePanel(); - - emit renderNeeded(); -} - -void ModuleVolume::onDataChanged() -{ - if (useRgbaMapping()) { - updateRgbaMappingDataObject(); - } - updatePanel(); -} - -void ModuleVolume::onComponentNamesModified() -{ - auto oldNames = m_componentNames; - resetComponentNames(); - auto newNames = m_componentNames; - - // Rename the map keys - for (int i = 0; i < oldNames.size(); ++i) { - if (newNames[i] != oldNames[i]) { - m_rgbaMappingRanges[newNames[i]] = m_rgbaMappingRanges.take(oldNames[i]); - if (m_rgbaMappingComponent == oldNames[i]) { - m_rgbaMappingComponent = newNames[i]; - } - } - } - - // Set labels on the triangle bar - if (newNames.size() >= 3) { - m_triangleBar->SetLabels(newNames[0].toLatin1().data(), - newNames[1].toLatin1().data(), - newNames[2].toLatin1().data()); - if (m_useRgbaMapping) { - emit renderNeeded(); - } - } - - // Update the panel - updatePanel(); -} - -void ModuleVolume::updateMapperInput(DataSource* data) -{ - if (useRgbaMapping()) { - m_volumeMapper->SetInputDataObject(m_rgbaDataObject); - } else if (data || (data = dataSource())) { - auto* output = data->producer()->GetOutputPort(); - m_volumeMapper->SetInputConnection(output); - } -} - -void ModuleVolume::computeRange(vtkDataArray* array, double range[2]) -{ - range[0] = DBL_MAX; - range[1] = -DBL_MAX; - for (int i = 0; i < array->GetNumberOfComponents(); ++i) { - auto* tmp = array->GetRange(i); - range[0] = std::min(range[0], tmp[0]); - range[1] = std::max(range[1], tmp[1]); - } -} - -static double computeNorm(double* vals, int num) -{ - double result = 0; - for (int i = 0; i < num; ++i) { - result += std::pow(vals[i], 2.0); - } - return std::sqrt(result); -} - -static double rescale(double val, double* oldRange, double* newRange) -{ - return (val - oldRange[0]) * (newRange[1] - newRange[0]) / - (oldRange[1] - oldRange[0]) + - newRange[0]; -} - -void ModuleVolume::updateVectorMode() -{ - int vectorMode = vtkSmartVolumeMapper::DISABLED; - auto* array = dataSource()->scalars(); - if (array->GetNumberOfComponents() > 1 && !useRgbaMapping()) { - vectorMode = vtkSmartVolumeMapper::MAGNITUDE; - } - - m_volumeMapper->SetVectorMode(vectorMode); -} - -bool ModuleVolume::rgbaMappingAllowed() -{ - auto* array = dataSource()->scalars(); - return array->GetNumberOfComponents() == 3; -} - -bool ModuleVolume::useRgbaMapping() -{ - if (!rgbaMappingAllowed()) { - m_useRgbaMapping = false; - } - - return m_useRgbaMapping; -} - -void ModuleVolume::updateRgbaMappingDataObject() -{ - auto* imageData = dataSource()->imageData(); - auto* input = dataSource()->scalars(); - - // FIXME: we should probably do a filter instead of an object. - m_rgbaDataObject->SetDimensions(imageData->GetDimensions()); - m_rgbaDataObject->AllocateScalars(input->GetDataType(), 4); - - auto* output = m_rgbaDataObject->GetPointData()->GetScalars(); - - // Rescale from 0 to 1 for the coloring. - double newRange[2] = { 0.0, 1.0 }; - auto oldRanges = activeRgbaRanges(); - for (int i = 0; i < input->GetNumberOfTuples(); ++i) { - for (int j = 0; j < 3; ++j) { - double oldVal = input->GetComponent(i, j); - double newVal = rescale(oldVal, oldRanges[j].data(), newRange); - output->SetComponent(i, j, newVal); - } - auto* vals = input->GetTuple3(i); - auto norm = computeNorm(vals, 3); - output->SetComponent(i, 3, norm); - } -} - -QString ModuleVolume::rgbaMappingComponent() -{ - if (!m_componentNames.contains(m_rgbaMappingComponent)) { - // Set it to the first component - m_rgbaMappingComponent = m_componentNames[0]; - } - - return m_rgbaMappingComponent; -} - -void ModuleVolume::updateColorMap() -{ - m_volumeProperty->SetScalarOpacity( - vtkPiecewiseFunction::SafeDownCast(opacityMap()->GetClientSideObject())); - m_volumeProperty->SetColor( - vtkColorTransferFunction::SafeDownCast(colorMap()->GetClientSideObject())); - - int propertyMode = vtkVolumeProperty::TF_1D; - const Module::TransferMode mode = getTransferMode(); - switch (mode) { - case (Module::SCALAR): { - // NOTE: Due to a bug in vtkMultiVolume, a gradient opacity function must - // be set or the shader will fail to compile. - // (likely fixed in - // https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8909) - bool usingMultiVolume = - VolumeManager::instance().usingMultiVolume(this->view()); - if (usingMultiVolume) { - m_volumeProperty->SetGradientOpacity(m_gradientOpacity); - } else { - m_volumeProperty->SetGradientOpacity(nullptr); - } - break; - } - case (Module::GRADIENT_1D): - m_volumeProperty->SetGradientOpacity(gradientOpacityMap()); - break; - case (Module::GRADIENT_2D): - if (transferFunction2D() && transferFunction2D()->GetExtent()[1] > 0) { - propertyMode = vtkVolumeProperty::TF_2D; - m_volumeProperty->SetTransferFunction2D(transferFunction2D()); - } else { - vtkSmartPointer image = - vtkImageData::SafeDownCast(dataSource()->dataObject()); - // See if the histogram is done, if it is then update the transfer - // function. - if (auto histogram2D = - HistogramManager::instance().getHistogram2D(image)) { - auto colorMap = vtkColorTransferFunction::SafeDownCast( - this->colorMap()->GetClientSideObject()); - auto opacityMap = vtkPiecewiseFunction::SafeDownCast( - this->opacityMap()->GetClientSideObject()); - vtkTransferFunctionBoxItem::rasterTransferFunction2DBox( - histogram2D, *this->transferFunction2DBox(), transferFunction2D(), - colorMap, opacityMap); - propertyMode = vtkVolumeProperty::TF_2D; - m_volumeProperty->SetTransferFunction2D(transferFunction2D()); - } - // If this histogram is not ready, then it finishing will trigger the - // functor created in the constructor and the volume mapper will be - // updated when the histogram2D is done - } - break; - } - - m_volumeProperty->SetTransferFunctionMode(propertyMode); - - // BUG: volume mappers don't update property when LUT is changed and has an - // older Mtime. Fix for now by forcing the LUT to update. - vtkObject::SafeDownCast(colorMap()->GetClientSideObject())->Modified(); -} - -bool ModuleVolume::finalize() -{ - if (m_view) { - m_view->RemovePropFromRenderer(m_volume); - m_view->RemovePropFromRenderer(m_triangleBar); - } - - return true; -} - -bool ModuleVolume::setVisibility(bool val) -{ - m_volume->SetVisibility(val ? 1 : 0); - m_triangleBar->SetVisibility(val ? 1 : 0); - - Module::setVisibility(val); - - return true; -} - -bool ModuleVolume::visibility() const -{ - return m_volume->GetVisibility() != 0; -} - -QJsonObject ModuleVolume::serialize() const -{ - auto json = Module::serialize(); - auto props = json["properties"].toObject(); - - props["transferMode"] = getTransferMode(); - props["interpolation"] = m_volumeProperty->GetInterpolationType(); - props["blendingMode"] = m_volumeMapper->GetBlendMode(); - props["rayJittering"] = m_volumeMapper->GetUseJittering() == 1; - - QJsonObject lighting; - lighting["enabled"] = m_volumeProperty->GetShade() == 1; - lighting["ambient"] = m_volumeProperty->GetAmbient(); - lighting["diffuse"] = m_volumeProperty->GetDiffuse(); - lighting["specular"] = m_volumeProperty->GetSpecular(); - lighting["specularPower"] = m_volumeProperty->GetSpecularPower(); - props["lighting"] = lighting; - props["solidity"] = solidity(); - - json["properties"] = props; - return json; -} - -bool ModuleVolume::deserialize(const QJsonObject& json) -{ - if (!Module::deserialize(json)) { - return false; - } - if (json["properties"].isObject()) { - auto props = json["properties"].toObject(); - - setTransferMode( - static_cast(props["transferMode"].toInt())); - onInterpolationChanged(props["interpolation"].toInt()); - setBlendingMode(props["blendingMode"].toInt()); - setJittering(props["rayJittering"].toBool()); - setSolidity(props["solidity"].toDouble()); - - if (props["lighting"].isObject()) { - auto lighting = props["lighting"].toObject(); - setLighting(lighting["enabled"].toBool()); - onAmbientChanged(lighting["ambient"].toDouble()); - onDiffuseChanged(lighting["diffuse"].toDouble()); - onSpecularChanged(lighting["specular"].toDouble()); - onSpecularPowerChanged(lighting["specularPower"].toDouble()); - } - - updatePanel(); - onScalarArrayChanged(); - return true; - } - return false; -} - -void ModuleVolume::addToPanel(QWidget* panel) -{ - if (panel->layout()) { - delete panel->layout(); - } - if (!m_controllers) { - m_controllers = new ModuleVolumeWidget; - } - - m_scalarsCombo = new ScalarsComboBox(); - m_scalarsCombo->setOptions(dataSource(), this); - m_controllers->formLayout()->insertRow(0, "Active Scalars", m_scalarsCombo); - - QVBoxLayout* layout = new QVBoxLayout; - panel->setLayout(layout); - - // Create, update and connect - layout->addWidget(m_controllers); - updatePanel(); - - connect(m_controllers, &ModuleVolumeWidget::jitteringToggled, this, - &ModuleVolume::setJittering); - connect(m_controllers, &ModuleVolumeWidget::lightingToggled, this, - &ModuleVolume::setLighting); - connect(m_controllers, &ModuleVolumeWidget::blendingChanged, this, - &ModuleVolume::setBlendingMode); - connect(m_controllers, &ModuleVolumeWidget::interpolationChanged, this, - &ModuleVolume::onInterpolationChanged); - connect(m_controllers, &ModuleVolumeWidget::ambientChanged, this, - &ModuleVolume::onAmbientChanged); - connect(m_controllers, &ModuleVolumeWidget::diffuseChanged, this, - &ModuleVolume::onDiffuseChanged); - connect(m_controllers, &ModuleVolumeWidget::specularChanged, this, - &ModuleVolume::onSpecularChanged); - connect(m_controllers, &ModuleVolumeWidget::specularPowerChanged, this, - &ModuleVolume::onSpecularPowerChanged); - connect(m_controllers, &ModuleVolumeWidget::transferModeChanged, this, - &ModuleVolume::onTransferModeChanged); - connect(m_controllers, &ModuleVolumeWidget::useRgbaMappingToggled, this, - &ModuleVolume::onRgbaMappingToggled); - connect(m_controllers, - &ModuleVolumeWidget::rgbaMappingCombineComponentsToggled, this, - &ModuleVolume::onRgbaMappingCombineComponentsToggled); - connect(m_controllers, &ModuleVolumeWidget::rgbaMappingComponentChanged, this, - &ModuleVolume::onRgbaMappingComponentChanged); - connect(m_controllers, &ModuleVolumeWidget::rgbaMappingMinChanged, this, - &ModuleVolume::onRgbaMappingMinChanged); - connect(m_controllers, &ModuleVolumeWidget::rgbaMappingMaxChanged, this, - &ModuleVolume::onRgbaMappingMaxChanged); - connect(m_scalarsCombo, QOverload::of(&QComboBox::currentIndexChanged), - this, [this](int idx) { - setActiveScalars(m_scalarsCombo->itemData(idx).toInt()); - onScalarArrayChanged(); - }); - connect(m_controllers, &ModuleVolumeWidget::solidityChanged, this, - &ModuleVolume::setSolidity); - connect(m_controllers, &ModuleVolumeWidget::allowMultiVolumeToggled, this, - &ModuleVolume::onAllowMultiVolumeToggled); -} - -void ModuleVolume::updatePanel() -{ - // If m_controllers is present update the values, if not they will be updated - // when it is created and shown. - if (!m_controllers || !m_volumeMapper || !m_volumeProperty || - !m_scalarsCombo) { - return; - } - auto blocked = QSignalBlocker(m_controllers); - - m_controllers->setJittering( - static_cast(m_volumeMapper->GetUseJittering())); - m_controllers->setLighting(static_cast(m_volumeProperty->GetShade())); - m_controllers->setBlendingMode(m_volumeMapper->GetBlendMode()); - m_controllers->setAmbient(m_volumeProperty->GetAmbient()); - m_controllers->setDiffuse(m_volumeProperty->GetDiffuse()); - m_controllers->setSpecular(m_volumeProperty->GetSpecular()); - m_controllers->setSpecularPower(m_volumeProperty->GetSpecularPower()); - m_controllers->setInterpolationType(m_volumeProperty->GetInterpolationType()); - m_controllers->setSolidity(solidity()); - m_controllers->setAllowMultiVolume( - VolumeManager::instance().allowMultiVolume(this->view())); - m_controllers->setEnableAllowMultiVolume( - VolumeManager::instance().volumeCount(this->view()) >= MULTI_VOLUME_SWITCH); - - m_controllers->setRgbaMappingAllowed(rgbaMappingAllowed()); - m_controllers->setUseRgbaMapping(useRgbaMapping()); - if (useRgbaMapping()) { - auto allComponents = rgbaMappingCombineComponents(); - auto options = m_componentNames; - auto component = rgbaMappingComponent(); - - m_controllers->setRgbaMappingCombineComponents(allComponents); - m_controllers->setRgbaMappingComponentOptions(options); - m_controllers->setRgbaMappingComponent(component); - - std::array minmax, sliderRange; - if (allComponents) { - minmax = m_rgbaMappingRangeAll; - computeRange(dataSource()->scalars(), sliderRange.data()); - } else { - minmax = rangeForComponent(component); - sliderRange = computeRange(component); - } - - m_controllers->setRgbaMappingMin(minmax[0]); - m_controllers->setRgbaMappingMax(minmax[1]); - m_controllers->setRgbaMappingSliderRange(sliderRange.data()); - } - - const auto tfMode = getTransferMode(); - m_controllers->setTransferMode(tfMode); - - m_scalarsCombo->setOptions(dataSource(), this); -} - -void ModuleVolume::onTransferModeChanged(const int mode) -{ - setTransferMode(static_cast(mode)); - updateColorMap(); - - emit transferModeChanged(mode); - emit renderNeeded(); -} - -void ModuleVolume::onRgbaMappingCombineComponentsToggled(const bool b) -{ - m_rgbaMappingCombineComponents = b; - updatePanel(); - - updateRgbaMappingDataObject(); - emit renderNeeded(); -} - -void ModuleVolume::onRgbaMappingComponentChanged(const QString& component) -{ - m_rgbaMappingComponent = component; - updatePanel(); -} - -void ModuleVolume::onRgbaMappingMinChanged(const double value) -{ - if (m_rgbaMappingCombineComponents) { - m_rgbaMappingRangeAll[0] = value; - } else { - rangeForComponent(rgbaMappingComponent())[0] = value; - } - - updateRgbaMappingDataObject(); - emit renderNeeded(); -} - -void ModuleVolume::onRgbaMappingMaxChanged(const double value) -{ - if (m_rgbaMappingCombineComponents) { - m_rgbaMappingRangeAll[1] = value; - } else { - rangeForComponent(rgbaMappingComponent())[1] = value; - } - - updateRgbaMappingDataObject(); - emit renderNeeded(); -} - -void ModuleVolume::onAllowMultiVolumeToggled(const bool value) -{ - VolumeManager::instance().allowMultiVolume(value, this->view()); - emit renderNeeded(); -} - -vtkDataObject* ModuleVolume::dataToExport() -{ - auto trv = dataSource()->producer(); - return trv->GetOutputDataObject(0); -} - -void ModuleVolume::onAmbientChanged(const double value) -{ - m_volumeProperty->SetAmbient(value); - emit renderNeeded(); -} - -void ModuleVolume::onDiffuseChanged(const double value) -{ - m_volumeProperty->SetDiffuse(value); - emit renderNeeded(); -} - -void ModuleVolume::onSpecularChanged(const double value) -{ - m_volumeProperty->SetSpecular(value); - emit renderNeeded(); -} - -void ModuleVolume::onSpecularPowerChanged(const double value) -{ - m_volumeProperty->SetSpecularPower(value); - emit renderNeeded(); -} - -void ModuleVolume::onInterpolationChanged(const int type) -{ - m_volumeProperty->SetInterpolationType(type); - emit renderNeeded(); -} - -void ModuleVolume::dataSourceMoved(double newX, double newY, double newZ) -{ - m_volume->SetPosition(newX, newY, newZ); -} - -void ModuleVolume::dataSourceRotated(double newX, double newY, double newZ) -{ - m_volume->SetOrientation(newX, newY, newZ); -} - -void ModuleVolume::setLighting(const bool val) -{ - m_volumeProperty->SetShade(val ? 1 : 0); - emit renderNeeded(); -} - -void ModuleVolume::setBlendingMode(const int mode) -{ - m_volumeMapper->SetBlendMode(mode); - emit renderNeeded(); -} - -void ModuleVolume::setJittering(const bool val) -{ - m_volumeMapper->SetUseJittering(val ? 1 : 0); - emit renderNeeded(); -} - -void ModuleVolume::onScalarArrayChanged() -{ - // The scalar arrays may have been renamed - if (m_scalarsCombo) { - m_scalarsCombo->setOptions(dataSource(), this); - } - - m_volumeMapper->SelectScalarArray(scalarsIndex()); - auto tp = dataSource()->producer(); - if (tp) { - tp->GetOutputDataObject(0)->Modified(); - } - emit renderNeeded(); -} - -double ModuleVolume::solidity() const -{ - return 1 / m_volumeProperty->GetScalarOpacityUnitDistance(); -} - -void ModuleVolume::setSolidity(const double value) -{ - int numComponents = useRgbaMapping() ? 4 : 1; - for (int i = 0; i < numComponents; ++i) { - m_volumeProperty->SetScalarOpacityUnitDistance(i, 1 / value); - } - emit renderNeeded(); -} - -int ModuleVolume::scalarsIndex() -{ - int index; - if (activeScalars() == Module::defaultScalarsIdx()) { - index = dataSource()->activeScalarsIdx(); - } else { - index = activeScalars(); - } - return index; -} - -bool ModuleVolume::updateClippingPlane(vtkPlane* plane, bool newFilter) -{ - if (m_volumeMapper->GetNumberOfClippingPlanes()) { - m_volumeMapper->RemoveClippingPlane(plane); - } - if (!newFilter) { - m_volumeMapper->AddClippingPlane(plane); - } - - emit renderNeeded(); - - return true; -} - -vtkVolume* ModuleVolume::getVolume() -{ - return m_volume; -} - -} // end of namespace tomviz diff --git a/tomviz/legacy/modules/ModuleVolume.h b/tomviz/legacy/modules/ModuleVolume.h deleted file mode 100644 index 8ef92ccac..000000000 --- a/tomviz/legacy/modules/ModuleVolume.h +++ /dev/null @@ -1,146 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleVolume_h -#define tomvizModuleVolume_h - -#include "Module.h" - -#include -#include - -#include -#include - -#include - -class vtkPVRenderView; - -class vtkImageClip; -class vtkImageData; -class vtkPiecewiseFunction; -class vtkPlane; -class vtkTriangleBar; -class vtkVolumeProperty; -class vtkVolume; - -namespace tomviz { - -class ModuleVolumeWidget; -class ScalarsComboBox; -class SmartVolumeMapper; - -class ModuleVolume : public Module -{ - Q_OBJECT - -public: - ModuleVolume(QObject* parent = nullptr); - virtual ~ModuleVolume(); - - QString label() const override { return "Volume"; } - QIcon icon() const override; - using Module::initialize; - void initializeMapper(DataSource *data=nullptr); - bool initialize(DataSource* dataSource, vtkSMViewProxy* view) override; - bool finalize() override; - bool setVisibility(bool val) override; - bool visibility() const override; - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - bool isColorMapNeeded() const override { return true; } - void addToPanel(QWidget* panel) override; - void updatePanel(); - - bool rgbaMappingAllowed(); - bool useRgbaMapping(); - void updateMapperInput(DataSource* data = nullptr); - void updateRgbaMappingDataObject(); - void resetRgbaMappingRanges(); - void updateVectorMode(); - - void dataSourceMoved(double newX, double newY, double newZ) override; - void dataSourceRotated(double newX, double newY, double newZ) override; - - bool supportsGradientOpacity() override { return true; } - - QString exportDataTypeString() override { return "Volume"; } - - vtkDataObject* dataToExport() override; - - bool updateClippingPlane(vtkPlane* plane, bool newFilter) override; - - double solidity() const; - - vtkVolume* getVolume(); - -protected: - void updateColorMap() override; - -private: - Q_DISABLE_COPY(ModuleVolume) - - QString rgbaMappingComponent(); - bool rgbaMappingCombineComponents() const - { - return m_rgbaMappingCombineComponents; - } - std::array computeRange(const QString& component) const; - static void computeRange(vtkDataArray* array, double range[2]); - std::array& rangeForComponent(const QString& component); - std::vector> activeRgbaRanges(); - void resetComponentNames(); - - vtkWeakPointer m_view; - vtkNew m_volume; - vtkNew m_volumeMapper; - vtkNew m_volumeProperty; - vtkNew m_gradientOpacity; - vtkNew m_triangleBar; - QPointer m_controllers; - QPointer m_scalarsCombo; - - // Data object used for mapping 3-components to Rgba - vtkNew m_rgbaDataObject; - - bool m_useRgbaMapping = false; - bool m_rgbaMappingCombineComponents = true; - QString m_rgbaMappingComponent; - - std::array m_rgbaMappingRangeAll; - // Ranges used for Rgba data object. QString is the component name. - QMap> m_rgbaMappingRanges; - - // Keep track of the component names for renaming... - QStringList m_componentNames; - -private slots: - /** - * Actuator methods for m_volumeMapper. These slots should be connected to - * the appropriate UI signals. - */ - void setLighting(const bool val); - void setBlendingMode(const int mode); - void onInterpolationChanged(const int type); - void setJittering(const bool val); - void onAmbientChanged(const double value); - void onDiffuseChanged(const double value); - void onSpecularChanged(const double value); - void onSpecularPowerChanged(const double value); - void onTransferModeChanged(const int mode); - void onRgbaMappingToggled(const bool b); - void onRgbaMappingCombineComponentsToggled(const bool b); - void onRgbaMappingComponentChanged(const QString& component); - void onRgbaMappingMinChanged(const double value); - void onRgbaMappingMaxChanged(const double value); - void onScalarArrayChanged(); - void setSolidity(const double value); - int scalarsIndex(); - void onAllowMultiVolumeToggled(const bool value); - - void onDataChanged(); - void onComponentNamesModified(); -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/modules/ModuleVolumeWidget.cxx b/tomviz/legacy/modules/ModuleVolumeWidget.cxx deleted file mode 100644 index 52e80acd1..000000000 --- a/tomviz/legacy/modules/ModuleVolumeWidget.cxx +++ /dev/null @@ -1,295 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "ModuleVolumeWidget.h" -#include "Module.h" -#include "ui_LightingParametersForm.h" -#include "ui_ModuleVolumeWidget.h" - -#include "vtkVolumeMapper.h" - -namespace tomviz { - -// If we make this bigger, such as 1000, and we make the max too -// close to the data minimum or the min too close to the data maximum, -// we run into errors like these: -// ( 118.718s) [paraview ]vtkOpenGLVolumeLookupTa:84 WARN| -// vtkOpenGLVolumeRGBTable (0x55ba0c5cc970): This OpenGL implementation does not -// support the required texture size of 65536, falling back to maximum allowed, -// 32768.This may cause an incorrect lookup table mapping. -static const double RANGE_INCREMENT = 500; - -ModuleVolumeWidget::ModuleVolumeWidget(QWidget* parent_) - : QWidget(parent_), m_ui(new Ui::ModuleVolumeWidget), - m_uiLighting(new Ui::LightingParametersForm) -{ - m_ui->setupUi(this); - - QWidget* lightingWidget = new QWidget; - m_uiLighting->setupUi(lightingWidget); - QWidget::layout()->addWidget(lightingWidget); - qobject_cast(QWidget::layout())->addStretch(); - - const int leWidth = 50; - m_uiLighting->sliAmbient->setLineEditWidth(leWidth); - m_uiLighting->sliDiffuse->setLineEditWidth(leWidth); - m_uiLighting->sliSpecular->setLineEditWidth(leWidth); - m_uiLighting->sliSpecularPower->setLineEditWidth(leWidth); - - m_uiLighting->sliSpecularPower->setMaximum(150); - m_uiLighting->sliSpecularPower->setMinimum(1); - m_uiLighting->sliSpecularPower->setResolution(200); - - m_ui->soliditySlider->setLineEditWidth(leWidth); - - QStringList labelsBlending; - labelsBlending << tr("Composite") << tr("Max") << tr("Min") << tr("Average") - << tr("Additive"); - m_ui->cbBlending->addItems(labelsBlending); - - QStringList labelsTransferMode; - labelsTransferMode << tr("Scalar") << tr("Scalar-Gradient 1D") - << tr("Scalar-Gradient 2D"); - m_ui->cbTransferMode->addItems(labelsTransferMode); - - QStringList labelsInterp; - labelsInterp << tr("Nearest Neighbor") << tr("Linear"); - m_ui->cbInterpolation->addItems(labelsInterp); - - connect(m_ui->cbJittering, &QCheckBox::toggled, this, - &ModuleVolumeWidget::jitteringToggled); - connect(m_ui->cbBlending, QOverload::of(&QComboBox::currentIndexChanged), - this, &ModuleVolumeWidget::onBlendingChanged); - connect(m_ui->cbInterpolation, - QOverload::of(&QComboBox::currentIndexChanged), this, - &ModuleVolumeWidget::interpolationChanged); - connect(m_ui->cbTransferMode, - QOverload::of(&QComboBox::currentIndexChanged), this, - &ModuleVolumeWidget::transferModeChanged); - connect(m_ui->cbMultiVolume, &QCheckBox::toggled, this, - &ModuleVolumeWidget::allowMultiVolumeToggled); - connect(m_ui->cbMultiVolume, &QCheckBox::toggled, this, - &ModuleVolumeWidget::setAllowMultiVolume); - - connect(m_ui->useRgbaMapping, &QCheckBox::toggled, this, - &ModuleVolumeWidget::useRgbaMappingToggled); - - connect(m_ui->rgbaMappingCombineComponents, &QCheckBox::toggled, this, - &ModuleVolumeWidget::rgbaMappingCombineComponentsToggled); - connect(m_ui->rgbaMappingComponent, &QComboBox::currentTextChanged, this, - &ModuleVolumeWidget::rgbaMappingComponentChanged); - - // Using QueuedConnections here to circumvent DoubleSliderWidget->BlockUpdate - connect(m_ui->sliRgbaMappingMin, &DoubleSliderWidget::valueEdited, this, - &ModuleVolumeWidget::onRgbaMappingMinChanged, Qt::QueuedConnection); - connect(m_ui->sliRgbaMappingMax, &DoubleSliderWidget::valueEdited, this, - &ModuleVolumeWidget::onRgbaMappingMaxChanged, Qt::QueuedConnection); - - connect(m_uiLighting->gbLighting, &QGroupBox::toggled, this, - &ModuleVolumeWidget::lightingToggled); - connect(m_uiLighting->sliAmbient, &DoubleSliderWidget::valueEdited, this, - &ModuleVolumeWidget::ambientChanged); - connect(m_uiLighting->sliDiffuse, &DoubleSliderWidget::valueEdited, this, - &ModuleVolumeWidget::diffuseChanged); - connect(m_uiLighting->sliSpecular, &DoubleSliderWidget::valueEdited, this, - &ModuleVolumeWidget::specularChanged); - connect(m_uiLighting->sliSpecularPower, &DoubleSliderWidget::valueEdited, - this, &ModuleVolumeWidget::specularPowerChanged); - connect(m_ui->soliditySlider, &DoubleSliderWidget::valueEdited, this, - &ModuleVolumeWidget::solidityChanged); - - m_ui->groupRgbaMappingRange->setVisible(false); - m_ui->rgbaMappingComponentLabel->setVisible(false); - m_ui->rgbaMappingComponent->setVisible(false); -} - -ModuleVolumeWidget::~ModuleVolumeWidget() = default; - -void ModuleVolumeWidget::setJittering(const bool enable) -{ - m_ui->cbJittering->setChecked(enable); -} - -void ModuleVolumeWidget::setBlendingMode(const int mode) -{ - m_uiLighting->gbLighting->setEnabled(usesLighting(mode)); - m_ui->cbBlending->setCurrentIndex(static_cast(mode)); -} - -void ModuleVolumeWidget::setInterpolationType(const int type) -{ - m_ui->cbInterpolation->setCurrentIndex(type); -} - -void ModuleVolumeWidget::setLighting(const bool enable) -{ - m_uiLighting->gbLighting->setChecked(enable); -} - -void ModuleVolumeWidget::setAmbient(const double value) -{ - m_uiLighting->sliAmbient->setValue(value); -} - -void ModuleVolumeWidget::setDiffuse(const double value) -{ - m_uiLighting->sliDiffuse->setValue(value); -} - -void ModuleVolumeWidget::setSpecular(const double value) -{ - m_uiLighting->sliSpecular->setValue(value); -} - -void ModuleVolumeWidget::setSpecularPower(const double value) -{ - m_uiLighting->sliSpecularPower->setValue(value); -} - -void ModuleVolumeWidget::onBlendingChanged(const int mode) -{ - m_uiLighting->gbLighting->setEnabled(usesLighting(mode)); - emit blendingChanged(mode); -} - -bool ModuleVolumeWidget::usesLighting(const int mode) const -{ - if (mode == vtkVolumeMapper::COMPOSITE_BLEND) { - return true; - } - - return false; -} - -void ModuleVolumeWidget::setTransferMode(const int transferMode) -{ - m_ui->cbTransferMode->setCurrentIndex(transferMode); -} - -void ModuleVolumeWidget::setSolidity(const double value) -{ - m_ui->soliditySlider->setValue(value); -} - -void ModuleVolumeWidget::setRgbaMappingAllowed(const bool b) -{ - m_ui->useRgbaMapping->setVisible(b); - - if (!b) { - setUseRgbaMapping(false); - } -} - -void ModuleVolumeWidget::setUseRgbaMapping(const bool b) -{ - m_ui->useRgbaMapping->setChecked(b); -} - -void ModuleVolumeWidget::setRgbaMappingMin(const double v) -{ - m_ui->sliRgbaMappingMin->setValue(v); -} - -void ModuleVolumeWidget::setRgbaMappingMax(const double v) -{ - m_ui->sliRgbaMappingMax->setValue(v); -} - -void ModuleVolumeWidget::setRgbaMappingSliderRange(const double range[2]) -{ - double min = range[0]; - double max = range[1]; - m_ui->sliRgbaMappingMin->setMinimum(min); - m_ui->sliRgbaMappingMin->setMaximum(max); - m_ui->sliRgbaMappingMax->setMinimum(min); - m_ui->sliRgbaMappingMax->setMaximum(max); -} - -void ModuleVolumeWidget::setRgbaMappingCombineComponents(const bool b) -{ - m_ui->rgbaMappingCombineComponents->setChecked(b); - m_ui->rgbaMappingComponent->setVisible(!b); - m_ui->rgbaMappingComponentLabel->setVisible(!b); -} - -void ModuleVolumeWidget::setRgbaMappingComponentOptions( - const QStringList& components) -{ - m_ui->rgbaMappingComponent->clear(); - m_ui->rgbaMappingComponent->addItems(components); -} - -void ModuleVolumeWidget::setRgbaMappingComponent(const QString& component) -{ - m_ui->rgbaMappingComponent->setCurrentText(component); -} - -void ModuleVolumeWidget::setAllowMultiVolume(const bool checked) -{ - if (checked != m_ui->cbMultiVolume->isChecked()) { - m_ui->cbMultiVolume->setChecked(checked); - } - - m_uiLighting->gbLighting->setEnabled(!checked || - !m_ui->cbMultiVolume->isEnabled()); -} - -void ModuleVolumeWidget::setEnableAllowMultiVolume(const bool enable) -{ - if (enable != m_ui->cbMultiVolume->isEnabled()) { - m_ui->cbMultiVolume->setEnabled(enable); - } - - m_uiLighting->gbLighting->setEnabled(!enable || - !m_ui->cbMultiVolume->isChecked()); -} - -void ModuleVolumeWidget::onRgbaMappingMinChanged(double v) -{ - // Compute an increment. Don't let the min value get closer - // than this to the maximum. - double fullRange[2] = { m_ui->sliRgbaMappingMax->minimum(), - m_ui->sliRgbaMappingMax->maximum() }; - double increment = (fullRange[1] - fullRange[0]) / RANGE_INCREMENT; - double trueMaximum = fullRange[1] - increment; - if (v > trueMaximum) { - setRgbaMappingMin(trueMaximum); - v = trueMaximum; - } - - double currentMax = m_ui->sliRgbaMappingMax->value(); - if (v > currentMax) { - // Set the maximum to be an increment above... - setRgbaMappingMax(v + increment); - } - - emit rgbaMappingMinChanged(v); -} - -void ModuleVolumeWidget::onRgbaMappingMaxChanged(double v) -{ - // Compute an increment. Don't let the max value get closer - // than this to the minimum. - double fullRange[2] = { m_ui->sliRgbaMappingMin->minimum(), - m_ui->sliRgbaMappingMin->maximum() }; - double increment = (fullRange[1] - fullRange[0]) / RANGE_INCREMENT; - double trueMinimum = fullRange[0] + increment; - if (v < trueMinimum) { - setRgbaMappingMax(trueMinimum); - v = trueMinimum; - } - - double currentMin = m_ui->sliRgbaMappingMin->value(); - if (v < currentMin) { - // Set the minimum to be an increment below... - setRgbaMappingMin(v - increment); - } - - emit rgbaMappingMaxChanged(v); -} - -QFormLayout* ModuleVolumeWidget::formLayout() -{ - return m_ui->formLayout; -} -} // namespace tomviz diff --git a/tomviz/legacy/modules/ModuleVolumeWidget.h b/tomviz/legacy/modules/ModuleVolumeWidget.h deleted file mode 100644 index a25797ea5..000000000 --- a/tomviz/legacy/modules/ModuleVolumeWidget.h +++ /dev/null @@ -1,102 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleVolumeWidget_h -#define tomvizModuleVolumeWidget_h - -#include -#include - -class QFormLayout; - -/** - * \brief UI layer of ModuleVolume. - * - * Signals are forwarded to the actuators on the mapper in ModuleVolume. - * This class is intended to contain only logic related to UI actions. - */ - -namespace Ui { -class ModuleVolumeWidget; -class LightingParametersForm; -} // namespace Ui - -namespace tomviz { - -class ModuleVolumeWidget : public QWidget -{ - Q_OBJECT - -public: - ModuleVolumeWidget(QWidget* parent_ = nullptr); - ~ModuleVolumeWidget() override; - - //@{ - /** - * UI update methods. The actual model state is stored in ModelVolume (either - * in the mapper or serialized), so the UI needs to be updated if the state - * changes or when constructing the UI. - */ - void setActiveScalars(const QString& scalars); - void setJittering(const bool enable); - void setBlendingMode(const int mode); - void setInterpolationType(const int type); - void setLighting(const bool enable); - void setAmbient(const double value); - void setDiffuse(const double value); - void setSpecular(const double value); - void setSpecularPower(const double value); - void setTransferMode(const int transferMode); - void setSolidity(const double value); - void setRgbaMappingAllowed(const bool allowed); - void setUseRgbaMapping(const bool b); - void setRgbaMappingMin(const double value); - void setRgbaMappingMax(const double value); - void setRgbaMappingSliderRange(const double range[2]); - void setRgbaMappingCombineComponents(const bool b); - void setRgbaMappingComponentOptions(const QStringList& list); - void setRgbaMappingComponent(const QString& component); - void setAllowMultiVolume(const bool allow); - void setEnableAllowMultiVolume(const bool enable); - QFormLayout* formLayout(); - //@} - -signals: - //@{ - /** - * Forwarded signals. - */ - void jitteringToggled(const bool state); - void blendingChanged(const int state); - void interpolationChanged(const int state); - void lightingToggled(const bool state); - void ambientChanged(const double value); - void diffuseChanged(const double value); - void specularChanged(const double value); - void specularPowerChanged(const double value); - void transferModeChanged(const int mode); - void solidityChanged(const double value); - void useRgbaMappingToggled(const bool b); - void rgbaMappingCombineComponentsToggled(const bool b); - void rgbaMappingMinChanged(const double value); - void rgbaMappingMaxChanged(const double value); - void rgbaMappingComponentChanged(const QString& component); - void allowMultiVolumeToggled(const bool state); - //@} - -private: - ModuleVolumeWidget(const ModuleVolumeWidget&) = delete; - void operator=(const ModuleVolumeWidget&) = delete; - - bool usesLighting(const int mode) const; - - QScopedPointer m_ui; - QScopedPointer m_uiLighting; - -private slots: - void onBlendingChanged(const int mode); - void onRgbaMappingMinChanged(double value); - void onRgbaMappingMaxChanged(double value); -}; -} // namespace tomviz -#endif diff --git a/tomviz/legacy/modules/ModuleVolumeWidget.ui b/tomviz/legacy/modules/ModuleVolumeWidget.ui deleted file mode 100644 index ebbe3e03c..000000000 --- a/tomviz/legacy/modules/ModuleVolumeWidget.ui +++ /dev/null @@ -1,230 +0,0 @@ - - - ModuleVolumeWidget - - - - 0 - 0 - 274 - 363 - - - - Form - - - - 0 - - - 12 - - - - - - - Transfer Mode - - - - - - - - - - Interpolation - - - - - - - - - - Blending Mode - - - - - - - - - - - - - - <html><head/><body><p>The &quot;solidity&quot; adjusts the unit distance on which the scalar opacity transfer function is defined. By default this is 1.0, meaning that over a distance of 1.0 units, a given opacity (from the transfer function) is accumulated. This is adjusted for the actual sampling distance during rendering.</p></body></html> - - - Solidity - - - - - - - - - - - - Ray jittering - - - true - - - - - - - Allow Multi Volume - - - true - - - - - - - <html><head/><body><p>This option is for 3-component data only. If checked, the three components will be mapped directly to RGB values, where the first, second, and third components are mapped to red, green, and blue, respectively, instead of using the color map to determine color. Additionally, the norm will be used along with the opacity editor to compute opacity.</p><p>The RGBA mapping range is used to determine the range of data values that get rescaled to the RGB color range.</p><p>If unchecked, the norm of the data is displayed instead.</p></body></html> - - - Use RGBA Mapping - - - - - - - <html><head/><body><p>Set the range of data values that get mapped to the RGB range. Data values below the range will have the minimum RGB values, and data values above the range will have the maximum RGB values.</p></body></html> - - - RGBA Mapping Range - - - - - - Min: - - - - - - - Max: - - - - - - - - - - Component: - - - - - - - - - - - - - <html><head/><body><p>If checked, a single range will be used for all components. </p><p>If unchecked, each component will have its own range.</p></body></html> - - - Use range for all components - - - true - - - - - - - - - - - tomviz::DoubleSliderWidget - QWidget -
DoubleSliderWidget.h
- 1 -
-
- - cbTransferMode - cbInterpolation - cbBlending - cbJittering - cbMultiVolume - useRgbaMapping - rgbaMappingCombineComponents - rgbaMappingComponent - - - - - useRgbaMapping - toggled(bool) - groupRgbaMappingRange - setVisible(bool) - - - 136 - 135 - - - 136 - 194 - - - - - rgbaMappingCombineComponents - toggled(bool) - rgbaMappingComponent - setVisible(bool) - - - 136 - 250 - - - 196 - 280 - - - - - rgbaMappingCombineComponents - toggled(bool) - rgbaMappingComponentLabel - setVisible(bool) - - - 136 - 250 - - - 77 - 280 - - - - -
diff --git a/tomviz/legacy/modules/ScalarsComboBox.cxx b/tomviz/legacy/modules/ScalarsComboBox.cxx deleted file mode 100644 index 08a5b5cf4..000000000 --- a/tomviz/legacy/modules/ScalarsComboBox.cxx +++ /dev/null @@ -1,38 +0,0 @@ -#include "ScalarsComboBox.h" - -#include "../DataSource.h" -#include "Module.h" - -namespace tomviz { - -ScalarsComboBox::ScalarsComboBox(QWidget* parent) : QComboBox(parent) -{ -} - -void ScalarsComboBox::setOptions(DataSource* ds, Module* module) -{ - const QSignalBlocker blocker(this); - - clear(); - - if (!ds || !module) { - return; - } - - addItem("Default", Module::defaultScalarsIdx()); - - QStringList scalars = ds->listScalars(); - for (int i = 0; i < scalars.length(); ++i) { - addItem(scalars[i], i); - } - - int currentIndex; - if (module->activeScalars() == Module::defaultScalarsIdx()) { - currentIndex = 0; - } else { - currentIndex = module->activeScalars() + 1; - } - - setCurrentIndex(currentIndex); -} -} diff --git a/tomviz/legacy/modules/ScalarsComboBox.h b/tomviz/legacy/modules/ScalarsComboBox.h deleted file mode 100644 index e78f55923..000000000 --- a/tomviz/legacy/modules/ScalarsComboBox.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef tomvizScalarsPicker_h -#define tomvizScalarsPicker_h - -#include - -namespace tomviz { - -class DataSource; -class Module; - -class ScalarsComboBox : public QComboBox -{ - Q_OBJECT - -public: - ScalarsComboBox(QWidget* parent = nullptr); - void setOptions(DataSource* ds, Module* module); -}; -} - -#endif diff --git a/tomviz/legacy/modules/VolumeManager.cxx b/tomviz/legacy/modules/VolumeManager.cxx deleted file mode 100644 index e6135022b..000000000 --- a/tomviz/legacy/modules/VolumeManager.cxx +++ /dev/null @@ -1,302 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "VolumeManager.h" - -#include "ModuleManager.h" -#include "ModuleVolume.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -namespace tomviz { - -class ViewVolumes -{ -public: - vtkNew multiVolume; - vtkNew mapper; - vtkNew auxVolume; - vtkNew auxData; - vtkNew auxProducer; - vtkNew auxProperty; - vtkNew auxOpacity; - vtkNew auxGradientOpacity; - vtkNew auxColors; - QMap volumePorts; - int currentPort = 1; - bool allowMultiVolume = true; - bool usingMultiVolume = false; -}; - -class VolumeManager::Internals -{ -public: - QMap> views; -}; - -VolumeManager::VolumeManager(QObject* parentObject) - : Superclass(parentObject), d(new VolumeManager::Internals()) -{ - connect(&ModuleManager::instance(), &ModuleManager::moduleAdded, this, - &VolumeManager::onModuleAdded); - connect(&ModuleManager::instance(), &ModuleManager::moduleRemoved, this, - &VolumeManager::onModuleRemoved); -} - -VolumeManager::~VolumeManager() = default; - -VolumeManager& VolumeManager::instance() -{ - static VolumeManager theInstance; - return theInstance; -} - -void VolumeManager::onModuleAdded(Module* module) -{ - auto volume = qobject_cast(module); - if (volume) { - connect(volume, &Module::visibilityChanged, this, - &VolumeManager::onVisibilityChanged); - auto view = volume->view(); - - if (!this->d->views.contains(view)) { - QSharedPointer viewVolumes(new ViewVolumes()); - viewVolumes->auxData->SetDimensions(1, 1, 1); - viewVolumes->auxData->AllocateScalars(VTK_FLOAT, 1); - viewVolumes->auxData->GetPointData()->GetScalars()->Fill(1); - viewVolumes->auxProducer->SetOutput(viewVolumes->auxData); - viewVolumes->mapper->SetInputConnection( - 0, viewVolumes->auxProducer->GetOutputPort()); - - viewVolumes->auxOpacity->AddPoint(0.0, 0.0); - viewVolumes->auxGradientOpacity->AddPoint(0.0, 1.0); - - viewVolumes->auxColors->AddRGBPoint(0.0, 0.0, 0.0, 0.0); - - viewVolumes->auxProperty->SetColor(viewVolumes->auxColors); - viewVolumes->auxProperty->SetScalarOpacity(viewVolumes->auxOpacity); - // NOTE: Due to a bug in vtkMultiVolume, a gradient opacity function must - // be set or the shader will fail to compile. - // (likely fixed in - // https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8909) - viewVolumes->auxProperty->SetGradientOpacity( - viewVolumes->auxGradientOpacity); - viewVolumes->auxVolume->SetProperty(viewVolumes->auxProperty); - - viewVolumes->multiVolume->SetVolume(viewVolumes->auxVolume, 0); - viewVolumes->multiVolume->SetMapper(viewVolumes->mapper); - this->d->views.insert(view, viewVolumes); - } - - this->d->views[view]->volumePorts[volume] = - this->d->views[view]->currentPort++; - auto allowMultiVolume = this->d->views[view]->allowMultiVolume; - - if (this->d->views[view]->volumePorts.size() >= MULTI_VOLUME_SWITCH && - allowMultiVolume) { - this->multiVolumeOn(view); - } - - emit volumeCountChanged(view, this->d->views[view]->volumePorts.size()); - } -} - -void VolumeManager::onModuleRemoved(Module* module) -{ - auto volume = qobject_cast(module); - if (volume) { - auto view = volume->view(); - - if (!this->d->views.contains(view)) { - return; - } - - auto& volumePorts = this->d->views[view]->volumePorts; - auto& mapper = this->d->views[view]->mapper; - auto& multiVolume = this->d->views[view]->multiVolume; - - auto it = volumePorts.find(volume); - if (it != volumePorts.end()) { - auto mod = it.key(); - auto port = it.value(); - auto vol = mod->getVolume(); - multiVolume->RemoveVolume(port); - mapper->RemoveInputConnection(port, - vol->GetMapper()->GetInputConnection(0, 0)); - volumePorts.erase(it); - } - - if (this->d->views[view]->volumePorts.size() < MULTI_VOLUME_SWITCH) { - this->multiVolumeOff(view); - } - - emit volumeCountChanged(view, this->d->views[view]->volumePorts.size()); - } -} - -void VolumeManager::multiVolumeOn(vtkSMViewProxy* view) -{ - if (!this->d->views.contains(view)) { - return; - } - - auto& volumePorts = this->d->views[view]->volumePorts; - auto& mapper = this->d->views[view]->mapper; - auto& multiVolume = this->d->views[view]->multiVolume; - auto v = vtkPVRenderView::SafeDownCast(view->GetClientSideView()); - - for (auto it = volumePorts.begin(); it != volumePorts.end(); ++it) { - auto mod = it.key(); - auto port = it.value(); - auto vol = mod->getVolume(); - - if (mod->visibility()) { - mapper->SetInputConnection(port, - vol->GetMapper()->GetInputConnection(0, 0)); - multiVolume->SetVolume(vol, port); - } - v->RemovePropFromRenderer(vol); - } - - v->AddPropToRenderer(multiVolume); - - if (!this->d->views[view]->usingMultiVolume) { - this->d->views[view]->usingMultiVolume = true; - emit usingMultiVolumeChanged(view, true); - } -} - -void VolumeManager::multiVolumeOff(vtkSMViewProxy* view) -{ - if (!this->d->views.contains(view)) { - return; - } - - auto& volumePorts = this->d->views[view]->volumePorts; - auto& mapper = this->d->views[view]->mapper; - auto& multiVolume = this->d->views[view]->multiVolume; - auto v = vtkPVRenderView::SafeDownCast(view->GetClientSideView()); - - for (auto it = volumePorts.begin(); it != volumePorts.end(); ++it) { - auto mod = it.key(); - auto port = it.value(); - auto vol = mod->getVolume(); - - multiVolume->RemoveVolume(port); - mapper->RemoveInputConnection(port, - vol->GetMapper()->GetInputConnection(0, 0)); - - v->AddPropToRenderer(vol); - } - - v->RemovePropFromRenderer(multiVolume); - - if (this->d->views[view]->usingMultiVolume) { - this->d->views[view]->usingMultiVolume = false; - emit usingMultiVolumeChanged(view, false); - } -} - -void VolumeManager::allowMultiVolume(bool allowMultiVolume, - vtkSMViewProxy* view) -{ - if (!this->d->views.contains(view)) { - return; - } - - auto usingMultiVolume = this->d->views[view]->usingMultiVolume; - - if (allowMultiVolume) { - auto nVolumes = this->d->views[view]->volumePorts.size(); - if (!usingMultiVolume && nVolumes >= MULTI_VOLUME_SWITCH) { - this->multiVolumeOn(view); - } - } else { - if (usingMultiVolume) { - this->multiVolumeOff(view); - } - } - - if (this->d->views[view]->allowMultiVolume != allowMultiVolume) { - this->d->views[view]->allowMultiVolume = allowMultiVolume; - emit allowMultiVolumeChanged(view, allowMultiVolume); - } -} - -bool VolumeManager::allowMultiVolume(vtkSMViewProxy* view) const -{ - if (!this->d->views.contains(view)) { - return true; - } - - return this->d->views[view]->usingMultiVolume; -} - -bool VolumeManager::usingMultiVolume(vtkSMViewProxy* view) const -{ - if (!this->d->views.contains(view)) { - return false; - } - - return this->d->views[view]->usingMultiVolume; -} - -int VolumeManager::volumeCount(vtkSMViewProxy* view) const -{ - if (!this->d->views.contains(view)) { - return 0; - } - - return this->d->views[view]->volumePorts.size(); -} - -void VolumeManager::onVisibilityChanged(bool visible) -{ - if (auto module = qobject_cast(sender())) { - auto view = module->view(); - - if (!this->d->views.contains(view)) { - return; - } - - auto& volumePorts = this->d->views[view]->volumePorts; - auto& mapper = this->d->views[view]->mapper; - auto& multiVolume = this->d->views[view]->multiVolume; - auto usingMultiVolume = this->d->views[view]->usingMultiVolume; - - auto it = volumePorts.find(module); - if (it == volumePorts.end()) { - return; - } - - auto port = it.value(); - auto vol = module->getVolume(); - - if (usingMultiVolume) { - if (visible) { - mapper->SetInputConnection(port, - vol->GetMapper()->GetInputConnection(0, 0)); - multiVolume->SetVolume(vol, port); - } else { - multiVolume->RemoveVolume(port); - mapper->RemoveInputConnection( - port, vol->GetMapper()->GetInputConnection(0, 0)); - } - } - } -} - -} // namespace tomviz diff --git a/tomviz/legacy/modules/VolumeManager.h b/tomviz/legacy/modules/VolumeManager.h deleted file mode 100644 index e9130c37c..000000000 --- a/tomviz/legacy/modules/VolumeManager.h +++ /dev/null @@ -1,60 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizVolumeManager_h -#define tomvizVolumeManager_h - -#include - -#include -#include - -class vtkSMViewProxy; - -namespace tomviz { - -const int MULTI_VOLUME_SWITCH = 2; - -class Module; -class ModuleVolume; - -/// Singleton to keep track of the volume modules added to each view, -/// So that a vtkMultiVolume can be used to fix volume overlapping issues. -class VolumeManager : public QObject -{ - Q_OBJECT - - typedef QObject Superclass; - -public: - static VolumeManager& instance(); - void allowMultiVolume(bool allow, vtkSMViewProxy* view); - bool allowMultiVolume(vtkSMViewProxy* view) const; - bool usingMultiVolume(vtkSMViewProxy* view) const; - int volumeCount(vtkSMViewProxy* view) const; - -signals: - void volumeCountChanged(vtkSMViewProxy* view, int count); - void usingMultiVolumeChanged(vtkSMViewProxy* view, bool enabled); - void allowMultiVolumeChanged(vtkSMViewProxy* view, bool allow); - -private slots: - void onModuleAdded(Module* module); - void onModuleRemoved(Module* module); - void onVisibilityChanged(bool visible); - -private: - Q_DISABLE_COPY(VolumeManager) - VolumeManager(QObject* parent = nullptr); - ~VolumeManager(); - - void multiVolumeOn(vtkSMViewProxy* view); - void multiVolumeOff(vtkSMViewProxy* view); - - class Internals; - QScopedPointer d; -}; - -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/operators/CustomPythonOperatorWidget.cxx b/tomviz/legacy/operators/CustomPythonOperatorWidget.cxx deleted file mode 100644 index 194635d04..000000000 --- a/tomviz/legacy/operators/CustomPythonOperatorWidget.cxx +++ /dev/null @@ -1,12 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "CustomPythonOperatorWidget.h" - -namespace tomviz { - -CustomPythonOperatorWidget::CustomPythonOperatorWidget(QWidget* p) : QWidget(p) -{} - -CustomPythonOperatorWidget::~CustomPythonOperatorWidget() {} -} // namespace tomviz diff --git a/tomviz/legacy/operators/CustomPythonOperatorWidget.h b/tomviz/legacy/operators/CustomPythonOperatorWidget.h deleted file mode 100644 index 9cba16a79..000000000 --- a/tomviz/legacy/operators/CustomPythonOperatorWidget.h +++ /dev/null @@ -1,40 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizCustomPythonOperatorWidget_h -#define tomvizCustomPythonOperatorWidget_h - -#include - -namespace tomviz { - -class OperatorPython; - -class CustomPythonOperatorWidget : public QWidget -{ - Q_OBJECT - -public: - CustomPythonOperatorWidget(QWidget* parent); - virtual ~CustomPythonOperatorWidget(); - - virtual void getValues(QMap& map) = 0; - virtual void setValues(const QMap& map) = 0; - - // Keep a copy of the current script (including edits) in case the - // custom python operator needs to use it. - virtual void setScript(const QString& script) { m_script = script; } - - // Subclasses can perform some UI setup when this is called, if needed - virtual void setupUI(OperatorPython*) {} - - // Subclasses can write settings when this is called, if needed. - // This is called when the operator is applied. - virtual void writeSettings() {} - -protected: - QString m_script; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/operators/EditOperatorWidget.cxx b/tomviz/legacy/operators/EditOperatorWidget.cxx deleted file mode 100644 index f3bd614b7..000000000 --- a/tomviz/legacy/operators/EditOperatorWidget.cxx +++ /dev/null @@ -1,11 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "EditOperatorWidget.h" - -namespace tomviz { - -EditOperatorWidget::EditOperatorWidget(QWidget* p) : Superclass(p) {} - -EditOperatorWidget::~EditOperatorWidget() {} -} // namespace tomviz diff --git a/tomviz/legacy/operators/EditOperatorWidget.h b/tomviz/legacy/operators/EditOperatorWidget.h deleted file mode 100644 index 2eaf79e1c..000000000 --- a/tomviz/legacy/operators/EditOperatorWidget.h +++ /dev/null @@ -1,48 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizEditOperatorWidget_h -#define tomvizEditOperatorWidget_h - -#include - -namespace tomviz { -// This class is the GUI needed to edit the properties of an operator. The -// operator will return one of these from its getEditorContents and it will -// be shown in a dialog. When Apply or Ok is clicked on the dialog, the -// applyChangesToOperator() slot will be called on the widget. -class EditOperatorWidget : public QWidget -{ - Q_OBJECT - typedef QWidget Superclass; - -public: - EditOperatorWidget(QWidget* parent); - ~EditOperatorWidget(); - - // Called when the user interacts to move the data source while the widget - // is active. By default this emits the signal which can be attached to - // subwidgets' slots, but it can be overridden for custom handling. - virtual void dataSourceMoved(double newX, double newY, double newZ) - { - emit dataMoved(newX, newY, newZ); - } - - // Used to set the mode of the EditOperatorWidget. The mode corresponds to - // options like tabs that change the whole widget's appearance and varies from - // operator to operator. If the requested mode is not recognized, or the - // widget does not support modes, this function does nothing. The default - // implementation of this function does nothing. This method should be - // overridden to add support for modes. - virtual void setViewMode(const QString&) {} - -signals: - void dataMoved(double, double, double); - -public slots: - // Called when the dialog should apply its changes to the operator - virtual void applyChangesToOperator() = 0; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/operators/EditPythonOperatorWidget.ui b/tomviz/legacy/operators/EditPythonOperatorWidget.ui deleted file mode 100644 index 8a72c3a65..000000000 --- a/tomviz/legacy/operators/EditPythonOperatorWidget.ui +++ /dev/null @@ -1,160 +0,0 @@ - - - EditPythonOperatorWidget - - - - 0 - 0 - 1098 - 739 - - - - - 1 - 1 - - - - - 866 - 629 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - Name - - - - - - - - - - - - - 1 - - - - Script - - - - - - Python Script - - - - - - - QTextEdit::NoWrap - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-size:11pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">def transform(dataset):</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> &quot;&quot;&quot;Define this method for Python operators that</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> transform the input array&quot;&quot;&quot;</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> import numpy as np</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> # Get the current volume as a numpy array.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> array = dataset.active_scalars</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> # This is where you operate on your data, here we square root it.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> result = np.sqrt(array)</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> # This is where the transformed data is set, it will display in tomviz.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> dataset.active_scalars = result</p></body></html> - - - def transform(dataset): - """Define this method for Python operators that - transform the input array""" - - import numpy as np - - # Get the current volume as a numpy array. - array = dataset.active_scalars - - # This is where you operate on your data, here we square root it. - result = np.sqrt(array) - - # This is where the transformed data is set, it will display in tomviz. - dataset.active_scalars = result - - - - - - - Save Script - - - - - - - - Set Parameter Values - - - - - - - - - - - - - - diff --git a/tomviz/legacy/operators/Operator.cxx b/tomviz/legacy/operators/Operator.cxx deleted file mode 100644 index e2c39eb48..000000000 --- a/tomviz/legacy/operators/Operator.cxx +++ /dev/null @@ -1,222 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "Operator.h" - -#include "../DataSource.h" -#include "../modules/ModuleManager.h" -#include "OperatorFactory.h" -#include "OperatorResult.h" -#include "../Pipeline.h" - -#include "vtkImageData.h" -#include "vtkSMSourceProxy.h" - -#include -#include -#include - -#include - -namespace tomviz { - -Operator::Operator(QObject* parentObject) : QObject(parentObject) -{ - qRegisterMetaType("TransformResult"); - qRegisterMetaType>(); - - // Whenever we emit transform modified, let's trip the m_modified flag - connect(this, &Operator::transformModified, this, - [this]() { m_modified = true; }); - - // When the transform is completed, let's reset m_modified and m_new flags - connect(this, &Operator::transformingDone, this, [this]() { - if (m_state == OperatorState::Complete) { - m_modified = false; - m_new = false; - } - }); -} - -Operator::~Operator() -{ - setNumberOfResults(0); - emit aboutToBeDestroyed(this); -} - -DataSource* Operator::dataSource() -{ - return qobject_cast(parent()); -} - -TransformResult Operator::transform(vtkDataObject* data) -{ - m_state = OperatorState::Running; - emit transformingStarted(); - setProgressStep(0); - bool result = this->applyTransform(data); - TransformResult transformResult = - result ? TransformResult::Complete : TransformResult::Error; - // If the user requested the operator to be canceled then when it returns - // we should treat is as canceled. - if (isCanceled()) { - transformResult = TransformResult::Canceled; - } else { - m_state = static_cast(transformResult); - } - emit transformingDone(transformResult); - - return transformResult; -} - -void Operator::setNumberOfResults(int n) -{ - int previousSize = m_results.size(); - if (previousSize < n) { - // Resize the list - m_results.reserve(n); - for (int i = previousSize; i < n; ++i) { - auto oa = new OperatorResult(this); - m_results.append(oa); - } - } else { - for (int i = n; i < previousSize; ++i) { - auto result = m_results.takeLast(); - delete result; - } - } -} - -int Operator::numberOfResults() const -{ - return m_results.size(); -} - -bool Operator::setResult(int index, vtkDataObject* object) -{ - if (index < 0 || index >= numberOfResults()) { - return false; - } - - auto result = m_results[index]; - Q_ASSERT(result); - result->setDataObject(object); - m_results[index] = result; - - return true; -} - -bool Operator::setResult(const char* name, vtkDataObject* object) -{ - bool valueSet = false; - QString qname(name); - foreach (auto result, m_results) { - if (result->name() == qname) { - result->setDataObject(object); - valueSet = true; - break; - } - } - - return valueSet; -} - -OperatorResult* Operator::resultAt(int i) const -{ - if (i < 0 || i >= m_results.size()) { - return nullptr; - } - return m_results[i]; -} - -void Operator::setHasChildDataSource(bool value) -{ - m_hasChildDataSource = value; -} - -bool Operator::hasChildDataSource() const -{ - return m_hasChildDataSource; -} - -void Operator::setChildDataSource(DataSource* source) -{ - ModuleManager::instance().addChildDataSource(source); - m_childDataSource = source; -} - -DataSource* Operator::childDataSource() const -{ - return m_childDataSource; -} - -QJsonObject Operator::serialize() const -{ - QJsonObject json; - if (childDataSource()) { - QJsonArray dataSources; - DataSource* ds = childDataSource(); - dataSources.append(ds->serialize()); - json["dataSources"] = dataSources; - } - json["type"] = OperatorFactory::instance().operatorType(this); - json["id"] = QString::asprintf("%p", static_cast(this)); - if (m_breakpoint) { - json["breakpoint"] = true; - } - - return json; -} - -bool Operator::deserialize(const QJsonObject& json) -{ - if (json.contains("breakpoint")) { - m_breakpoint = json["breakpoint"].toBool(); - } - - return true; -} - -void Operator::createNewChildDataSource( - const QString& label, vtkSmartPointer childData, - DataSource::DataSourceType type, DataSource::PersistenceState state) -{ - if (this->childDataSource() == nullptr) { - DataSource* childDS = - new DataSource(vtkImageData::SafeDownCast(childData), type, - this->dataSource()->pipeline(), state); - childDS->setLabel(label); - setChildDataSource(childDS); - setHasChildDataSource(true); - emit Operator::newChildDataSource(childDS); - } - // Reuse the existing "Output" data source. - else { - childDataSource()->setData(childData); - childDataSource()->setLabel(label); - childDataSource()->setForkable(true); - childDataSource()->dataModified(); - setHasChildDataSource(true); - } -} - -void Operator::setBreakpoint(bool enabled) -{ - if (m_breakpoint != enabled) { - m_breakpoint = enabled; - emit breakpointChanged(); - } -} - -void Operator::cancelTransform() -{ - m_state = OperatorState::Canceled; - emit transformCanceled(); -} - -void Operator::completionTransform() -{ - m_state = OperatorState::Complete; - emit transformCompleted(); -} -} // namespace tomviz diff --git a/tomviz/legacy/operators/Operator.h b/tomviz/legacy/operators/Operator.h deleted file mode 100644 index 5caabded5..000000000 --- a/tomviz/legacy/operators/Operator.h +++ /dev/null @@ -1,319 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizOperator_h -#define tomvizOperator_h - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "../DataSource.h" - - -class vtkImageData; -class QWidget; - -namespace tomviz { -class EditOperatorWidget; -class OperatorResult; - -enum class OperatorState -{ - Queued, - Running, - Complete, - Canceled, - Error, - Edit -}; - -enum class TransformResult -{ - Complete = static_cast(OperatorState::Complete), - Canceled = static_cast(OperatorState::Canceled), - Error = static_cast(OperatorState::Error) -}; - -class Operator : public QObject -{ - Q_OBJECT - -public: - Operator(QObject* parent = nullptr); - ~Operator() override; - - /// Returns the data source the operator operates on - DataSource* dataSource(); - - /// Returns a label for this operator. - virtual QString label() const = 0; - - /// Returns an icon to use for this operator. - virtual QIcon icon() const = 0; - - TransformResult transform(vtkDataObject* data); - - /// Return a new clone. - virtual Operator* clone() const = 0; - - /// Set the number of results produced by this operator. - virtual void setNumberOfResults(int n); - - /// Get number of output results - virtual int numberOfResults() const; - - /// Set the result at the given index to the object. - virtual bool setResult(int index, vtkDataObject* object); - - /// Set the result with the given name to the object. - virtual bool setResult(const char* name, vtkDataObject* object); - - /// Get output result at index. - virtual OperatorResult* resultAt(int index) const; - - /// Set whether the operator is expected to produce a child DataSource. - virtual void setHasChildDataSource(bool value); - - /// Get whether the operator is expected to produce a child DataSource. - virtual bool hasChildDataSource() const; - - /// Set the child DataSource. Can be nullptr. - virtual void setChildDataSource(DataSource* source); - - /// Get the child DataSource. - virtual DataSource* childDataSource() const; - - /// Save/Restore state. - virtual QJsonObject serialize() const; - virtual bool deserialize(const QJsonObject& json); - - /// There are two versions of this function, this one and - /// getEditorContentsWithData. Subclasses should override this one if their - /// editors do not need the previous state of the data. Subclasses should - /// override the other if they need the data just prior to this Operator for - /// the widget to display correctly. If this data is needed, the default - /// implementation to return nullptr should be left for this function. - virtual EditOperatorWidget* getEditorContents(QWidget* vtkNotUsed(parent)) - { - return nullptr; - } - - /// Should return a widget for editing customizable parameters on this - /// operator or nullptr if there is nothing to edit. The vtkImageData - /// is a copy of the DataSource's image with all Operators prior in the - /// pipeline applied to it. This should be used if the widget needs to - /// display the VTK data, but modifications to it will not affect the - /// DataSource. - virtual EditOperatorWidget* getEditorContentsWithData( - QWidget* parent_, - vtkSmartPointer vtkNotUsed(inputDataForDisplay)) - { - return this->getEditorContents(parent_); - } - - /// Should return true if the Operator has a non-null widget to return from - /// getEditorContents. - virtual bool hasCustomUI() const { return false; } - - - /// If the operator has some custom progress UI, then return that UI from this - /// function. This custom UI must be parented to the given widget and should - /// have its signal/slot connections set up to show the progress of the - /// operator. If there is no need for custom progress UI, then leave this - /// default implementation and a default QProgressBar will be created instead. - virtual QWidget* getCustomProgressWidget(QWidget*) const { return nullptr; } - - /// Returns true if the operation supports canceling midway through the - /// applyTransform function via the cancelTransform slot. Defaults to false, - /// can be set by the setSupportsCancel(bool) method by subclasses. - bool supportsCancelingMidTransform() const { return m_supportsCancel; } - - /// Returns true if the operation supports early completion midway through the - /// applyTransform function via the cancelTransform slot. - /// Defaults to false, can be set by the setSupportsCompletion(bool) - /// method by subclasses. - bool supportsCompletionMidTransform() const { return m_supportsCompletion; } - - /// Return the total number of progress updates (assuming each update - /// increments the progress from 0 to some maximum. If the operator doesn't - /// support incremental progress updates, leave value set to zero - /// that QProgressBar interprets the progress as unknown. - int totalProgressSteps() const { return m_totalProgressSteps; } - - /// Set the total number of progress steps - void setTotalProgressSteps(int steps) - { - m_totalProgressSteps = steps; - emit totalProgressStepsChanged(steps); - } - - /// Returns the current progress step - int progressStep() const { return m_progressStep; } - - /// Set the current progress step - void setProgressStep(int step) - { - m_progressStep = step; - emit progressStepChanged(step); - } - - /// Returns the current progress message - QString progressMessage() const { return m_progressMessage; } - - /// Set the current progress message which will appear in the progress dialog - /// title. - void setProgressMessage(const QString& message) - { - m_progressMessage = message; - emit progressMessageChanged(message); - } - - /// Set the operator state, this is needed for external execution. - void setState(OperatorState state) { m_state = state; } - - /// Get/set whether a breakpoint is set on this operator. - bool hasBreakpoint() const { return m_breakpoint; } - void setBreakpoint(bool enabled); - - /// Get the operator's help url - QString helpUrl() const { return m_helpUrl; } - void setHelpUrl(const QString& s) { m_helpUrl = s; } - -signals: - /// Emit this signal with the operation is updated/modified - /// implying that the data needs to be reprocessed. - void transformModified(); - - /// Emit this signal to indicate that the operator's label changed - /// and the GUI needs to refresh its display of the Operator. - void labelModified(); - - /// Emitted to indicate that the progress step has changed. - void progressStepChanged(int); - - /// Emitted to indicate that the progress message has changed. - void progressMessageChanged(const QString& message); - - /// Emitted when the operator starts transforming the data - void transformingStarted(); - - /// Emitted when the operator is done transforming the data. The parameter is - /// the return value from the transform() function. True for success, false - /// for failure. - void transformingDone(TransformResult result); - - /// Emitted when an result is added. - void resultAdded(OperatorResult* result); - - /// Emitted when the total progress steps has changed. - void totalProgressStepsChanged(int steps); - - /// Emitted when a child data source is create by this operator. - void newChildDataSource(DataSource*); - - // Signal used to request the creation of a new data source. Needed to - // ensure the initialization of the new DataSource is performed on UI thread - void newChildDataSource(const QString&, vtkSmartPointer); - - /// Emitted just prior to this object's destruction. - void aboutToBeDestroyed(Operator* op); - - // Emitted when a data source is move to a new operator. For example when - // a new operator is added. - void dataSourceMoved(DataSource*); - - // Emitted when the operator is moved into the canceled state. Note at this - // point the operator may still be running. This is used to indicate that - // a request to cancel this operator has been issued. - void transformCanceled(); - - // Emitted when the operator is accepted before reaching a completed - // transform. The operator will still be running, so there has to be - // a request to cancel the operator as well as encourage the next - // operator to run. - void transformCompleted(); - - // Emitted when the breakpoint state changes. - void breakpointChanged(); - -public slots: - /// Called when the 'Cancel' button is pressed on the progress dialog. - /// Subclasses overriding this method should call the base implementation - /// to ensure the operator is marked as canceled. - /// TODO: Not sure we need this/want this virtual anymore? - virtual void cancelTransform(); - virtual void completionTransform(); - bool isCanceled() { return m_state == OperatorState::Canceled; } - - /// Distinction between this and isFinished is necessary to prevent cascading - /// errors - bool isCompleted() { return m_state == OperatorState::Complete; } - bool failed() { return m_state == OperatorState::Error; } - bool isFinished() - { - return m_state == OperatorState::Complete || - m_state == OperatorState::Error; - }; - bool isModified() { return m_modified; } - bool isNew() { return m_new; } - bool isEditing() { return m_state == OperatorState::Edit; } - bool isQueued() { return m_state == OperatorState::Queued; } - - OperatorState state() { return m_state; } - void setModified() { m_modified = true; } - void resetState() { m_state = OperatorState::Queued; } - void setEditing() { m_state = OperatorState::Edit; } - void setComplete() { m_state = OperatorState::Complete; } - -protected slots: - // Create a new child datasource and set it on this operator - void createNewChildDataSource( - const QString& label, vtkSmartPointer, - DataSource::DataSourceType type = DataSource::DataSourceType::Volume, - DataSource::PersistenceState state = - DataSource::PersistenceState::Transient); - -protected: - /// Method to transform a dataset in-place. - virtual bool applyTransform(vtkDataObject* data) = 0; - - /// Method to set whether the operator supports canceling midway through the - /// transform method call. If you set this to true, you should also override - /// the cancelTransform slot to listen for the cancel signal and handle it. - void setSupportsCancel(bool b) { m_supportsCancel = b; } - - /// Method to set whether the operator supports early completion midway - /// through the transform method call. - /// If you set this to true, you should also override the - /// completionTransform slot to listen for the done signal and handle - /// it. - void setSupportsCompletion(bool b) { m_supportsCompletion = b; } - -private: - Q_DISABLE_COPY(Operator) - - QList m_results; - bool m_supportsCancel = false; - bool m_supportsCompletion = false; - bool m_hasChildDataSource = false; - bool m_modified = true; - bool m_new = true; - QPointer m_childDataSource; - int m_totalProgressSteps = 0; - int m_progressStep = 0; - QString m_progressMessage; - QString m_helpUrl; - bool m_breakpoint = false; - std::atomic m_state{ OperatorState::Queued }; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/operators/OperatorDialog.cxx b/tomviz/legacy/operators/OperatorDialog.cxx deleted file mode 100644 index baae0396c..000000000 --- a/tomviz/legacy/operators/OperatorDialog.cxx +++ /dev/null @@ -1,36 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "OperatorDialog.h" - -#include -#include -#include - -namespace tomviz { - -OperatorDialog::OperatorDialog(QWidget* parentObject) : Superclass(parentObject) -{ - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(new QLabel("(Operator parameters not available)", this)); - QDialogButtonBox* buttons = new QDialogButtonBox( - QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - this->setLayout(layout); - layout->addWidget(buttons); -} - -OperatorDialog::~OperatorDialog() {} - -void OperatorDialog::setJSONDescription(const QString& /*json*/) -{ - // Stubbed out: OperatorWidget has been removed. -} - -QMap OperatorDialog::values() const -{ - // Stubbed out: OperatorWidget has been removed. - return {}; -} -} // namespace tomviz diff --git a/tomviz/legacy/operators/OperatorDialog.h b/tomviz/legacy/operators/OperatorDialog.h deleted file mode 100644 index 6725823fc..000000000 --- a/tomviz/legacy/operators/OperatorDialog.h +++ /dev/null @@ -1,34 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizOperatorDialog_h -#define tomvizOperatorDialog_h - -#include -#include -#include -#include - -namespace tomviz { - -class OperatorDialog : public QDialog -{ - Q_OBJECT - typedef QDialog Superclass; - -public: - OperatorDialog(QWidget* parent = nullptr); - ~OperatorDialog() override; - - /// Set the JSON description of the operator - void setJSONDescription(const QString& json); - - /// Get parameter values - QMap values() const; - -private: - Q_DISABLE_COPY(OperatorDialog) -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/operators/OperatorFactory.cxx b/tomviz/legacy/operators/OperatorFactory.cxx deleted file mode 100644 index 3b4c6186a..000000000 --- a/tomviz/legacy/operators/OperatorFactory.cxx +++ /dev/null @@ -1,72 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "OperatorFactory.h" - -#include "OperatorPython.h" -#include -#include - -namespace tomviz { - -OperatorFactory::OperatorFactory() = default; - -OperatorFactory::~OperatorFactory() = default; - -OperatorFactory& OperatorFactory::instance() -{ - static OperatorFactory theInstance; - return theInstance; -} - -QList OperatorFactory::operatorTypes() -{ - QList reply; - reply << "Python"; - return reply; -} - -Operator* OperatorFactory::createConvertToVolumeOperator( - DataSource::DataSourceType) -{ - return nullptr; -} - -Operator* OperatorFactory::createOperator(const QString& type, DataSource* ds) -{ - Operator* op = nullptr; - if (type == "Python") { - op = new OperatorPython(ds); - } - return op; -} - -const char* OperatorFactory::operatorType(const Operator* op) -{ - if (qobject_cast(op)) { - return "Python"; - } - return nullptr; -} - -void OperatorFactory::registerPythonOperator( - const QString& label, const QString& source, bool requiresTiltSeries, - bool requiresVolume, bool requiresFib, const QString& json) -{ - PythonOperatorInfo info; - info.label = label; - info.source = source; - info.requiresTiltSeries = requiresTiltSeries; - info.requiresVolume = requiresVolume; - info.requiresFib = requiresFib; - info.json = json; - - m_pythonOperators.append(info); -} - -const QList& OperatorFactory::registeredPythonOperators() -{ - return m_pythonOperators; -} - -} // namespace tomviz diff --git a/tomviz/legacy/operators/OperatorFactory.h b/tomviz/legacy/operators/OperatorFactory.h deleted file mode 100644 index 76ecf1064..000000000 --- a/tomviz/legacy/operators/OperatorFactory.h +++ /dev/null @@ -1,60 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizOperatorFactory_h -#define tomvizOperatorFactory_h - -#include - -#include "../DataSource.h" - -namespace tomviz { -class Operator; - -struct PythonOperatorInfo -{ - QString label; - QString source; - bool requiresTiltSeries; - bool requiresVolume; - bool requiresFib; - QString json; -}; - -class OperatorFactory -{ - typedef QObject Superclass; - -public: - static OperatorFactory& instance(); - - /// Returns a list of module types - QList operatorTypes(); - - Operator* createConvertToVolumeOperator( - DataSource::DataSourceType t = DataSource::Volume); - - /// Creates an operator of the given type - Operator* createOperator(const QString& type, DataSource* ds); - - /// Returns the type for an operator instance. - const char* operatorType(const Operator* module); - - /// Register a Python operator - void registerPythonOperator(const QString& label, const QString& source, - bool requiresTiltSeries, bool requiresVolume, - bool requiresFib, const QString& json); - - /// Returns the list of register Python operators - const QList& registeredPythonOperators(); - -private: - OperatorFactory(); - ~OperatorFactory(); - Q_DISABLE_COPY(OperatorFactory) - - QList m_pythonOperators; -}; -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/operators/OperatorProxy.cxx b/tomviz/legacy/operators/OperatorProxy.cxx deleted file mode 100644 index 0f332ad10..000000000 --- a/tomviz/legacy/operators/OperatorProxy.cxx +++ /dev/null @@ -1,76 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "OperatorProxy.h" - -#include "core/PythonFactory.h" - -#include "OperatorPython.h" - -#include - -namespace tomviz { - -OperatorProxy::OperatorProxy(void* o) : OperatorProxyBase(o) -{ - m_op = static_cast(o); -} - -bool OperatorProxy::canceled() -{ - return m_op->isCanceled(); -} - -bool OperatorProxy::completed() -{ - return m_op->isCompleted(); -} - -void OperatorProxy::setTotalProgressSteps(int progress) -{ - m_op->setTotalProgressSteps(progress); -} - -int OperatorProxy::totalProgressSteps() -{ - return m_op->totalProgressSteps(); -} - -void OperatorProxy::setProgressStep(int progress) -{ - m_op->setProgressStep(progress); -} - -int OperatorProxy::progressStep() -{ - return m_op->progressStep(); -} - -void OperatorProxy::setProgressMessage(const std::string& message) -{ - QString msg = QString::fromStdString(message); - m_op->setProgressMessage(msg); -} - -std::string OperatorProxy::progressMessage() -{ - return m_op->progressMessage().toStdString(); -} - -void OperatorProxy::setProgressData(vtkImageData* imageData) -{ - TemporarilyReleaseGil releaseMe; - emit m_op->childDataSourceUpdated(imageData); -} - -OperatorProxyBase* OperatorProxyFactory::create(void* o) -{ - return new OperatorProxy(o); -} - -void OperatorProxyFactory::registerWithFactory() -{ - PythonFactory::instance().setOperatorProxyFactory(new OperatorProxyFactory); -} - -} // namespace tomviz diff --git a/tomviz/legacy/operators/OperatorProxy.h b/tomviz/legacy/operators/OperatorProxy.h deleted file mode 100644 index 950cb264c..000000000 --- a/tomviz/legacy/operators/OperatorProxy.h +++ /dev/null @@ -1,50 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizOperatorProxy_h -#define tomvizOperatorProxy_h - -#include "../core/OperatorProxyBase.h" - -namespace tomviz { - -class OperatorPython; - -/** Pure virtual base class providing a proxy to the operator class. */ -class OperatorProxy : public OperatorProxyBase -{ -public: - OperatorProxy(void* o); - - bool canceled() override; - - bool completed() override; - - void setTotalProgressSteps(int progress) override; - - int totalProgressSteps() override; - - void setProgressStep(int progress) override; - - int progressStep() override; - - void setProgressMessage(const std::string& message) override; - - std::string progressMessage() override; - - void setProgressData(vtkImageData* object) override; - -private: - OperatorPython* m_op = nullptr; -}; - -class OperatorProxyFactory : public OperatorProxyBaseFactory -{ -public: - OperatorProxyBase* create(void* o) override; - static void registerWithFactory(); -}; - -} // namespace tomviz - -#endif \ No newline at end of file diff --git a/tomviz/legacy/operators/OperatorPython.cxx b/tomviz/legacy/operators/OperatorPython.cxx deleted file mode 100644 index 54cd98fb1..000000000 --- a/tomviz/legacy/operators/OperatorPython.cxx +++ /dev/null @@ -1,914 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "OperatorPython.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ActiveObjects.h" -#include "CustomPythonOperatorWidget.h" -#include "../DataSource.h" -#include "EditOperatorWidget.h" -#include "../modules/ModuleManager.h" -#include "OperatorResult.h" -#include "../Pipeline.h" -#include "PythonUtilities.h" -#include "Utilities.h" -#include "pqPythonSyntaxHighlighter.h" - -#include "vtkDataObject.h" -#include "vtkImageData.h" -#include "vtkNew.h" -#include "vtkPointData.h" -#include "vtkSMParaViewPipelineController.h" -#include "vtkSMProxy.h" -#include "vtkSMProxyManager.h" -#include "vtkSMSessionProxyManager.h" -#include "vtkSMSourceProxy.h" -#include "vtkTrivialProducer.h" - -#include "ui_EditPythonOperatorWidget.h" - -namespace { - -class EditPythonOperatorWidget : public tomviz::EditOperatorWidget -{ - Q_OBJECT - -public: - EditPythonOperatorWidget( - QWidget* p, tomviz::OperatorPython* o, - tomviz::CustomPythonOperatorWidget* customWidget = nullptr) - : tomviz::EditOperatorWidget(p), m_op(o), m_ui(), - m_customWidget(customWidget) - { - m_ui.setupUi(this); - m_ui.name->setText(o->label()); - // Ensure the tab widget expands to fill available vertical space. - m_ui.verticalLayout->setStretch(1, 1); - auto* highlighter = - new pqPythonSyntaxHighlighter(m_ui.script, *m_ui.script); - highlighter->ConnectHighligter(); - // Set font after the highlighter, which overwrites with QFont("Monospace") - m_ui.script->setFont( - QFontDatabase::systemFont(QFontDatabase::FixedFont)); - if (!o->script().isEmpty()) { - m_ui.script->setPlainText(o->script()); - } - if (customWidget) { - QVBoxLayout* layout = new QVBoxLayout(); - m_customWidget->setupUI(m_op); - m_customWidget->setValues(m_op->arguments()); - layout->addWidget(m_customWidget, 1); - m_ui.argumentsWidget->setLayout(layout); - } else { - QVBoxLayout* layout = new QVBoxLayout(); - layout->addStretch(); - m_ui.argumentsWidget->setLayout(layout); - } - - onScriptModified(); - setupConnections(); - } - - void setupConnections() - { - connect(m_ui.script, &QTextEdit::textChanged, this, - &EditPythonOperatorWidget::onScriptModified); - connect(m_ui.saveScript, &QPushButton::clicked, this, - &EditPythonOperatorWidget::saveScript); - } - - void setViewMode(const QString& mode) override - { - if (mode == QStringLiteral("viewCode")) { - m_ui.tabWidget->setCurrentWidget(m_ui.scriptTab); - } - } - - void applyChangesToOperator() override - { - if (m_op) { - m_op->setLabel(m_ui.name->text()); - m_op->setScript(m_ui.script->toPlainText()); - if (m_customWidget) { - QMap args; - m_customWidget->getValues(args); - m_op->setArguments(args); - m_customWidget->writeSettings(); - } - } - } - - void onScriptModified() - { - // Update any objects that need an up-to-date script... - if (m_customWidget) { - m_customWidget->setScript(m_ui.script->toPlainText()); - } - } - - void saveScript() - { - QString name = QFileDialog::getSaveFileName( - this, "Save Script", tomviz::userDataPath(), "Python scripts (*.py)"); - if (name.isEmpty()) { - return; - } - - QFile file(name, this); - if (file.exists()) { - file.remove(); - } - - if (!file.open(QIODevice::WriteOnly)) { - auto msg = QString("Failed to open file: %1").arg(name); - QMessageBox::critical(this, "Failed to save script", msg); - qDebug() << msg; - return; - } - - if (file.write(m_ui.script->toPlainText().toLatin1()) == -1) { - auto msg = QString("Failed to write to file: %1").arg(name); - QMessageBox::critical(this, "Failed to save script", msg); - qDebug() << msg; - file.close(); - return; - } - - file.close(); - - // If there is a JSON description, and the file does not exist, - // write that out too. - if (m_op->JSONDescription().isEmpty()) { - return; - } - - // Use the same name with a ".json" extension. - QFileInfo info(name); - auto descriptionFilename = info.path() + "/" + info.baseName() + ".json"; - QFile descriptionFile(descriptionFilename, this); - if (descriptionFile.exists()) { - // Check if the user wants to overwrite it. - auto title = QString("Overwrite description file?"); - auto msg = - QString("%1 exists.\n\nOverwrite it?").arg(descriptionFilename); - auto response = QMessageBox::question(this, title, msg); - if (response == QMessageBox::No) { - // Do not overwrite - return; - } - } - - if (!descriptionFile.open(QIODevice::WriteOnly)) { - auto msg = QString("Failed to open %1").arg(descriptionFilename); - QMessageBox::critical(this, "Failed to save description", msg); - qDebug() << msg; - return; - } - - if (descriptionFile.write(m_op->JSONDescription().toLatin1()) == -1) { - auto msg = - QString("Failed to write to file: %1").arg(descriptionFilename); - QMessageBox::critical(this, "Failed to save description", msg); - qDebug() << msg; - }; - - descriptionFile.close(); - } - -private: - QPointer m_op; - Ui::EditPythonOperatorWidget m_ui; - tomviz::CustomPythonOperatorWidget* m_customWidget; -}; - -QMap> - CustomWidgetMap; -} // namespace - -namespace tomviz { - -void OperatorPython::registerCustomWidget(const QString& key, bool needsData, - CustomWidgetFunction func) -{ - CustomWidgetMap.insert(key, - QPair(needsData, func)); -} - -class OperatorPython::OPInternals -{ -public: - Python::Module OperatorModule; - Python::Module TransformModule; - Python::Function TransformMethod; - Python::Module InternalModule; - Python::Function FindTransformFunction; - Python::Function IsCancelableFunction; - Python::Function IsCompletableFunction; - Python::Function DeleteModuleFunction; - Python::Function TransformMethodWrapper; -}; - -OperatorPython::OperatorPython(DataSource* parentObject) - : Operator(parentObject), d(new OperatorPython::OPInternals()), - m_label("Python Operator") -{ - Python::initialize(); - - { - Python python; - d->OperatorModule = python.import("tomviz.utils"); - if (!d->OperatorModule.isValid()) { - qCritical() << "Failed to import tomviz.utils module."; - } - - d->InternalModule = python.import("tomviz._internal"); - if (!d->InternalModule.isValid()) { - qCritical() << "Failed to import tomviz._internal module."; - } - - d->IsCancelableFunction = d->InternalModule.findFunction("is_cancelable"); - if (!d->IsCancelableFunction.isValid()) { - qCritical() << "Unable to locate is_cancelable."; - } - - d->IsCompletableFunction = d->InternalModule.findFunction("is_completable"); - if (!d->IsCompletableFunction.isValid()) { - qCritical() << "Unable to locate is_completable."; - } - - d->FindTransformFunction = - d->InternalModule.findFunction("find_transform_function"); - if (!d->FindTransformFunction.isValid()) { - qCritical() << "Unable to locate find_transform_function."; - } - - d->DeleteModuleFunction = d->InternalModule.findFunction("delete_module"); - if (!d->DeleteModuleFunction.isValid()) { - qCritical() << "Unable to locate delete_module."; - } - - d->TransformMethodWrapper = d->InternalModule.findFunction("transform_method_wrapper"); - if (!d->TransformMethodWrapper.isValid()) { - qCritical() << "Unable to locate transform_method_wrapper."; - } - } - - auto connectionType = Qt::BlockingQueuedConnection; - if (dataSource() != nullptr && dataSource()->pipeline() != nullptr && - dataSource()->pipeline()->executionMode() != - Pipeline::ExecutionMode::Threaded) { - connectionType = Qt::DirectConnection; - } - // Needed so the worker thread can update data in the UI thread. - connect(this, &OperatorPython::childDataSourceUpdated, this, - QOverload>::of( - &OperatorPython::updateChildDataSource), - connectionType); - - // This connection is needed so we can create new child data sources in the UI - // thread from a pipeline worker threads. - connect( - this, - QOverload>::of( - &OperatorPython::newChildDataSource), - this, - [this](const QString& label, vtkSmartPointer data) { - createNewChildDataSource(label, data); - }, - connectionType); - connect(this, &OperatorPython::newOperatorResult, this, - &OperatorPython::setOperatorResult); -} - -OperatorPython::~OperatorPython() {} - -void OperatorPython::setLabel(const QString& txt) -{ - m_label = txt; - emit labelModified(); -} - -QIcon OperatorPython::icon() const -{ - return QIcon(":/pqWidgets/Icons/pqProgrammableFilter.svg"); -} - -void OperatorPython::setJSONDescription(const QString& str) -{ - if (m_jsonDescription == str) { - return; - } - - m_jsonDescription = str; - - auto document = QJsonDocument::fromJson(m_jsonDescription.toLatin1()); - if (!document.isObject()) { - qCritical() << "Failed to parse operator JSON"; - qCritical() << m_jsonDescription; - return; - } - - QJsonObject root = document.object(); - - // Get the label for the operator - QJsonValueRef labelNode = root["label"]; - if (!labelNode.isUndefined() && !labelNode.isNull()) { - setLabel(labelNode.toString()); - } - - QJsonValueRef widgetNode = root["widget"]; - if (!widgetNode.isUndefined() && !widgetNode.isNull()) { - m_customWidgetID = widgetNode.toString(); - } - - m_resultNames.clear(); - - // Get the number of results - QJsonValueRef resultsNode = root["results"]; - if (!resultsNode.isUndefined() && !resultsNode.isNull()) { - QJsonArray resultsArray = resultsNode.toArray(); - QJsonObject::size_type numResults = resultsArray.size(); - setNumberOfResults(numResults); - - for (QJsonObject::size_type i = 0; i < numResults; ++i) { - OperatorResult* oa = resultAt(i); - if (!oa) { - Q_ASSERT(oa != nullptr); - } - QJsonObject resultNode = resultsArray[i].toObject(); - QJsonValueRef nameValue = resultNode["name"]; - if (!nameValue.isUndefined() && !nameValue.isNull()) { - oa->setName(nameValue.toString()); - m_resultNames.append(nameValue.toString()); - } - QJsonValueRef labelValue = resultNode["label"]; - if (!labelValue.isUndefined() && !labelValue.isNull()) { - oa->setLabel(labelValue.toString()); - } - } - } - - // Get the number of parameters - QJsonValueRef parametersNode = root["parameters"]; - if (!parametersNode.isUndefined() && !parametersNode.isNull()) { - QJsonArray parametersArray = parametersNode.toArray(); - QJsonObject::size_type numParameters = parametersArray.size(); - setNumberOfParameters(numParameters); - } - - // Get child dataset information - QJsonValueRef childDatasetNode = root["children"]; - if (!childDatasetNode.isUndefined() && !childDatasetNode.isNull()) { - QJsonArray childDatasetArray = childDatasetNode.toArray(); - QJsonObject::size_type size = childDatasetArray.size(); - if (size != 1) { - qCritical() << "Only one child dataset is supported for now. Found" - << size << " but only the first will be used"; - } - if (size > 0) { - setHasChildDataSource(true); - QJsonObject dataSourceNode = childDatasetArray[0].toObject(); - QJsonValueRef nameValue = dataSourceNode["name"]; - QJsonValueRef labelValue = dataSourceNode["label"]; - if (!nameValue.isUndefined() && !nameValue.isNull() && - !labelValue.isUndefined() && !labelValue.isNull()) { - m_childDataSourceName = nameValue.toString(); - m_childDataSourceLabel = labelValue.toString(); - } else if (nameValue.isNull()) { - qCritical() << "No name given for child DataSet"; - } else if (labelValue.isNull()) { - qCritical() << "No label given for child DataSet"; - } - } - } - - setHelpFromJson(root); -} - -const QString& OperatorPython::JSONDescription() const -{ - return m_jsonDescription; -} - -void OperatorPython::setScript(const QString& str) -{ - if (m_script != str) { - m_script = str; - - Python::Object result; - Python::Object resultComplete; - { - Python python; - QString moduleName = QString("tomviz_%1").arg(label()); - d->TransformModule = python.import(this->m_script, label(), moduleName); - if (!d->TransformModule.isValid()) { - qCritical("Failed to create module."); - return; - } - - // Delete the module from sys.module so we don't reuse it - Python::Tuple delArgs(1); - Python::Object name(moduleName); - delArgs.set(0, name); - auto delResult = d->DeleteModuleFunction.call(delArgs); - if (!delResult.isValid()) { - qCritical("An error occurred deleting module."); - return; - } - - // Create capsule to hold the pointer to the operator in the python world - Python::Tuple findArgs(2); - Python::Capsule op(this); - - findArgs.set(0, d->TransformModule); - findArgs.set(1, op); - - d->TransformMethod = d->FindTransformFunction.call(findArgs); - if (!d->TransformMethod.isValid()) { - qCritical("Script doesn't have any 'transform' function."); - return; - } - - Python::Tuple isArgs(1); - isArgs.set(0, d->TransformModule); - - result = d->IsCancelableFunction.call(isArgs); - if (!result.isValid()) { - qCritical("Error calling is_cancelable."); - return; - } - - resultComplete = d->IsCompletableFunction.call(isArgs); - if (!resultComplete.isValid()) { - qCritical("Error calling is_completable."); - return; - } - } - - setSupportsCancel(result.toBool()); - setSupportsCompletion(resultComplete.toBool()); - - emit transformModified(); - } -} - -void OperatorPython::createChildDataSource() -{ - // Create child datasets in advance. Keep a map from DataSource to name - // so that we can match Python script return dictionary values containing - // child data after the script finishes. - if (hasChildDataSource() && !childDataSource()) { - // Create uninitialized data set as a placeholder for the data - vtkSmartPointer childData = - vtkSmartPointer::New(); - childData->ShallowCopy( - vtkImageData::SafeDownCast(dataSource()->dataObject())); - emit newChildDataSource(m_childDataSourceLabel, childData); - } -} - -bool OperatorPython::updateChildDataSource(Python::Dict outputDict) -{ - if (hasChildDataSource()) { - Python::Object pyDataObject; - pyDataObject = outputDict[m_childDataSourceName]; - if (!pyDataObject.isValid()) { - qCritical() << "No child dataset named " << m_childDataSourceName - << "defined in dictionary returned from Python script.\n"; - return false; - } else { - vtkObjectBase* vtkobject = Python::VTK::convertToDataObject(pyDataObject); - vtkDataObject* dataObject = vtkDataObject::SafeDownCast(vtkobject); - if (dataObject) { - TemporarilyReleaseGil releaseMe; - emit childDataSourceUpdated(dataObject); - } - } - } - - return true; -} - -bool OperatorPython::updateChildDataSource( - QMap> output) -{ - if (hasChildDataSource()) { - for (QMap>::const_iterator iter = - output.begin(); - iter != output.end(); ++iter) { - - if (iter.key() != m_childDataSourceName) { - qCritical() << "No child dataset named " << m_childDataSourceName - << "defined in dictionary returned from Python script.\n"; - return false; - } - - emit childDataSourceUpdated(iter.value()); - } - } - - return true; -} - -bool OperatorPython::applyTransform(vtkDataObject* data) -{ - if (m_script.isEmpty()) { - return false; - } - if (!d->OperatorModule.isValid() || !d->TransformMethod.isValid() || - !d->TransformMethodWrapper.isValid()) { - return false; - } - - Q_ASSERT(data); - - createChildDataSource(); - - Python::Object result; - { - Python python; - - Python::Tuple args(3); - - // Serialize the operator, so that the transform wrapper can - // decide whether to execute this one internally or externally - auto operatorSerialized = Python::Object(QString::fromUtf8(QJsonDocument(serialize()).toJson())); - - args.set(0, d->TransformMethod); - args.set(1, operatorSerialized); - - // Get the name of the function - auto transformMethod = d->TransformMethod.getAttr("__name__").toString(); - if (transformMethod == "transform_scalars") { - // Use the arguments for transform_scalars() - Python::Object pydata = Python::VTK::GetObjectFromPointer(data); - args.set(2, pydata); - } else if (transformMethod == "transform") { - // Use the arguments for transform() - Python::Object pydata = Python::createDataset(data, *dataSource()); - if (!pydata.isValid()) { - qCritical("Failed to create dataset for transform."); - return false; - } - args.set(2, pydata); - } else { - qDebug() << "Unknown TransformMethod name: " << transformMethod; - return false; - } - - Python::Dict kwargs; - foreach (QString key, m_arguments.keys()) { - auto value = m_arguments[key]; - if (value.canConvert()) { - // Handle special case for data sources... - auto* ds = value.value(); - if (transformMethod == "transform_scalars") { - auto pydata = Python::VTK::GetObjectFromPointer(ds->imageData()); - kwargs.set(key, pydata); - } else if (transformMethod == "transform") { - auto pydata = Python::createDataset(ds->imageData(), *ds); - if (!pydata.isValid()) { - qCritical("Failed to create dataset for transform kwarg."); - return false; - } - kwargs.set(key, pydata); - } else { - qDebug() << "Unknown TransformMethod name: " << transformMethod; - return false; - } - continue; - } - - Variant v = toVariant(value); - kwargs.set(key, v); - } - - result = d->TransformMethodWrapper.call(args, kwargs); - if (!result.isValid()) { - qCritical("Failed to execute the script."); - return false; - } - } - - // Look for additional outputs from the filter returned in a dictionary - int check = 0; - { - Python python; - check = result.isDict(); - } - - bool errorEncountered = false; - if (check) { - Python python; - Python::Dict outputDict = result.toDict(); - - // Support setting child data from the output dictionary - errorEncountered = !updateChildDataSource(outputDict); - - // Results (tables, etc.) - for (int i = 0; i < m_resultNames.size(); ++i) { - Python::Object pyDataObject; - pyDataObject = outputDict[m_resultNames[i]]; - - if (!pyDataObject.isValid()) { - errorEncountered = true; - qCritical() << "No result named" << m_resultNames[i] - << "defined in dictionary returned from Python script.\n"; - continue; - } - vtkObjectBase* vtkobject = - Python::VTK::GetPointerFromObject(pyDataObject, "vtkDataObject"); - vtkDataObject* dataObject = vtkDataObject::SafeDownCast(vtkobject); - if (dataObject) { - // Emit signal so we switch back to UI thread - emit newOperatorResult(m_resultNames[i], dataObject); - } else { - qCritical() << "Result named '" << m_resultNames[i] - << "' is not a vtkDataObject"; - continue; - } - } - - if (errorEncountered) { - - qCritical() << "Dictionary return from Python script is:\n" - << outputDict.toString(); - } - } - - return !errorEncountered; -} - -Operator* OperatorPython::clone() const -{ - OperatorPython* newClone = - new OperatorPython(qobject_cast(parent())); - newClone->setLabel(label()); - newClone->setScript(script()); - newClone->setJSONDescription(JSONDescription()); - return newClone; -} - -QJsonObject OperatorPython::serialize() const -{ - auto json = Operator::serialize(); - json["description"] = JSONDescription(); - json["label"] = label(); - json["script"] = script(); - if (!m_arguments.isEmpty()) { - auto arguments = m_arguments; - - for (auto& value : arguments) { - if (value.canConvert()) { - // Write out the id - auto* ds = value.value(); - value.setValue(ds->id()); - } - } - - json["arguments"] = QJsonObject::fromVariantMap(arguments); - // If we have no description we still need to save the types of - // the arguments - if (JSONDescription().isEmpty() && !m_typeInfo.isEmpty()) { - QJsonObject typeObj; - for (auto itr = m_typeInfo.begin(); itr != m_typeInfo.end(); ++itr) { - typeObj[itr.key()] = itr.value(); - } - json["argumentTypeInformation"] = typeObj; - } - } - - if (!helpUrl().isEmpty()) { - auto helpObj = QJsonObject(); - helpObj["url"] = helpUrl(); - json["help"] = helpObj; - } - - return json; -} - -namespace { -QJsonObject findJsonObject(const QJsonArray& array, const QString& name) -{ - for (int i = 0; i < array.size(); ++i) { - auto object = array[i].toObject(); - if (object["name"].toString() == name) { - return object; - } - } - return QJsonObject(); -} - -QVariant castJsonArg(const QJsonValue& arg, const QString& type) -{ - if (arg.isArray()) { - auto arr = arg.toArray(); - QVariantList arrayList; - if (type == "int" || type == "enumeration") { - for (int i = 0; i < arr.size(); ++i) { - arrayList << arr[i].toInt(); - } - } else if (type == "double") { - for (int i = 0; i < arr.size(); ++i) { - arrayList << arr[i].toDouble(); - } - } else if (type == "select_scalars") { - for (int i = 0; i < arr.size(); ++i) { - arrayList << arr[i].toString(); - } - } - return arrayList; - } else if (arg.isDouble()) { - QVariant variant; - if (type == "int" || type == "enumeration") { - variant = arg.toInt(); - } else if (type == "double") { - variant = arg.toDouble(); - } - return variant; - } else if (arg.isBool()) { - return arg.toBool(); - } else if (arg.isString()) { - return arg.toString(); - } - return QVariant(); -} -} // namespace - -bool OperatorPython::deserialize(const QJsonObject& json) -{ - setJSONDescription(json["description"].toString()); - setLabel(json["label"].toString()); - setScript(json["script"].toString()); - m_arguments.clear(); - // We use the JSON description to ensure things have the correct type. - if (json.contains("arguments")) { - if (!m_jsonDescription.isEmpty()) { - auto document = QJsonDocument::fromJson(m_jsonDescription.toLatin1()); - if (!document.isObject()) { - qCritical() << "Failed to parse operator JSON"; - qCritical() << m_jsonDescription; - return false; - } - auto args = json["arguments"].toObject(); - auto params = document.object()["parameters"].toArray(); - foreach (const QString& key, args.keys()) { - auto param = findJsonObject(params, key); - if (param.isEmpty()) { - continue; - } - - auto value = castJsonArg(args[key], param["type"].toString()); - if (value.toString().startsWith("0x")) { - // Need to convert a data source id to a data source - // The ModuleManager will be responsible for making sure the - // right data source is loaded before deserializing this - // operator. - auto id = value.toString(); - auto* ds = ModuleManager::instance().dataSourceForStateId(id); - if (!ds) { - qDebug() << "Error: no data source found for state id: " << id; - } else { - value.setValue(ds); - } - } - - m_arguments[key] = value; - } - } else if (json.contains("argumentTypeInformation")) { - auto args = json["arguments"].toObject(); - auto typeInfo = json["argumentTypeInformation"].toObject(); - foreach (const QString& key, args.keys()) { - if (!typeInfo.contains(key)) { - qCritical() << "Deserializing operator " << m_label - << " found argument " << key << " with unknown type."; - return false; - } - auto type = typeInfo[key].toString(); - m_arguments[key] = castJsonArg(args[key], type); - } - } - } - - setHelpFromJson(json); - return true; -} - -EditOperatorWidget* OperatorPython::getEditorContents(QWidget* p) -{ - CustomPythonOperatorWidget* widget = nullptr; - if (m_customWidgetID != "" && CustomWidgetMap.contains(m_customWidgetID)) { - if (!CustomWidgetMap[m_customWidgetID].first) { - vtkSmartPointer nullPtr; - widget = CustomWidgetMap[m_customWidgetID].second(p, this, nullPtr); - } else { - // return nullptr so the caller knows to get the input data and - // use the getEditorContentsWithData method - return nullptr; - } - } - return new EditPythonOperatorWidget(p, this, widget); -} - -EditOperatorWidget* OperatorPython::getEditorContentsWithData( - QWidget* p, vtkSmartPointer displayImage) -{ - // Should only be called if there is a custom widget that needs input data - Q_ASSERT(m_customWidgetID != ""); - Q_ASSERT(CustomWidgetMap[m_customWidgetID].first); - auto widget = CustomWidgetMap[m_customWidgetID].second(p, this, displayImage); - return new EditPythonOperatorWidget(p, this, widget); -} - -void OperatorPython::updateChildDataSource(vtkSmartPointer data) -{ - // Check to see if a child data source has already been created. If not, - // create it here. - auto dataSource = childDataSource(); - Q_ASSERT(dataSource); - - if (!dataSource->volumeModuleAutoAdded()) { - // Automatically add a volume so that users can see live updates - // This also fixes some strange issue where the application will - // crash after this function if no visualization module was ever - // created for this child data source, before the new data was - // copied over. - ModuleManager::instance().createAndAddModule("Volume", dataSource, - ActiveObjects::instance().activeView()); - dataSource->setVolumeModuleAutoAdded(true); - } - - // Now deep copy the new data to the child source data if needed - dataSource->copyData(data); - emit dataSource->dataChanged(); - emit dataSource->dataPropertiesChanged(); - ActiveObjects::instance().renderAllViews(); -} - -void OperatorPython::setOperatorResult(const QString& name, - vtkSmartPointer result) -{ - bool resultWasSet = setResult(name.toLatin1().data(), result); - if (!resultWasSet) { - qCritical() << "Could not set result '" << name << "'"; - } -} - -void OperatorPython::setArguments(QMap args) -{ - if (args != m_arguments) { - m_arguments = args; - emit transformModified(); - } -} - -QMap OperatorPython::arguments() const -{ - return m_arguments; -} - -void OperatorPython::setTypeInfo(const QMap& typeInfo) -{ - m_typeInfo = typeInfo; -} - -const QMap& OperatorPython::typeInfo() const -{ - return m_typeInfo; -} - -void OperatorPython::setHelpFromJson(const QJsonObject& json) -{ - // Clear before trying to read - setHelpUrl(""); - auto helpNode = json["help"]; - if (!helpNode.isUndefined() && !helpNode.isNull()) { - // Need to keep a reference to the object or it gets deleted - auto obj = helpNode.toObject(); - auto helpNodeUrl = obj["url"]; - if (!helpNodeUrl.isUndefined() && !helpNodeUrl.isNull()) { - setHelpUrl(helpNodeUrl.toString()); - } - } -} - -void OperatorPython::setChildDataSource(DataSource* source) -{ - if (source != nullptr) { - source->setLabel(m_childDataSourceLabel); - } - Operator::setChildDataSource(source); -} - -} // namespace tomviz -#include "OperatorPython.moc" diff --git a/tomviz/legacy/operators/OperatorPython.h b/tomviz/legacy/operators/OperatorPython.h deleted file mode 100644 index 8d8d11261..000000000 --- a/tomviz/legacy/operators/OperatorPython.h +++ /dev/null @@ -1,119 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizOperatorPython_h -#define tomvizOperatorPython_h - -#include "Operator.h" -#include "PythonUtilities.h" -#include -#include -#include -#include -#include - -namespace tomviz { - -class CustomPythonOperatorWidget; - -class OperatorPython : public Operator -{ - Q_OBJECT - -public: - // The parent must be a DataSource - OperatorPython(DataSource* parent); - ~OperatorPython() override; - - QString label() const override { return m_label; } - void setLabel(const QString& txt); - - /// Returns an icon to use for this operator. - QIcon icon() const override; - - /// Return a new clone. - Operator* clone() const override; - - QJsonObject serialize() const override; - bool deserialize(const QJsonObject& json) override; - - void setJSONDescription(const QString& str); - const QString& JSONDescription() const; - - void setScript(const QString& str); - const QString& script() const { return m_script; } - - EditOperatorWidget* getEditorContents(QWidget* parent) override; - EditOperatorWidget* getEditorContentsWithData( - QWidget* parent, - vtkSmartPointer inputDataForDisplay) override; - bool hasCustomUI() const override { return true; } - - /// Set the arguments to pass to the transform_scalars function - void setArguments(QMap args); - - /// Returns the argument that will be passed to transform_scalars - QMap arguments() const; - - /// Not really "public" but needs to called when running pipeline externally. - /// Needed to create the data source upfront for live updates. - void createChildDataSource(); - - /// Not really "public" but needs to called when running pipeline externally. - bool updateChildDataSource(Python::Dict output); - bool updateChildDataSource( - QMap> output); - - typedef CustomPythonOperatorWidget* (*CustomWidgetFunction)( - QWidget*, Operator*, vtkSmartPointer); - - static void registerCustomWidget(const QString& key, bool needsData, - CustomWidgetFunction func); - - void setTypeInfo(const QMap& typeInfo); - const QMap& typeInfo() const; - - int numberOfParameters() const { return m_numberOfParameters; } - - void setChildDataSource(DataSource* source) override; - -signals: - void newOperatorResult(const QString&, vtkSmartPointer); - /// Signal uses to request that the child data source be updated with - /// a new vtkDataObject. - void childDataSourceUpdated(vtkSmartPointer); - -protected: - bool applyTransform(vtkDataObject* data) override; - -private slots: - void updateChildDataSource(vtkSmartPointer); - void setOperatorResult(const QString& name, - vtkSmartPointer result); - -private: - Q_DISABLE_COPY(OperatorPython) - - void setNumberOfParameters(int n) { m_numberOfParameters = n; } - void setHelpFromJson(const QJsonObject& json); - class OPInternals; - const QScopedPointer d; - QString m_label; - QString m_jsonDescription; - QString m_script; - - // This is for operators without a JSON description but with arguments. - // Serialization needs to know the type of the arguments. - QMap m_typeInfo; - - QString m_customWidgetID; - - QList m_resultNames; - QString m_childDataSourceName = "output"; - QString m_childDataSourceLabel = "Output"; - - QMap m_arguments; - int m_numberOfParameters = 0; -}; -} // namespace tomviz -#endif diff --git a/tomviz/legacy/operators/OperatorResult.cxx b/tomviz/legacy/operators/OperatorResult.cxx deleted file mode 100644 index fa2ef70bc..000000000 --- a/tomviz/legacy/operators/OperatorResult.cxx +++ /dev/null @@ -1,138 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "OperatorResult.h" - -#include "ActiveObjects.h" -#include "../modules/ModuleFactory.h" -#include "../modules/ModuleManager.h" -#include "../modules/ModuleMolecule.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace tomviz { - -OperatorResult::OperatorResult(QObject* parent) - : Superclass(parent), m_name(tr("Unnamed")) -{} - -OperatorResult::~OperatorResult() -{ - finalize(); -} - -void OperatorResult::setName(QString name) -{ - m_name = name; -} - -QString OperatorResult::name() const -{ - return m_name; -} - -void OperatorResult::setLabel(QString label) -{ - m_label = label; -} - -QString OperatorResult::label() const -{ - return m_label; -} - -void OperatorResult::setDescription(QString desc) -{ - m_description = desc; -} - -QString OperatorResult::description() const -{ - return m_description; -} - -bool OperatorResult::finalize() -{ - deleteProxy(); - return true; -} - -vtkDataObject* OperatorResult::dataObject() -{ - vtkDataObject* object = nullptr; - if (m_producerProxy) { - vtkObjectBase* clientSideObject = m_producerProxy->GetClientSideObject(); - vtkTrivialProducer* producer = - vtkTrivialProducer::SafeDownCast(clientSideObject); - object = producer->GetOutputDataObject(0); - } - - return object; -} - -void OperatorResult::setDataObject(vtkDataObject* object) -{ - vtkDataObject* previousObject = dataObject(); - - if (object == previousObject) { - // Nothing to do - return; - } - - if (object == nullptr) { - deleteProxy(); - return; - } - - createProxyIfNeeded(); - - // Set the output in the producer - vtkObjectBase* clientSideObject = m_producerProxy->GetClientSideObject(); - vtkTrivialProducer* producer = - vtkTrivialProducer::SafeDownCast(clientSideObject); - producer->SetOutput(object); -} - -vtkSMSourceProxy* OperatorResult::producerProxy() -{ - createProxyIfNeeded(); - return m_producerProxy; -} - -void OperatorResult::createProxyIfNeeded() -{ - if (!m_producerProxy) { - vtkSMProxyManager* proxyManager = vtkSMProxyManager::GetProxyManager(); - vtkSMSessionProxyManager* sessionProxyManager = - proxyManager->GetActiveSessionProxyManager(); - - vtkSmartPointer producerProxy; - producerProxy.TakeReference( - sessionProxyManager->NewProxy("sources", "TrivialProducer")); - m_producerProxy = vtkSMSourceProxy::SafeDownCast(producerProxy); - m_producerProxy->UpdateVTKObjects(); - - vtkNew controller; - controller->PreInitializeProxy(m_producerProxy); - controller->PostInitializeProxy(m_producerProxy); - controller->RegisterPipelineProxy(m_producerProxy); - } -} - -void OperatorResult::deleteProxy() -{ - if (m_producerProxy) { - vtkNew controller; - controller->UnRegisterPipelineProxy(m_producerProxy); - m_producerProxy = nullptr; - } -} - -} // namespace tomviz diff --git a/tomviz/legacy/operators/OperatorResult.h b/tomviz/legacy/operators/OperatorResult.h deleted file mode 100644 index eef642758..000000000 --- a/tomviz/legacy/operators/OperatorResult.h +++ /dev/null @@ -1,64 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizOperatorResult_h -#define tomvizOperatorResult_h - -#include -#include - -#include - -class vtkDataObject; -class vtkSMSourceProxy; - -namespace tomviz { - -// Output result from an operator. Such results may include label maps or -// tables. This class wraps a single vtkDataObject produced by an operator. -class OperatorResult : public QObject -{ - Q_OBJECT - typedef QObject Superclass; - -public: - OperatorResult(QObject* parent = nullptr); - virtual ~OperatorResult() override; - - /// Set name of object. - void setName(QString name); - QString name() const; - - /// Set label of object. - void setLabel(QString label); - QString label() const; - - /// Set description of object. - void setDescription(QString desc); - QString description() const; - - /// Clean up object, releasing the data object and the proxy created - /// for it. - virtual bool finalize(); - - /// Get and set the data object this result wraps. - virtual vtkDataObject* dataObject(); - virtual void setDataObject(vtkDataObject* dataObject); - - virtual vtkSMSourceProxy* producerProxy(); - -private: - Q_DISABLE_COPY(OperatorResult) - - vtkWeakPointer m_producerProxy; - QString m_name; - QString m_label; - QString m_description = "Operator Result"; - - void createProxyIfNeeded(); - void deleteProxy(); -}; - -} // namespace tomviz - -#endif diff --git a/tomviz/legacy/pybind11/PipelineStateManager.cxx b/tomviz/legacy/pybind11/PipelineStateManager.cxx deleted file mode 100644 index a6880cc00..000000000 --- a/tomviz/legacy/pybind11/PipelineStateManager.cxx +++ /dev/null @@ -1,157 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#include "PipelineStateManager.h" - -#include "../core/PipelineProxyBase.h" -#include "../core/PythonFactory.h" - -using namespace tomviz; - -PipelineStateManager::PipelineStateManager() -{ - m_proxy = PythonFactory::instance().createPipelineProxy(); -} - -void PipelineStateManager::syncToPython() -{ - m_proxy->syncToPython(); -} - -void PipelineStateManager::syncViewsToPython() -{ - m_proxy->syncViewsToPython(); -} - -std::string PipelineStateManager::serialize() -{ - return m_proxy->serialize(); -} - -void PipelineStateManager::load(const std::string& state, - const std::string& stateRelDir) -{ - m_proxy->load(state, stateRelDir); -} - -std::string PipelineStateManager::modulesJson() -{ - return m_proxy->modulesJson(); -} - -std::string PipelineStateManager::operatorsJson() -{ - return m_proxy->operatorsJson(); -} - -std::string PipelineStateManager::serializeOperator(const std::string& path, - const std::string& id) -{ - return m_proxy->serializeOperator(path, id); -} - -void PipelineStateManager::updateOperator(const std::string& path, - const std::string& state) -{ - m_proxy->updateOperator(path, state); -} - -std::string PipelineStateManager::serializeModule(const std::string& path, - const std::string& id) -{ - return m_proxy->serializeModule(path, id); -} - -void PipelineStateManager::updateModule(const std::string& path, - const std::string& state) -{ - m_proxy->updateModule(path, state); -} - -std::string PipelineStateManager::serializeDataSource(const std::string& path, - const std::string& id) -{ - return m_proxy->serializeDataSource(path, id); -} - -void PipelineStateManager::updateDataSource(const std::string& path, - const std::string& state) -{ - m_proxy->updateDataSource(path, state); -} - -void PipelineStateManager::modified(std::vector opPaths, - std::vector modulePaths) -{ - m_proxy->modified(opPaths, modulePaths); -} - -std::string PipelineStateManager::addModule(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& moduleType) -{ - return m_proxy->addModule(dataSourcePath, dataSourceId, moduleType); -} - -std::string PipelineStateManager::addOperator(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& opState) -{ - return m_proxy->addOperator(dataSourcePath, dataSourceId, opState); -} - -std::string PipelineStateManager::addDataSource( - const std::string& dataSourceState) -{ - return m_proxy->addDataSource(dataSourceState); -} - -void PipelineStateManager::removeOperator(const std::string& opPath, - const std::string& dataSourceId, - const std::string& opId) -{ - m_proxy->removeOperator(opPath, dataSourceId, opId); -} - -void PipelineStateManager::removeModule(const std::string& modulePath, - const std::string& dataSourceId, - const std::string& moduleId) -{ - m_proxy->removeModule(modulePath, dataSourceId, moduleId); -} - -void PipelineStateManager::removeDataSource(const std::string& dataSourcePath, - const std::string& dataSourceId) -{ - m_proxy->removeDataSource(dataSourcePath, dataSourceId); -} - -void PipelineStateManager::enableSyncToPython() -{ - m_proxy->enableSyncToPython(); -} - -void PipelineStateManager::disableSyncToPython() -{ - m_proxy->disableSyncToPython(); -} - -void PipelineStateManager::pausePipeline(const std::string& dataSourcePath) -{ - m_proxy->pausePipeline(dataSourcePath); -} - -void PipelineStateManager::resumePipeline(const std::string& dataSourcePath) -{ - m_proxy->resumePipeline(dataSourcePath); -} - -void PipelineStateManager::executePipeline(const std::string& dataSourcePath) -{ - m_proxy->executePipeline(dataSourcePath); -} - -bool PipelineStateManager::pipelinePaused(const std::string& dataSourcePath) -{ - return m_proxy->pipelinePaused(dataSourcePath); -} diff --git a/tomviz/legacy/pybind11/PipelineStateManager.h b/tomviz/legacy/pybind11/PipelineStateManager.h deleted file mode 100644 index 0ece0a7af..000000000 --- a/tomviz/legacy/pybind11/PipelineStateManager.h +++ /dev/null @@ -1,59 +0,0 @@ -/* This source file is part of the Tomviz project, https://tomviz.org/. - It is released under the 3-Clause BSD License, see "LICENSE". */ - -#ifndef tomvizModuleManagerWrapper_h -#define tomvizModuleManagerWrapper_h - -#include -#include - -namespace tomviz { -class PipelineProxyBase; -} - -class PipelineStateManager -{ -public: - PipelineStateManager(); - std::string serialize(); - void load(const std::string& state, const std::string& stateRelDir); - std::string modulesJson(); - std::string operatorsJson(); - std::string serializeOperator(const std::string& path, const std::string& id); - void updateOperator(const std::string& path, const std::string& state); - std::string serializeModule(const std::string& path, const std::string& id); - void updateModule(const std::string& path, const std::string& state); - std::string serializeDataSource(const std::string& path, - const std::string& id); - void updateDataSource(const std::string& path, const std::string& state); - std::string addModule(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& moduleType); - std::string addOperator(const std::string& dataSourcePath, - const std::string& dataSourceId, - const std::string& opState); - std::string addDataSource(const std::string& dataSourceState); - void removeOperator(const std::string& opPath, - const std::string& dataSourceId, - const std::string& opId = ""); - void removeModule(const std::string& modulePath, - const std::string& dataSourceId, - const std::string& moduleId = ""); - void removeDataSource(const std::string& dataSourcePath, - const std::string& dataSourceId = ""); - void modified(std::vector opPaths, - std::vector modulePaths); - void syncToPython(); - void syncViewsToPython(); - void enableSyncToPython(); - void disableSyncToPython(); - void pausePipeline(const std::string& dataSourcePath); - void resumePipeline(const std::string& dataSourcePath); - void executePipeline(const std::string& dataSourcePath); - bool pipelinePaused(const std::string& dataSourcePath); - -private: - tomviz::PipelineProxyBase* m_proxy = nullptr; -}; - -#endif diff --git a/tomviz/pipeline/LegacyStateLoader.cxx b/tomviz/pipeline/LegacyStateLoader.cxx index 8ecf51e0f..768aa54fb 100644 --- a/tomviz/pipeline/LegacyStateLoader.cxx +++ b/tomviz/pipeline/LegacyStateLoader.cxx @@ -6,7 +6,6 @@ #include "ActiveObjects.h" #include "EmdFormat.h" #include "LoadDataReaction.h" -#include "MoleculeSource.h" #include "pipeline/InputPort.h" #include "pipeline/Link.h" @@ -64,6 +63,7 @@ #include #include #include +#include #include #include @@ -937,9 +937,12 @@ void LegacyStateLoader::scheduleViewStateApply(const QJsonObject& state, if (viewJson.isEmpty() || !view || !pipeline) { return; } + // Weak pointer: the view may be destroyed before this fires (see the + // note in scheduleViewStatesApply); applyViewState() no-ops on null. + vtkWeakPointer weakView(view); QObject::connect( pipeline, &Pipeline::executionFinished, pipeline, - [view, viewJson]() { applyViewState(view, viewJson); }, + [weakView, viewJson]() { applyViewState(weakView, viewJson); }, Qt::SingleShotConnection); } @@ -976,10 +979,25 @@ void LegacyStateLoader::scheduleViewStatesApply(const QJsonObject& state, } applyViewState(view, viewJson, /*cameraToo=*/false); + // Capture the view as a weak pointer. This connection is bound to + // the long-lived `pipeline` (ResetReaction only calls + // Pipeline::clear(), so the Pipeline survives a reset / re-load), + // but the view proxy is destroyed by pqDeleteReaction::deleteAll() + // in the next restoreViewsAndLayouts(). If a prior load left this + // connection pending — its pipeline never reached "any sink + // Current", so it never self-disconnected — a later execute would + // otherwise fire applyViewState() on a freed view and crash. + vtkWeakPointer weakView(view); auto conn = std::make_shared(); *conn = QObject::connect( pipeline, &Pipeline::executionFinished, pipeline, - [view, viewJson, pipeline, conn]() { + [weakView, viewJson, pipeline, conn]() { + if (!weakView) { + // View was torn down by a reset / subsequent load. Drop the + // stale connection so it stops firing. + QObject::disconnect(*conn); + return; + } // A SinkNode only reaches NodeState::Current after consume() // succeeded — so "any sink Current" reliably discriminates // the real execute from the synthetic executionFinished we @@ -1008,8 +1026,8 @@ void LegacyStateLoader::scheduleViewStatesApply(const QJsonObject& state, // first-render adjust on a freshly-created view; letting // that settle before we push our camera prevents it from // clipping our saved position. - QTimer::singleShot(0, pipeline, [view, viewJson, conn]() { - applyViewState(view, viewJson, /*cameraToo=*/true); + QTimer::singleShot(0, pipeline, [weakView, viewJson, conn]() { + applyViewState(weakView, viewJson, /*cameraToo=*/true); QObject::disconnect(*conn); }); }); diff --git a/tomviz/pipeline/Pipeline.cxx b/tomviz/pipeline/Pipeline.cxx index b355d0677..60cc8d71e 100644 --- a/tomviz/pipeline/Pipeline.cxx +++ b/tomviz/pipeline/Pipeline.cxx @@ -86,6 +86,15 @@ QList Pipeline::nodes() const void Pipeline::clear() { + // Stop any in-flight execution and wait for the worker to leave the + // current node before deleting anything. Otherwise the worker thread + // can still be executing a node we delete below and emit from / touch + // the freed Node — a SIGSEGV. The close-while-running path + // (MainWindow::closeEvent -> clear()) is the one that hits this. + if (m_executor && m_executor->isRunning()) { + m_executor->cancelAndWait(); + } + // Drop links first so their removal signals fire while both // endpoint nodes are still alive. removeNode() itself also scrubs // any remaining links connected to the node and deletes the node; diff --git a/tomviz/pipeline/PipelineExecutor.h b/tomviz/pipeline/PipelineExecutor.h index a8c8530f4..e352d9358 100644 --- a/tomviz/pipeline/PipelineExecutor.h +++ b/tomviz/pipeline/PipelineExecutor.h @@ -23,6 +23,15 @@ class PipelineExecutor : public QObject virtual void execute(const QList& nodes, Pipeline* pipeline) = 0; virtual void cancel() = 0; + + /// Cancel and block until execution has actually stopped. Callers that + /// are about to delete nodes (e.g. Pipeline::clear()) must use this, not + /// cancel(), so a worker thread can't still be executing a node we free. + /// The base implementation just cancels — enough for synchronous + /// executors, which can't be running when this is called from outside + /// execute(); ThreadedExecutor overrides it to join its worker. + virtual void cancelAndWait() { cancel(); } + virtual bool isRunning() const = 0; signals: diff --git a/tomviz/pipeline/SinkNode.cxx b/tomviz/pipeline/SinkNode.cxx index 92696a7ff..a0a348e87 100644 --- a/tomviz/pipeline/SinkNode.cxx +++ b/tomviz/pipeline/SinkNode.cxx @@ -6,6 +6,7 @@ #include "InputPort.h" #include "Link.h" #include "OutputPort.h" +#include "ThreadUtils.h" namespace tomviz { namespace pipeline { @@ -48,10 +49,27 @@ void SinkNode::onIntermediateData() } } if (!inputs.isEmpty()) { + runConsume(inputs); + } +} + +bool SinkNode::runConsume(const QMap& inputs) +{ + // execute() and onIntermediateData() both run on the pipeline worker + // thread. Sinks that touch GUI / GL / SM-proxy state opt in via + // consumeOnGuiThread() so the whole trio hops to the GUI thread once + // (the worker blocks until it returns), rather than marshaling each + // access individually. + auto body = [&]() { prepareConsume(inputs); - bool success = consume(inputs); - postConsume(success); + bool ok = consume(inputs); + postConsume(ok); + return ok; + }; + if (consumeOnGuiThread()) { + return callOnThread(this, body); } + return body(); } bool SinkNode::execute() @@ -74,9 +92,7 @@ bool SinkNode::execute() } } - prepareConsume(inputs); - bool success = consume(inputs); - postConsume(success); + bool success = runConsume(inputs); if (success) { m_retainedInputs = handles; diff --git a/tomviz/pipeline/SinkNode.h b/tomviz/pipeline/SinkNode.h index fac10abbb..24629c434 100644 --- a/tomviz/pipeline/SinkNode.h +++ b/tomviz/pipeline/SinkNode.h @@ -43,12 +43,24 @@ class SinkNode : public Node /// Default no-op. virtual void onInputDisconnected(InputPort* /*port*/) {} + /// Opt-in for sinks that own GL / render-window / SM-proxy state: + /// return true to run the whole prepareConsume()/consume()/ + /// postConsume() trio on the GUI thread, instead of marshaling each + /// individual access from the worker thread (see ThreadUtils.h). + /// Default false — the trio runs inline on the pipeline worker thread. + virtual bool consumeOnGuiThread() const { return false; } + private slots: void onIntermediateData(); private: void connectUpstreamIntermediate(InputPort* port); + /// Run the prepareConsume()/consume()/postConsume() trio, marshaling + /// it to the GUI thread (blocking the worker until it completes) when + /// consumeOnGuiThread() is true. Returns consume()'s success. + bool runConsume(const QMap& inputs); + /// Retained handles to the input payloads seen during the last /// successful consume(). Two effects: the shared_ptr keeps the /// upstream port's heap PortData alive (so the producer port's diff --git a/tomviz/pipeline/ThreadedExecutor.cxx b/tomviz/pipeline/ThreadedExecutor.cxx index 21e187170..aed04095a 100644 --- a/tomviz/pipeline/ThreadedExecutor.cxx +++ b/tomviz/pipeline/ThreadedExecutor.cxx @@ -13,7 +13,11 @@ #include "Pipeline.h" #include "PortData.h" +#include +#include +#include #include +#include #include #include @@ -42,8 +46,10 @@ class ExecutionWorker : public QObject public: ExecutionWorker(std::atomic& cancelFlag, - std::atomic& currentNode) - : m_cancelFlag(cancelFlag), m_currentNode(currentNode) + std::atomic& currentNode, + QObject* mainThreadContext, QSemaphore& syncSem) + : m_cancelFlag(cancelFlag), m_currentNode(currentNode), + m_mainThreadContext(mainThreadContext), m_syncSem(syncSem) { } @@ -52,6 +58,12 @@ public slots: { Q_UNUSED(pipeline); + // Drop any stray barrier permit left by a previous run that was + // cancelled while parked at the per-node barrier below, so this + // run's first barrier actually waits for the main thread. + while (m_syncSem.tryAcquire()) { + } + // Per-plan strong-ref retainer; see DefaultExecutor::execute for the // detailed rationale. Local to this slot so it drops as soon as the // plan finishes, evicting any transient outputs that no consumer @@ -118,6 +130,26 @@ public slots: m_currentNode.store(nullptr); emit nodeFinished(node, success); + // Barrier: don't start the next node until the main thread has + // drained this node's nodeExecutionFinished handlers. The + // color-map rescale wired to that signal pushes ParaView SM proxy + // state through the session (vtkSMProxy::UpdateVTKObjects), which + // is not thread-safe against the operator we'd otherwise start + // running here concurrently — that race was the SIGBUS in + // VolumeData::rescaleColorMap. The release is posted *after* the + // queued nodeFinished above (same receiver, same thread → FIFO), + // so by the time it runs the handlers have finished. The + // cancel-checked timeout keeps ~ThreadedExecutor's QThread::wait() + // from deadlocking against a worker parked here during teardown. + QMetaObject::invokeMethod( + m_mainThreadContext, [this]() { m_syncSem.release(); }, + Qt::QueuedConnection); + while (!m_syncSem.tryAcquire(1, 50)) { + if (m_cancelFlag.load()) { + break; + } + } + for (auto* input : node->inputPorts()) { input->clearHandle(); } @@ -145,11 +177,14 @@ public slots: private: std::atomic& m_cancelFlag; std::atomic& m_currentNode; + QObject* m_mainThreadContext; + QSemaphore& m_syncSem; }; ThreadedExecutor::ThreadedExecutor(QObject* parent) : PipelineExecutor(parent), m_thread(new QThread(this)), - m_worker(new ExecutionWorker(m_cancelRequested, m_currentNode)) + m_worker(new ExecutionWorker(m_cancelRequested, m_currentNode, this, + m_syncSem)) { m_worker->moveToThread(m_thread); @@ -178,7 +213,19 @@ ThreadedExecutor::~ThreadedExecutor() m_pendingNodes.clear(); cancel(); m_thread->quit(); - m_thread->wait(); + // The worker may be parked in a BlockingQueuedConnection marshaling a + // sink's consume() onto this (the GUI) thread (see SinkNode::runConsume + // / ThreadUtils) or in the per-node barrier above. A bare wait() would + // deadlock the first case: the worker can't return until we service the + // posted call, but wait() doesn't run our event loop. Pump just the + // queued meta-calls until the thread exits so any in-flight marshal + // completes and the worker unwinds. Restricting to MetaCall avoids + // re-dispatching paint/timer/input events mid-teardown. When the worker + // is idle (the common case) wait(10) returns at once and nothing is + // pumped. + while (!m_thread->wait(10)) { + QCoreApplication::sendPostedEvents(nullptr, QEvent::MetaCall); + } delete m_worker; } @@ -234,6 +281,36 @@ void ThreadedExecutor::cancel() } } +void ThreadedExecutor::cancelAndWait() +{ + if (!m_running.load()) { + return; + } + // Drop any queued follow-up run so executePending() can't restart us + // out from under the wait below. + m_pendingPipeline = nullptr; + m_pendingNodes.clear(); + cancel(); + + // Block until the worker leaves the current node and run() returns. + // The worker only checks the cancel flag at node boundaries (a running + // Python transform isn't interrupted mid-call), so we must wait for it + // — Pipeline::clear() deletes nodes right after this, and the worker + // emitting from / touching a freed Node is a SIGSEGV. executionComplete + // fires from our executionDone handler once the worker is idle; a + // nested event loop keeps delivering the worker's queued signals (and + // any sink consume() marshaled to this thread) until then, so it can't + // deadlock. ExcludeUserInputEvents avoids acting on stray clicks while + // we're tearing down. + QEventLoop loop; + auto conn = connect(this, &PipelineExecutor::executionComplete, &loop, + &QEventLoop::quit); + if (m_running.load()) { + loop.exec(QEventLoop::ExcludeUserInputEvents); + } + disconnect(conn); +} + bool ThreadedExecutor::isRunning() const { return m_running; diff --git a/tomviz/pipeline/ThreadedExecutor.h b/tomviz/pipeline/ThreadedExecutor.h index 149214559..a02768aaf 100644 --- a/tomviz/pipeline/ThreadedExecutor.h +++ b/tomviz/pipeline/ThreadedExecutor.h @@ -6,6 +6,8 @@ #include "PipelineExecutor.h" +#include + #include class QThread; @@ -25,12 +27,19 @@ class ThreadedExecutor : public PipelineExecutor void execute(const QList& nodes, Pipeline* pipeline) override; void cancel() override; + void cancelAndWait() override; bool isRunning() const override; private: void executePending(); QThread* m_thread = nullptr; + // Per-node barrier: the worker releases nothing itself; the main + // thread releases one permit after each node's nodeExecutionFinished + // handlers (the color-map rescale) have run, letting the worker start + // the next node. Constructed before m_worker so the reference handed + // to the worker is valid. See the barrier in ExecutionWorker::run. + QSemaphore m_syncSem; ExecutionWorker* m_worker = nullptr; std::atomic m_cancelRequested{ false }; std::atomic m_running{ false }; diff --git a/tomviz/pipeline/data/VolumeData.cxx b/tomviz/pipeline/data/VolumeData.cxx index 0cecf857b..714faafa7 100644 --- a/tomviz/pipeline/data/VolumeData.cxx +++ b/tomviz/pipeline/data/VolumeData.cxx @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -478,16 +479,7 @@ QVector VolumeData::tiltAngles() const void VolumeData::setTiltAngles(const QVector& angles) { - if (!m_imageData) { - return; - } - vtkNew array; - array->SetName("tilt_angles"); - array->SetNumberOfTuples(angles.size()); - for (int i = 0; i < angles.size(); ++i) { - array->SetValue(i, angles[i]); - } - m_imageData->GetFieldData()->AddArray(array); + setTiltAngles(m_imageData, angles); } bool VolumeData::hasTiltAngles(vtkImageData* image) @@ -516,6 +508,21 @@ QVector VolumeData::getTiltAngles(vtkImageData* image) return result; } +void VolumeData::setTiltAngles(vtkImageData* image, + const QVector& angles) +{ + if (!image) { + return; + } + vtkNew array; + array->SetName("tilt_angles"); + array->SetNumberOfTuples(angles.size()); + for (int i = 0; i < angles.size(); ++i) { + array->SetValue(i, angles[i]); + } + image->GetFieldData()->AddArray(array); +} + bool VolumeData::hasScanIds() const { return hasScanIds(m_imageData); @@ -528,16 +535,7 @@ QVector VolumeData::scanIds() const void VolumeData::setScanIds(const QVector& ids) { - if (!m_imageData) { - return; - } - vtkNew array; - array->SetName("scan_ids"); - array->SetNumberOfTuples(ids.size()); - for (int i = 0; i < ids.size(); ++i) { - array->SetValue(i, ids[i]); - } - m_imageData->GetFieldData()->AddArray(array); + setScanIds(m_imageData, ids); } bool VolumeData::hasScanIds(vtkImageData* image) @@ -566,6 +564,126 @@ QVector VolumeData::getScanIds(vtkImageData* image) return result; } +void VolumeData::setScanIds(vtkImageData* image, const QVector& ids) +{ + if (!image) { + return; + } + vtkNew array; + array->SetName("scan_ids"); + array->SetNumberOfTuples(ids.size()); + for (int i = 0; i < ids.size(); ++i) { + array->SetValue(i, ids[i]); + } + image->GetFieldData()->AddArray(array); +} + +void VolumeData::clearScanIds(vtkImageData* image) +{ + if (image && image->GetFieldData()) { + image->GetFieldData()->RemoveArray("scan_ids"); + } +} + +bool VolumeData::wasSubsampled(vtkImageData* image) +{ + if (!image || !image->GetFieldData()) { + return false; + } + auto* arr = image->GetFieldData()->GetArray("was_subsampled"); + return arr != nullptr && arr->GetNumberOfTuples() > 0 && + arr->GetComponent(0, 0) != 0; +} + +void VolumeData::setWasSubsampled(vtkImageData* image, bool b) +{ + if (!image) { + return; + } + vtkNew array; + array->SetName("was_subsampled"); + array->SetNumberOfTuples(1); + array->SetValue(0, b ? 1 : 0); + image->GetFieldData()->AddArray(array); +} + +void VolumeData::subsampleStrides(vtkImageData* image, int strides[3]) +{ + for (int i = 0; i < 3; ++i) { + strides[i] = 1; + } + if (!image || !image->GetFieldData()) { + return; + } + auto* arr = image->GetFieldData()->GetArray("subsample_strides"); + if (arr && arr->GetNumberOfTuples() >= 3) { + for (int i = 0; i < 3; ++i) { + strides[i] = static_cast(arr->GetComponent(i, 0)); + } + } +} + +void VolumeData::setSubsampleStrides(vtkImageData* image, int strides[3]) +{ + if (!image) { + return; + } + vtkNew array; + array->SetName("subsample_strides"); + array->SetNumberOfTuples(3); + for (int i = 0; i < 3; ++i) { + array->SetValue(i, strides[i]); + } + image->GetFieldData()->AddArray(array); +} + +void VolumeData::subsampleVolumeBounds(vtkImageData* image, int bounds[6]) +{ + for (int i = 0; i < 6; ++i) { + bounds[i] = -1; + } + if (!image || !image->GetFieldData()) { + return; + } + auto* arr = image->GetFieldData()->GetArray("subsample_volume_bounds"); + if (arr && arr->GetNumberOfTuples() >= 6) { + for (int i = 0; i < 6; ++i) { + bounds[i] = static_cast(arr->GetComponent(i, 0)); + } + } +} + +void VolumeData::setSubsampleVolumeBounds(vtkImageData* image, int bounds[6]) +{ + if (!image) { + return; + } + vtkNew array; + array->SetName("subsample_volume_bounds"); + array->SetNumberOfTuples(6); + for (int i = 0; i < 6; ++i) { + array->SetValue(i, bounds[i]); + } + image->GetFieldData()->AddArray(array); +} + +void VolumeData::setType(vtkImageData* image, DataType type) +{ + if (!image) { + return; + } + vtkNew array; + array->SetName("tomviz_data_source_type"); + array->SetNumberOfTuples(1); + array->SetValue(0, static_cast(type)); + image->GetFieldData()->AddArray(array); + + if (type != DataType::TiltSeries) { + // Mirror legacy DataSource: a non-tilt-series type has no tilt angles. + image->GetFieldData()->RemoveArray("tilt_angles"); + } +} + void VolumeData::setTimeSteps(const QList& steps) { m_timeSteps = steps; diff --git a/tomviz/pipeline/data/VolumeData.h b/tomviz/pipeline/data/VolumeData.h index 7b1574537..8c9b510ef 100644 --- a/tomviz/pipeline/data/VolumeData.h +++ b/tomviz/pipeline/data/VolumeData.h @@ -171,6 +171,7 @@ class VolumeData /// in its field data (array named "tilt_angles"). static bool hasTiltAngles(vtkImageData* image); static QVector getTiltAngles(vtkImageData* image); + static void setTiltAngles(vtkImageData* image, const QVector& angles); // -- Scan ID accessors -- @@ -180,6 +181,31 @@ class VolumeData static bool hasScanIds(vtkImageData* image); static QVector getScanIds(vtkImageData* image); + static void setScanIds(vtkImageData* image, const QVector& ids); + static void clearScanIds(vtkImageData* image); + + // -- Subsample metadata (stored in field data) -- + + static bool wasSubsampled(vtkImageData* image); + static void setWasSubsampled(vtkImageData* image, bool b); + static void subsampleStrides(vtkImageData* image, int strides[3]); + static void setSubsampleStrides(vtkImageData* image, int strides[3]); + static void subsampleVolumeBounds(vtkImageData* image, int bounds[6]); + static void setSubsampleVolumeBounds(vtkImageData* image, int bounds[6]); + + // -- Data type metadata (persisted in EMD field data) -- + + /// On-disk tomviz data-source type, stored as an int8 in the image's + /// field data ("tomviz_data_source_type"). Distinct from the runtime + /// PortType; this is the value EMD files carry for compatibility. + enum class DataType : int + { + Volume = 0, + TiltSeries = 1, + Fib = 2 + }; + + static void setType(vtkImageData* image, DataType type); // -- Time series support -- diff --git a/tomviz/pipeline/sinks/ClipSink.cxx b/tomviz/pipeline/sinks/ClipSink.cxx index 82cffb87c..a366547a3 100644 --- a/tomviz/pipeline/sinks/ClipSink.cxx +++ b/tomviz/pipeline/sinks/ClipSink.cxx @@ -202,13 +202,9 @@ bool ClipSink::consume(const QMap& inputs) applyDirection(); syncClippingPlane(); - // SetEnabled() touches the renderer and Qt GL state; consume() runs - // on the pipeline worker thread, so defer to the GUI thread. - QMetaObject::invokeMethod(this, [this]() { - if (m_widget) { - m_widget->SetEnabled(visibility() ? 1 : 0); - } - }, Qt::QueuedConnection); + // SetEnabled() touches the renderer and Qt GL state. consume() runs + // on the GUI thread (consumeOnGuiThread()), so apply directly. + m_widget->SetEnabled(visibility() ? 1 : 0); } else { // Without widget: just update clipping plane from stored direction double cx = (m_bounds[0] + m_bounds[1]) / 2.0; diff --git a/tomviz/pipeline/sinks/ContourSink.cxx b/tomviz/pipeline/sinks/ContourSink.cxx index c9b6e67db..573ff2f8e 100644 --- a/tomviz/pipeline/sinks/ContourSink.cxx +++ b/tomviz/pipeline/sinks/ContourSink.cxx @@ -147,9 +147,9 @@ bool ContourSink::consume(const QMap& inputs) m_flyingEdges->Update(); m_mapper->SetInputDataObject(m_flyingEdges->GetOutput()); - // Defer panel update to the main thread (consume() runs on a worker thread). - QMetaObject::invokeMethod(this, &ContourSink::updatePanel, - Qt::QueuedConnection); + // consume() runs on the GUI thread (consumeOnGuiThread()), so refresh + // the panel directly. + updatePanel(); onMetadataChanged(); emit renderNeeded(); diff --git a/tomviz/pipeline/sinks/LegacyModuleSink.cxx b/tomviz/pipeline/sinks/LegacyModuleSink.cxx index c815d38a5..9f58ec7c1 100644 --- a/tomviz/pipeline/sinks/LegacyModuleSink.cxx +++ b/tomviz/pipeline/sinks/LegacyModuleSink.cxx @@ -30,6 +30,7 @@ #include #include #include +#include namespace tomviz { namespace pipeline { @@ -519,11 +520,19 @@ void LegacyModuleSink::resetCameraIfFirstSink() } } - // Schedule camera reset on the main thread - auto* renderViewProxy = vtkSMRenderViewProxy::SafeDownCast(m_viewProxy); + // Schedule camera reset on the main thread. Capture the view as a + // weak pointer, not raw: m_viewProxy is itself weak because the sink + // doesn't own the view (it's torn down by pqDeleteReaction::deleteAll() + // on reset / state reload). This queued call runs on `this` (the + // sink), which can outlive the view, so a raw capture could fire + // ResetCamera() on a freed proxy. + vtkWeakPointer renderViewProxy( + vtkSMRenderViewProxy::SafeDownCast(m_viewProxy)); if (renderViewProxy) { QMetaObject::invokeMethod(this, [renderViewProxy]() { - renderViewProxy->ResetCamera(); + if (renderViewProxy) { + renderViewProxy->ResetCamera(); + } }, Qt::QueuedConnection); } } diff --git a/tomviz/pipeline/sinks/LegacyModuleSink.h b/tomviz/pipeline/sinks/LegacyModuleSink.h index df3858917..d2432d10d 100644 --- a/tomviz/pipeline/sinks/LegacyModuleSink.h +++ b/tomviz/pipeline/sinks/LegacyModuleSink.h @@ -166,6 +166,14 @@ class LegacyModuleSink : public SinkNode /// otherwise. void postConsume(bool success) override; + // Legacy module sinks own render-window actors / SM proxies and touch + // them throughout the consume() trio (postConsume() rebinds the color + // map via SM proxies, subclasses set actor visibility, etc.), so run + // the whole trio on the GUI thread. Subclasses that do heavy compute + // in consume() (e.g. SegmentSink runs an embedded Python script) opt + // back out. + bool consumeOnGuiThread() const override { return true; } + void onInputDisconnected(InputPort* port) override; void restorePresentation() override { restoreVisualization(); } diff --git a/tomviz/pipeline/sinks/PlotSink.cxx b/tomviz/pipeline/sinks/PlotSink.cxx index 35123d07f..ba12ed98c 100644 --- a/tomviz/pipeline/sinks/PlotSink.cxx +++ b/tomviz/pipeline/sinks/PlotSink.cxx @@ -101,19 +101,16 @@ bool PlotSink::consume(const QMap& inputs) m_table = tablePtr; - // Defer VTK chart manipulation to the main thread — consume() runs on - // the pipeline worker thread, but vtkChartXY / vtkPVContextView objects - // belong to the GUI thread. - QMetaObject::invokeMethod(this, [this]() { - if (m_chart) { - addAllPlots(); - - if (m_contextView) { - m_contextView->Update(); - } + // vtkChartXY / vtkPVContextView belong to the GUI thread; consume() + // runs there (consumeOnGuiThread()), so manipulate the chart directly. + if (m_chart) { + addAllPlots(); + + if (m_contextView) { + m_contextView->Update(); } - emit renderNeeded(); - }, Qt::QueuedConnection); + } + emit renderNeeded(); return true; } diff --git a/tomviz/pipeline/sinks/RulerSink.cxx b/tomviz/pipeline/sinks/RulerSink.cxx index f0cc38f8c..bc3194249 100644 --- a/tomviz/pipeline/sinks/RulerSink.cxx +++ b/tomviz/pipeline/sinks/RulerSink.cxx @@ -126,9 +126,9 @@ bool RulerSink::consume(const QMap& inputs) m_pendingImage = volume->imageData(); - // All SM proxy work must happen on the main thread. - QMetaObject::invokeMethod(this, &RulerSink::setupOrUpdatePipeline, - Qt::QueuedConnection); + // SM proxy work must happen on the GUI thread; consume() runs there + // now (consumeOnGuiThread()). + setupOrUpdatePipeline(); return true; } diff --git a/tomviz/pipeline/sinks/SegmentSink.h b/tomviz/pipeline/sinks/SegmentSink.h index f49ab8908..092733eff 100644 --- a/tomviz/pipeline/sinks/SegmentSink.h +++ b/tomviz/pipeline/sinks/SegmentSink.h @@ -84,6 +84,13 @@ class SegmentSink : public LegacyModuleSink void updateColorMap() override; void updatePanel(); + // consume() runs an embedded Python segmentation script + // (runSegmentation), which must stay on the pipeline worker thread — + // running it on the GUI thread would block the UI and take the GIL on + // the main thread. So opt out of the LegacyModuleSink GUI-thread + // default and keep this sink's consume() on the worker. + bool consumeOnGuiThread() const override { return false; } + private: bool runSegmentation(vtkImageData* input); diff --git a/tomviz/pipeline/sinks/SliceSink.cxx b/tomviz/pipeline/sinks/SliceSink.cxx index e88881f24..78275a249 100644 --- a/tomviz/pipeline/sinks/SliceSink.cxx +++ b/tomviz/pipeline/sinks/SliceSink.cxx @@ -196,18 +196,14 @@ bool SliceSink::consume(const QMap& inputs) // Apply thick slicing m_widget->SetSliceThickness(m_sliceThickness); - // SetEnabled() touches the renderer and Qt GL state; consume() runs - // on the pipeline worker thread, so defer to the GUI thread. + // SetEnabled() touches the renderer and Qt GL state. consume() runs + // on the GUI thread (consumeOnGuiThread()), so apply directly. m_dataReceived = true; - QMetaObject::invokeMethod(this, [this]() { - if (m_widget) { - m_widget->SetEnabled(visibility() ? 1 : 0); - if (visibility()) { - m_widget->SetArrowVisibility(m_showArrow ? 1 : 0); - m_widget->SetInteraction(m_showArrow ? 1 : 0); - } - } - }, Qt::QueuedConnection); + m_widget->SetEnabled(visibility() ? 1 : 0); + if (visibility()) { + m_widget->SetArrowVisibility(m_showArrow ? 1 : 0); + m_widget->SetInteraction(m_showArrow ? 1 : 0); + } } else { // No widget yet (no view initialized) — just store dimensions for later if (m_slice < 0 && isOrtho()) { diff --git a/tomviz/pipeline/sinks/ThresholdSink.cxx b/tomviz/pipeline/sinks/ThresholdSink.cxx index ce3ffe636..8b26d5baf 100644 --- a/tomviz/pipeline/sinks/ThresholdSink.cxx +++ b/tomviz/pipeline/sinks/ThresholdSink.cxx @@ -144,9 +144,9 @@ bool ThresholdSink::consume(const QMap& inputs) m_upper = mid + 0.1 * delta; } - // All SM proxy work must happen on the main thread. - QMetaObject::invokeMethod(this, &ThresholdSink::setupOrUpdatePipeline, - Qt::QueuedConnection); + // SM proxy work must happen on the GUI thread; consume() runs there + // now (consumeOnGuiThread()). + setupOrUpdatePipeline(); onMetadataChanged(); return true; diff --git a/tomviz/pipeline/sinks/VolumeSink.cxx b/tomviz/pipeline/sinks/VolumeSink.cxx index 6d885461a..17ab24257 100644 --- a/tomviz/pipeline/sinks/VolumeSink.cxx +++ b/tomviz/pipeline/sinks/VolumeSink.cxx @@ -166,35 +166,32 @@ bool VolumeSink::consume(const QMap& inputs) int VolumeSink::maxTextureSize() const { // GL queries (and MakeCurrent below) must run on the thread that owns the - // render window's GL context - the GUI thread. consume() runs on the pipeline - // worker thread, so callOnThread() marshals the query over rather than calling - // MakeCurrent here, which would abort with the Qt fatal "Cannot make - // QOpenGLContext current in a different thread". - return callOnThread(const_cast(this), [this]() -> int { - TOMVIZ_ASSERT_GUI_THREAD(); - if (renderView()) { - if (auto* glRW = vtkOpenGLRenderWindow::SafeDownCast( - renderView()->GetRenderWindow())) { - // GetMaximumTextureSize3D only returns a value when the GL context is - // current; consume() usually runs outside a render, so make the window - // current first. This avoids over-bricking on high-limit GPUs (e.g. - // NVIDIA reports 16384) when we would otherwise hit the fallback. - if (glRW->GetNeverRendered() == 0) { - glRW->MakeCurrent(); - } - int maxSize = vtkTextureObject::GetMaximumTextureSize3D(glRW); - if (maxSize > 0) { - return maxSize; - } + // render window's GL context - the GUI thread. This runs inside consume(), + // which VolumeSink runs on the GUI thread (consumeOnGuiThread()), so no + // marshaling is needed; the assert pins that contract. + TOMVIZ_ASSERT_GUI_THREAD(); + if (renderView()) { + if (auto* glRW = vtkOpenGLRenderWindow::SafeDownCast( + renderView()->GetRenderWindow())) { + // GetMaximumTextureSize3D only returns a value when the GL context is + // current; consume() usually runs outside a render, so make the window + // current first. This avoids over-bricking on high-limit GPUs (e.g. + // NVIDIA reports 16384) when we would otherwise hit the fallback. + if (glRW->GetNeverRendered() == 0) { + glRW->MakeCurrent(); + } + int maxSize = vtkTextureObject::GetMaximumTextureSize3D(glRW); + if (maxSize > 0) { + return maxSize; } } - // No usable GL context yet (e.g. the view has not rendered once). 2048 is - // the smallest GL_MAX_3D_TEXTURE_SIZE we expect to encounter, so bricking - // to it is always safe for correctness; on a higher-limit GPU it just means - // we may brick a volume that would have fit, until the next data update - // re-queries the now-current context. - return 2048; - }); + } + // No usable GL context yet (e.g. the view has not rendered once). 2048 is + // the smallest GL_MAX_3D_TEXTURE_SIZE we expect to encounter, so bricking + // to it is always safe for correctness; on a higher-limit GPU it just means + // we may brick a volume that would have fit, until the next data update + // re-queries the now-current context. + return 2048; } void VolumeSink::updateMapperForInput(vtkImageData* image) diff --git a/tomviz/pybind11/CMakeLists.txt b/tomviz/pybind11/CMakeLists.txt index 8388dc9c5..848ad0d39 100644 --- a/tomviz/pybind11/CMakeLists.txt +++ b/tomviz/pybind11/CMakeLists.txt @@ -2,12 +2,9 @@ find_package(TBB REQUIRED) set(CMAKE_MODULE_LINKER_FLAGS "") pybind11_add_module(_wrapping - ../legacy/pybind11/PipelineStateManager.cxx PythonTypeConversions.cxx Wrapping.cxx) target_include_directories(_wrapping PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../legacy/pybind11 - ${CMAKE_CURRENT_SOURCE_DIR}/../legacy/core ${CMAKE_CURRENT_SOURCE_DIR}/../core) target_link_libraries(_wrapping PRIVATE tomvizcore VTK::CommonDataModel VTK::WrappingPythonCore) diff --git a/tomviz/pybind11/Wrapping.cxx b/tomviz/pybind11/Wrapping.cxx index 161992737..602310e19 100644 --- a/tomviz/pybind11/Wrapping.cxx +++ b/tomviz/pybind11/Wrapping.cxx @@ -1,68 +1,11 @@ /* This source file is part of the Tomviz project, https://tomviz.org/. It is released under the 3-Clause BSD License, see "LICENSE". */ -#include "PybindVTKTypeCaster.h" #include -#include - -#include "../legacy/core/DataSourceBase.h" - -#include "PipelineStateManager.h" -#include "PythonTypeConversions.h" -#include "vtkImageData.h" namespace py = pybind11; -using tomviz::DataSourceBase; -using tomviz::MetadataType; - -PYBIND11_VTK_TYPECASTER(vtkImageData) - PYBIND11_MODULE(_wrapping, m) { m.doc() = "tomviz wrapped classes"; - - py::class_(m, "DataSource") - .def_property_readonly("dark_data", &DataSourceBase::darkData, - "Get the dark image data") - .def_property_readonly("white_data", &DataSourceBase::whiteData, - "Get the white image data") - .def_property_readonly("file_name", - [](const DataSourceBase& b) { return b.fileName(); }, - "Get the file from which the data source was loaded") - .def_property_readonly("metadata", - [](const DataSourceBase& b) { - auto* obj = tomviz::toPyObject(b.metadata()); - // The PyObject* reference is currently unmanaged. - // Therefore, we steal it, rather than borrow. - return py::reinterpret_steal(obj); - }, - "Get the data source metadata"); - - py::class_(m, "PipelineStateManagerBase") - .def(py::init()) - .def("serialize", &PipelineStateManager::serialize) - .def("load", &PipelineStateManager::load) - .def("module_json", &PipelineStateManager::modulesJson) - .def("operator_json", &PipelineStateManager::operatorsJson) - .def("serialize_op", &PipelineStateManager::serializeOperator) - .def("serialize_module", &PipelineStateManager::serializeModule) - .def("serialize_datasource", &PipelineStateManager::serializeDataSource) - .def("update_op", &PipelineStateManager::updateOperator) - .def("update_module", &PipelineStateManager::updateModule) - .def("update_datasource", &PipelineStateManager::updateDataSource) - .def("modified", &PipelineStateManager::modified) - .def("add_module", &PipelineStateManager::addModule) - .def("add_operator", &PipelineStateManager::addOperator) - .def("add_datasource", &PipelineStateManager::addDataSource) - .def("remove_operator", &PipelineStateManager::removeOperator) - .def("remove_module", &PipelineStateManager::removeModule) - .def("remove_datasource", &PipelineStateManager::removeDataSource) - .def("enable_sync_to_python", &PipelineStateManager::enableSyncToPython) - .def("disable_sync_to_python", &PipelineStateManager::disableSyncToPython) - .def("pause_pipeline", &PipelineStateManager::pausePipeline) - .def("resume_pipeline", &PipelineStateManager::resumePipeline) - .def("execute_pipeline", &PipelineStateManager::executePipeline) - .def("pipeline_paused", &PipelineStateManager::pipelinePaused); - } diff --git a/tomviz/python/tomviz/state/__init__.py b/tomviz/python/tomviz/state/__init__.py deleted file mode 100644 index e2afe5cde..000000000 --- a/tomviz/python/tomviz/state/__init__.py +++ /dev/null @@ -1,137 +0,0 @@ -import json -from pathlib import Path - -from ._schemata import ( - TomvizSchema, -) - -from ._models import ( # noqa - Pipeline, - DataSource, - init_operators, - init_modules, -) - -from ._jsonpatch import ( - sync_state_to_app, - sync_state_to_python -) - -from ._pipeline import PipelineStateManager - -from ._views import ( - View -) - -from paraview.simple import ( - GetViews, - GetActiveView, -) - -t = None -pipelines = None -views = [] -active_view = None -_state = None - - -def _init(): - global pipelines - global _state - _state = PipelineStateManager().serialize() - _state = json.loads(_state) - schema = TomvizSchema() - - global t - t = schema.load(_state) - pipelines = t.pipelines - - -def _sync_to_python(pipeline_state): - global _state - schema = TomvizSchema() - _state = schema.dump(t) - - sync_state_to_python(_state, json.loads(pipeline_state)) - - _state = schema.dump(t) - - -def _current_state(): - global t - schema = TomvizSchema() - return schema.dump(t) - - -def _pipeline_index(ds): - for (i, p) in enumerate(pipelines): - if p.dataSource == ds: - return i - break - - return -1 - - -def _active_view(): - return View(GetActiveView()) - - -def _views(): - return [View(v) for v in GetViews()] - - -views = _views() -active_view = _active_view() - - -def _sync_views(): - global views - global active_view - - views = _views() - active_view = _active_view() - - -def sync(): - global _state - - schema = TomvizSchema() - new_state = schema.dump(t) - - sync_state_to_app(_state, new_state) - _state = schema.dump(t)#new_state - - -def reset(): - _init() - - -def load(state, state_dir=None): - def _load_from_path(path): - nonlocal state_dir - state_dir = str(path.parent) - with path.open('r') as fp: - state = fp.read() - - return state - - if isinstance(state, str): - path = Path(state) - state = _load_from_path(path) - elif isinstance(state, Path): - state = _load_from_path(state) - elif isinstance(state, dict): - state = json.dumps(state) - elif hasattr(state, 'read'): - state = state.read() - - if state_dir is None: - raise Exception("'state_dir' must be provided inorder to locate data " - "associated with state file.") - - PipelineStateManager().load(state, state_dir) - - -init_modules() -init_operators() -_init() diff --git a/tomviz/python/tomviz/state/_jsonpatch.py b/tomviz/python/tomviz/state/_jsonpatch.py deleted file mode 100644 index f1d906d01..000000000 --- a/tomviz/python/tomviz/state/_jsonpatch.py +++ /dev/null @@ -1,794 +0,0 @@ -import jsonpatch -import re -import json - -from jsonpointer import resolve_pointer - -from ._jsonpath import ( - operator_path, - module_path, - datasource_path, - pipeline_index, - find_operator, - find_module, - find_datasource, - find_removed_object_id - -) - -from ._schemata import ( - load_operator, - load_module, - load_datasource, - dump_module, - dump_operator, - dump_datasource -) - -from ._models import Pipeline - -from ._pipeline import PipelineStateManager - - -def update_list(src, dst): - same = len(src) == len(dst) - if len(src) == len(dst): - for (x, y) in zip(src, dst): - same = hasattr(x, 'id') and hasattr(y, 'id') and x.id == y.id - if not same: - break - - # If we are dealing with a list of the same object we can just update - if same: - for i in range(0, len(src)): - x = src[i] - if isinstance(x, built_in): - dst[i] = x - else: - update(x, dst[i]) - # Otherwise, clear the list and start again - else: - for x in dst: - if hasattr(x, '_kill'): - x._kill() - dst.clear() - for x in src: - dst.append(x) - - -built_in = (str, int, float, complex, dict) - - -def update(src, dst): - for attr, value in vars(src).items(): - if isinstance(value, list): - if not hasattr(dst, attr): - setattr(dst, attr, value) - else: - update_list(value, getattr(dst, attr)) - elif isinstance(value, built_in): - setattr(dst, attr, value) - else: - if not hasattr(dst, attr) or getattr(dst, attr) is None: - setattr(dst, attr, value) - else: - update(value, getattr(dst, attr)) - - return dst - - -def operator_update(patch_op): - # First get path to operator - path = patch_op['path'] - op_path = operator_path(path) - - # Now get the current state of the operator - op = find_operator(op_path.split('/')[1:]) - current_state = PipelineStateManager().serialize_op(path, op.id) - current_state_dct = json.loads(current_state) - - # Apply the update - # Adjust the path - patch_op['path'] = path.replace(op_path, '') - patch = jsonpatch.JsonPatch([patch_op]) - current_state_dct = patch.apply(current_state_dct) - - # Now update the operator - PipelineStateManager().update_op(op_path, json.dumps(current_state_dct)) - - return op_path - - -def module_update(patch_module): - # First get path to module - path = patch_module['path'] - mod_path = module_path(path) - - # Now get the current state of the module - module = find_module(mod_path.split('/')[1:]) - current_state = PipelineStateManager().serialize_module(path, module.id) - current_state_dct = json.loads(current_state) - - # Apply the update - # Adjust the path - patch_module['path'] = path.replace(mod_path, '') - patch = jsonpatch.JsonPatch([patch_module]) - current_state_dct = patch.apply(current_state_dct) - - # Now update the module - PipelineStateManager().update_module(mod_path, - json.dumps(current_state_dct)) - - return mod_path - - -def datasource_update(patch_datasource): - # First get path to datasource - path = patch_datasource['path'] - ds_path = datasource_path(path) - - # Now get the current state of the datasource - datasource = find_datasource(ds_path.split('/')[1:]) - current_state = PipelineStateManager().serialize_datasource(path, - datasource.id) - current_state_dct = json.loads(current_state) - - # Apply the update - # Adjust the path - patch_datasource['path'] = path.replace(ds_path, '') - patch = jsonpatch.JsonPatch([patch_datasource]) - current_state_dct = patch.apply(current_state_dct) - - # Now update the datasource - PipelineStateManager().update_datasource(ds_path, - json.dumps(current_state_dct)) - - return ds_path - - -def module_add(patch_module): - if patch_module['value'] == []: - return - - module_json = patch_module['value'] - if isinstance(module_json, list): - module_json = module_json[0] - path = patch_module['path'] - ds_path = datasource_path(path) - data_source = find_datasource(ds_path.split('/')[1:]) - - state = PipelineStateManager().add_module(ds_path, data_source.id, - module_json['type']) - # If we are adding our first module just find the datasource and the first - # module - parts = path.split('/')[1:] - if path.endswith('modules'): - module = find_datasource(parts).modules[0] - path += '/0' - else: - module = find_module(parts) - - module.properties.visibility = True - updates = module._updates() - state = jsonpatch.JsonPatch(updates).apply(json.loads(state)) - - # Now update the module - PipelineStateManager().update_module(path, json.dumps(state)) - - module_state = PipelineStateManager().serialize_module(path, state['id']) - new_module = load_module(json.loads(module_state)) - update(new_module, module) - - -def operator_add(patch_op): - if patch_op['value'] == []: - return - - op_json = patch_op['value'] - - path = patch_op['path'] - ds_path = datasource_path(path) - data_source = find_datasource(ds_path.split('/')[1:]) - - for o in op_json if isinstance(op_json, list) else [op_json]: - op_state = PipelineStateManager().add_operator(ds_path, data_source.id, - json.dumps(o)) - - op = find_operator(path.split('/')[1:]) - new_op = load_operator(json.loads(op_state)) - update(new_op, op) - - -def datasource_add(patch_datasource): - if patch_datasource['value'] == []: - return - - datasource_json = patch_datasource['value'] - # if isinstance(datasource_json, list): - # datasource_json = datasource_json[0] - path = patch_datasource['path'] - - datasource = find_datasource(path.split('/')[1:]) - datasource_state = \ - PipelineStateManager().add_datasource(json.dumps(datasource_json)) - new_datasource = load_datasource(json.loads(datasource_state)) - update(new_datasource, datasource) - - -def operator_remove(patch_op): - path = patch_op['path'] - ds_path = datasource_path(path) - data_source = find_datasource(ds_path.split('/')[1:]) - - op_id = '' - # Are we removing them all? - if not path.endswith('operators'): - op_id = find_removed_object_id(path) - - PipelineStateManager().remove_operator(path, data_source.id, op_id) - - -def module_remove(patch_module): - path = patch_module['path'] - ds_path = datasource_path(path) - data_source = find_datasource(ds_path.split('/')[1:]) - - module_id = '' - # Are we removing them all? - if not path.endswith('modules'): - module_id = find_removed_object_id(path) - - PipelineStateManager().remove_module(path, data_source.id, module_id) - - -def datasource_remove(patch_datasource): - path = patch_datasource['path'] - - ds_id = '' - # Are we removing them all? - if not path.endswith('dataSources'): - ds_id = find_removed_object_id(path) - - PipelineStateManager().remove_datasource(path, ds_id) - - -_datasource_regx = re.compile(r'.*/dataSources/\d(.*)') - - -def is_datasource_update(path): - datasource_match = _datasource_regx.match(path) - - return (datasource_match and - (is_module_update(datasource_match.group(1)) is None) and - (is_operator_update(datasource_match.group(1)) is None)) - - -_module_regx = re.compile(r'.*/modules/\d(.*)') - - -def is_module_update(path): - module_match = _module_regx.match(path) - - return (module_match and - (is_datasource_update(module_match.group(1)) is None)) - - -_op_regx = re.compile(r'.*/operators/\d(.*)') - - -def is_operator_update(path): - op_match = _op_regx.match(path) - - return op_match and (is_datasource_update(op_match.group(1)) is None) - - -_module_add_regx = re.compile(r'.*/modules/?\d*$') - - -def is_module_add(path): - return _module_add_regx.match(path) - - -_module_prop_add_regx = re.compile(r'.*/modules/\d*/.*$') - - -def is_module_prop_add(path): - return _module_prop_add_regx.match(path) - - -_op_add_regx = re.compile(r'.*/operators/?\d*$') - - -def is_operator_add(path): - return _op_add_regx.match(path) - - -_datasource_add_regx = re.compile(r'.*/dataSources/?\d*$') - - -def is_datasource_add(path): - return _datasource_add_regx.match(path) - - -_op_remove_regx = re.compile(r'.*/operators/?\d*$') - - -def is_operator_remove(path): - return _op_remove_regx.match(path) - - -_module_remove_regx = re.compile(r'.*/modules/?\d*$') - - -def is_module_remove(path): - return _module_remove_regx.match(path) - - -_ds_remove_regx = re.compile(r'.*/dataSources/?\d*$') - - -def is_datasource_remove(path): - return _ds_remove_regx.match(path) - - -def diff(src, dst): - # The semantic for Tomviz state is a little different than a simple dict. - # You can't change a operator or module by simply patch id of a dict. If - # an id is different then the objects should be treated as immutable. So we - # need to post process the operations and replace any attempts to patch - # object ids with a operation to replace the whole object. - - # Utility function to get the prefix from a path - def _prefix(p): - return '/'.join(p.split('/')[:-1]) - - # The new patch - patch = [] - - # The set of paths the we can skip over, as we are doing a full object - # replace - exclude_paths = set() - - # First get the patch from jsonpatch - original_patch = jsonpatch.JsonPatch.from_diff(src, dst) - - # Iterate once to build up list of paths we can discard as we will be - # simple replacing the whole object. We need todo this in two iterations as - # we can't guarantee that the replace operation for a id will appear before - # other operations that we need to exclude. - for op in original_patch: - path_prefix = _prefix(op['path']) - - if op['op'] == 'replace': - if op['path'].endswith('/id'): - # Save the path so we know we can ignore operations that start - # with this path. - exclude_paths.add(path_prefix) - - # Now iterate through and update and exclude appropriate operations. - for op in original_patch: - path_prefix = _prefix(op['path']) - if op['op'] == 'replace': - # We are trying to patch an id, replace with operation that - # replaces the whole object. - if op['path'].endswith('/id'): - - new_object = resolve_pointer(dst, path_prefix) - op = { - "op": "replace", - "path": path_prefix, - "value": new_object - } - - patch.append(op) - continue - - # If not in exclude path add operation - if path_prefix not in exclude_paths: - patch.append(op) - - return patch - - -def mark_modified(ops, modules): - ops_to_mark = {} - - for path in ops: - pindex = pipeline_index(path) - op_index = path[-1] - if pindex not in ops_to_mark or op_index < ops_to_mark[pindex][-1]: - ops_to_mark[pindex] = path - - PipelineStateManager().modified(list(ops_to_mark.values()), list(modules)) - - -def update_app(patch, ops_modified, modules_modified): - path = patch['path'] - - if is_operator_update(path): - ops_modified.add(operator_update(patch)) - elif is_module_update(path): - modules_modified.add(module_update(patch)) - elif is_datasource_update(path): - pass - # TODO: Deal with changing file Names - # datasources_modified.add(datasource_update(o)) - - -def add_to_app(patch): - path = patch['path'] - - if is_module_add(path): - module_add(patch) - elif is_module_update(path): - module_update(patch) - elif is_operator_add(path): - operator_add(patch) - elif is_operator_update(path): - operator_update(patch) - elif is_datasource_add(path): - datasource_add(patch) - elif is_datasource_update(path): - pass - - -def remove_from_app(patch): - path = patch['path'] - if is_operator_remove(path): - operator_remove(patch) - elif is_operator_update(path): - operator_update(patch) - elif is_module_remove(path): - module_remove(patch) - elif is_module_update(path): - module_update(patch) - elif is_datasource_remove(path): - datasource_remove(patch) - elif is_datasource_update(path): - pass - - -def convert_move_app(patch, ops_modified, modules_modified): - # Just convert to a remove and add - state = PipelineStateManager().serialize() - state = json.loads(state) - value = resolve_pointer(state, patch['from']) - remove_from_app({ - 'op': 'remove', - 'path': patch['from'], - }) - - update_app({ - 'op': 'add', - 'path': patch['path'], - 'value': value - }, ops_modified, modules_modified) - - -def sync_state_to_app(src, dst): - patch = diff(src, dst) - - ops_modified = set() - modules_modified = set() - - PipelineStateManager().disable_sync_to_python() - - for o in patch: - patch_op = o['op'] - if patch_op == 'replace': - update_app(o, ops_modified, modules_modified) - elif patch_op == 'add': - add_to_app(o) - elif patch_op == 'remove': - remove_from_app(o) - elif patch_op == 'move': - convert_move_app(o, ops_modified, modules_modified) - else: - raise Exception('Unexcepted op type: %s' % o['op']) - - PipelineStateManager().enable_sync_to_python() - - # Sync from app to ensure python as the updated state - sync_state_to_python() - - mark_modified(ops_modified, modules_modified) - - -# -# Function for syncing state to Python -# - - -def operator_update_python(patch_op, removed_cache): - # First get path to operator - path = patch_op['path'] - op_path = operator_path(path) - - # Now get the current state of the operator - op = find_operator(op_path.split('/')[1:]) - current_op_state = dump_operator(op) - - # Apply the update - # Adjust the path - patch_op['path'] = path.replace(op_path, '') - patch = jsonpatch.JsonPatch([patch_op]) - current_op_state = patch.apply(current_op_state) - new_op = load_operator(current_op_state, removed_cache) - - # Finally update the object inplace - update(new_op, op) - - -def add_mod_to_removed_cache(removed_cache, mod): - removed_cache['modules'][mod.id] = mod - - -def add_ds_to_removed_cache(removed_cache, ds): - removed_cache['dataSources'][ds.id] = ds - - for op in ds.operators: - add_op_to_removed_cache(removed_cache, op) - - for mod in ds.modules: - add_mod_to_removed_cache(removed_cache, mod) - - -def add_op_to_removed_cache(removed_cache, op): - removed_cache['operators'][op.id] = op - - if hasattr(op, 'dataSources'): - for ds in op.dataSources: - add_ds_to_removed_cache(removed_cache, ds) - - -def operator_remove_from_python(patch_op, removed_cache): - # First get path to operator - path = patch_op['path'] - op_path = operator_path(path) - - # Find parent data source - ds = find_datasource(op_path.split('/')[1:]) - - # Now get the the operator - op = find_operator(op_path.split('/')[1:]) - - # Remove from data source - ds.operators.remove(op) - - add_op_to_removed_cache(removed_cache, op) - - -def operator_add_to_python(patch_op, removed_cache): - # First get path to operator - path = patch_op['path'] - op_path = operator_path(path) - - # Find parent data source - ds = find_datasource(op_path.split('/')[1:]) - - # Load the new ops - value = patch_op['value'] - ops = value if isinstance(value, list) else [value] - for op in ops: - op = load_operator(op, removed_cache) - ds.operators.append(op) - - -def module_update_python(patch_module): - # First get path to module - path = patch_module['path'] - mod_path = module_path(path) - - # Now get the current state of the module - module = find_module(mod_path.split('/')[1:]) - current_module_state = dump_module(module) - - # Apply the update - # Adjust the path - patch_module['path'] = path.replace(mod_path, '') - patch = jsonpatch.JsonPatch([patch_module]) - - current_module_state = patch.apply(current_module_state) - new_module = load_module(current_module_state) - - # Finally update the object inplace - update(new_module, module) - - -def module_remove_from_python(patch_op, removed_cache): - # First get path to module - path = patch_op['path'] - mod_path = module_path(path) - - # Find parent data source - ds = find_datasource(mod_path.split('/')[1:]) - - # Now get the the module - mod = find_module(mod_path.split('/')[1:]) - - # Remove from data source - ds.modules.remove(mod) - - # Add to cache, in case we need to resurrect it - add_mod_to_removed_cache(removed_cache, mod) - - # Kill it - # mod._kill() - - -def module_add_to_python(patch_module, removed_cache): - # First get path to module - path = patch_module['path'] - mod_path = module_path(path) - - # Find parent data source - ds = find_datasource(mod_path.split('/')[1:]) - - # Load the new modules - value = patch_module['value'] - mods = value if isinstance(value, list) else [value] - for mod in mods: - mod = load_module(mod, removed_cache) - ds.modules.append(mod) - - -def datasource_update_python(patch_ds, removed_cache): - # First get path to module - path = patch_ds['path'] - ds_path = datasource_path(path) - - # Now get the current state of the datasource - ds = find_datasource(ds_path.split('/')[1:]) - current_ds_state = dump_datasource(ds) - - # Apply the update - # Adjust the path - patch_ds['path'] = path.replace(ds_path, '') - patch = jsonpatch.JsonPatch([patch_ds]) - - current_ds_state = patch.apply(current_ds_state) - new_ds = load_datasource(current_ds_state, removed_cache) - - # Finally update the object inplace - update(new_ds, ds) - - -def datasource_remove_from_python(patch_module, removed_cache): - from . import pipelines - - # First get path to datasource - path = patch_module['path'] - ds_path = datasource_path(path) - - # Find parent data source - ds = find_datasource(ds_path.split('/')[1:]) - - # Remove from pipelines - pipeline = None - for p in pipelines: - if p.dataSource == ds: - pipeline = p - break - - if pipeline is None: - raise Exception('Unable to find data source.') - - pipelines.remove(pipeline) - - # Kill them! - pipeline._kill() - - add_ds_to_removed_cache(removed_cache, ds) - - -def datasource_add_to_python(patch_datasource, removed_cache): - from . import pipelines - # Load the new datasources - value = patch_datasource['value'] - data_sources = value if isinstance(value, list) else [value] - - for ds in data_sources: - ds = load_datasource(ds, removed_cache) - pipelines.append(Pipeline(ds)) - - -def add_to_python(patch, remove_cache): - path = patch['path'] - - if is_module_add(path): - module_add_to_python(patch, remove_cache) - elif is_module_update(path): - module_update_python(patch) - elif is_operator_add(path): - operator_add_to_python(patch, remove_cache) - elif is_operator_update(path): - operator_update_python(patch, remove_cache) - elif is_datasource_add(path): - datasource_add_to_python(patch, remove_cache) - elif is_datasource_update(path): - pass - - -def update_python(patch, remove_cache): - path = patch['path'] - if is_operator_update(path): - operator_update_python(patch, remove_cache) - if is_module_update(path): - module_update_python(patch) - elif is_datasource_update(path): - pass - - -def remove_from_python(patch, removed_cache): - path = patch['path'] - if is_module_remove(path): - module_remove_from_python(patch, removed_cache) - elif is_module_update(path): - module_update_python(patch) - elif is_operator_remove(path): - operator_remove_from_python(patch, removed_cache) - elif is_operator_update(path): - operator_update_python(patch, removed_cache) - elif is_datasource_remove(path): - datasource_remove_from_python(patch, removed_cache) - elif is_datasource_update(path): - datasource_update_python(patch, removed_cache) - - -def convert_move_python(patch, removed_cache): - from . import _current_state - state = _current_state() - # Just convert to a remove and add - value = resolve_pointer(state, patch['from']) - remove_from_python({ - 'op': 'remove', - 'path': patch['from'], - }, removed_cache) - - update_python({ - 'op': 'add', - 'path': patch['path'], - 'value': value - }, removed_cache) - - -def sync_state_to_python(current_python_state=None, current_app_state=None): - from . import _current_state - - if current_python_state is None: - current_python_state = _current_state() - - if current_app_state is None: - current_app_state = PipelineStateManager().serialize() - current_app_state = json.loads(current_app_state) - - patch = diff(current_python_state, current_app_state) - - removed_cache = { - 'modules': {}, - 'operators': {}, - 'dataSources': {} - } - - for o in patch: - patch_op = o['op'] - if patch_op == 'replace': - update_python(o, removed_cache) - elif patch_op == 'add': - add_to_python(o, removed_cache) - elif patch_op == 'remove': - remove_from_python(o, removed_cache) - elif patch_op == 'move': - convert_move_python(o, removed_cache) - else: - raise Exception('Unexpected op type: %s' % o['op']) - - # Now kill off any removed objects - for m in removed_cache['modules'].values(): - m._kill() - - for o in removed_cache['operators'].values(): - o._kill() - - for d in removed_cache['dataSources'].values(): - d._kill() diff --git a/tomviz/python/tomviz/state/_jsonpath.py b/tomviz/python/tomviz/state/_jsonpath.py deleted file mode 100644 index 8308b76f2..000000000 --- a/tomviz/python/tomviz/state/_jsonpath.py +++ /dev/null @@ -1,117 +0,0 @@ -from jsonpointer import resolve_pointer - - -def find_pipeline(path): - from . import pipelines - - if len(path) < 2: - raise ValueError("Path doesn't have enough parts.") - - obj_type = path.pop(0) - if obj_type != 'dataSources': - raise ValueError("Path doesn't start with 'dataSources'.") - - pipeline_index = int(path.pop(0)) - - if pipeline_index >= len(pipelines): - raise ValueError('Pipeline index no longer exists.') - - return pipelines[pipeline_index] - - -def find_operator(path): - pipeline = find_pipeline(path) - - if len(path) < 1: - raise ValueError("Path doesn't have enough parts.") - - obj_type = path.pop(0) - if obj_type != "operators": - raise ValueError("Path doesn't contain 'operators'.") - - op_index = -1 - if path: - op_index = path.pop(0) - op_index = int(op_index) - - operators = pipeline.dataSource.operators - if op_index >= len(operators): - raise ValueError("Operator index no longer exists.") - - return operators[op_index] - - -def find_datasource(path): - copy = path.copy() - pipeline = find_pipeline(copy) - - # If the path has no other dataSources we are done - if 'dataSources' not in copy: - del path[:2] - - return pipeline.dataSource - - # Find the operator (the child data source must be associated with one ...) - op = find_operator(path) - del path[:2] - - return op.dataSources[0] - - -def find_module(path): - # First find the data source the module is attached to. - datasource = find_datasource(path) - if datasource is None: - raise ValueError('Unable to find data source.') - - if len(path) < 1: - raise ValueError("Path doesn't have enough parts.") - - obj_type = path.pop(0) - if obj_type != 'modules': - raise ValueError("Path doesn't contain 'modules'.") - - mod_index = -1 - if path: - mod_index = path.pop(0) - mod_index = int(mod_index) - - modules = datasource.modules - if mod_index >= len(modules): - raise ValueError("Module index no longer exists.") - - return modules[mod_index] - - -# Note: This can only use using during a _update_state(...) call! -def find_removed_object_id(path): - # _state is the old state containing the remove object - from . import _state - obj = resolve_pointer(_state, path) - - return obj['id'] - - -def _path(path, obj_type): - path = path.split('/') - # Find last occurrence - index = len(path) - 1 - path[::-1].index(obj_type) - path = path[0:index+2] - - return '/'.join(path) - - -def operator_path(path): - return _path(path, 'operators') - - -def module_path(path): - return _path(path, 'modules') - - -def datasource_path(path): - return _path(path, 'dataSources') - - -def pipeline_index(path): - return int(path.split('/')[2]) diff --git a/tomviz/python/tomviz/state/_models.py b/tomviz/python/tomviz/state/_models.py deleted file mode 100644 index ef4d41066..000000000 --- a/tomviz/python/tomviz/state/_models.py +++ /dev/null @@ -1,175 +0,0 @@ -import json -import jsonpatch - -from tomviz import operators, modules - -from ._pipeline import PipelineStateManager - -from ._utils import to_namespaces - -op_class_attrs = ['description', 'label', 'script', 'type'] - - -class InvalidStateError(RuntimeError): - pass - - -class Base(object): - def __init__(self, **kwargs): - for k, v in kwargs.items(): - setattr(self, k, v) - - -class Mortal(object): - _dead = False - - def _kill(self): - self._dead = True - - def __getattribute__(self, item): - # Make an exception for the attribute we need to check or we will go - # recursive! - if item == '_dead': - return super(Mortal, self).__getattribute__(item) - - if self._dead: - raise InvalidStateError( - "'%s' no longer represents a valid pipeline element." % self) - else: - return super(Mortal, self).__getattribute__(item) - - -class OperatorMeta(type): - def __init__(cls, name, bases, dict): - attrs = {} - - for k, v in dict.items(): - if k not in op_class_attrs: - attrs[k] = property(lambda self: getattr(self, k), - lambda self, value: setattr(self, k, value)) - else: - attrs[k] = v - - super(OperatorMeta, cls).__init__(name, bases, attrs) - - -class ModuleMeta(type): - def __new__(meta, name, parents, dct): - attrs = {} - - attrs = { - '_props': dct - } - - return super(ModuleMeta, meta).__new__(meta, name, parents, attrs) - - -class Pipeline(Mortal): - def __init__(self, datasource): - self._datasource = datasource - - @property - def dataSource(self): - return self._datasource - - def _ds_path(self): - from . import _pipeline_index - pipeline_index = _pipeline_index(self._datasource) - if pipeline_index < 0: - raise Exception('Pipeline is not valid.') - - return '/dataSources/%d' % pipeline_index - - def pause(self): - PipelineStateManager().pause_pipeline(self._ds_path()) - - def resume(self): - PipelineStateManager().resume_pipeline(self._ds_path()) - - def execute(self): - paused = self.paused() - if paused: - self.resume() - - PipelineStateManager().execute_pipeline(self._ds_path()) - - if paused: - self.pause() - - def paused(self): - return PipelineStateManager().pipeline_paused(self._ds_path()) - - -class Reader(Base, Mortal): - pass - - -class DataSource(Base, Mortal): - def __init__(self, **kwargs): - self.reader = Reader( - name=kwargs.pop('name', None), - fileNames=kwargs.pop('fileNames', []), - subsampleSettings=kwargs.pop('subsampleSettings', {})) - - self.operators = [] - self.modules = [modules.Outline(), modules.Slice()] - super(DataSource, self).__init__(**kwargs) - - -class Operator(Base, Mortal): - pass - - -class Module(Mortal): - def __init__(self, **kwargs): - args = self._props - args.update(kwargs) - - for k, v in args.items(): - if isinstance(v, dict): - v = to_namespaces(v) - setattr(self, k, v) - - def _updates(self): - from ._schemata import dump_module - current_state = dump_module(self) - patch = jsonpatch.JsonPatch.from_diff(self._props, current_state) - patch = json.loads(patch.to_string()) - - return patch - - -class Tomviz(object): - def __init__(self, pipelines=None): - self.pipelines = pipelines if pipelines else [] - - -def module_json_to_classes(module_json): - for name, info in module_json.items(): - info['type'] = name - del info['id'] - # Default visibility to true - info['properties']['visibility'] = True - if not hasattr(modules, name): - cls = ModuleMeta(name, (Module,), info) - setattr(modules, name, cls) - - -def operator_json_to_classes(operator_json): - for name, info in operator_json.items(): - del info['id'] - if not hasattr(operators, name): - cls = OperatorMeta(name, (Operator,), info) - setattr(operators, name, cls) - - -def init_modules(): - module_json_str = PipelineStateManager().module_json() - module_json = json.loads(module_json_str) - module_json_to_classes(module_json) - - -def init_operators(): - operator_json_str = PipelineStateManager().operator_json() - operator_json = json.loads(operator_json_str) - operator_json_to_classes(operator_json) diff --git a/tomviz/python/tomviz/state/_pipeline.py b/tomviz/python/tomviz/state/_pipeline.py deleted file mode 100644 index 72297b1dc..000000000 --- a/tomviz/python/tomviz/state/_pipeline.py +++ /dev/null @@ -1,12 +0,0 @@ -from tomviz._wrapping import PipelineStateManagerBase - - -class PipelineStateManager(PipelineStateManagerBase): - _instance = None - - def __new__(cls, *args, **kwargs): - if cls._instance is None: - cls._instance = PipelineStateManagerBase.__new__(cls, *args, - **kwargs) - - return cls._instance diff --git a/tomviz/python/tomviz/state/_schemata.py b/tomviz/python/tomviz/state/_schemata.py deleted file mode 100644 index 50859c9c8..000000000 --- a/tomviz/python/tomviz/state/_schemata.py +++ /dev/null @@ -1,317 +0,0 @@ -import json -import types - -from marshmallow import ( - fields, - Schema, - post_load, - EXCLUDE, - INCLUDE, - pre_load, - post_dump -) - -from ._models import ( - Tomviz, - Pipeline, - DataSource, - Reader -) - -from ._utils import ( - to_namespaces, - from_namespace, - attrs -) - -from tomviz import operators, modules - - -class OperatorField(fields.Field): - def _serialize(self, value, attr, obj, **kwargs): - if value is None: - return "" - - state = {} - for a in dir(value): - if a.startswith('_') or a == 'name': - continue - - v = getattr(value, a) - if a == 'dataSources': - s = DataSourceSchema() - v = [s.dump(d) for d in v] - state[a] = v - elif attrs(v): - state[a] = from_namespace(v) - else: - state[a] = v - - return state - - def _deserialize(self, value, attr, data, **kwargs): - from ._jsonpatch import update - try: - description = json.loads(value['description']) - name = description['name'] - except (json.decoder.JSONDecodeError, KeyError): - name = ''.join([x.capitalize() for x in value['label'].split(' ')]) - cls = getattr(operators, name) - - args = {} - if 'arguments' in value: - args['arguments'] = to_namespaces(value['arguments']) - if 'dataSources' in value: - s = DataSourceSchema(context=self.context) - datasources = value['dataSources'] - datasources = [s.load(d) for d in datasources] - args['dataSources'] = datasources - args['id'] = value['id'] - - removed_cache = self.context.get('operators', {}) - op = cls(**args) - - # Do we a op that we can recurrect? - if op.id in removed_cache: - op = update(op, removed_cache.pop(op.id)) - - return op - - @property - def context(self): - # Fall back to an empty dictionary if no context exists on the root - return getattr(self.root, 'context', {}) - - -class OperatorSchema(Schema): - operator = OperatorField() - - -def load_operator(operator, removed_cache=None): - schema = OperatorSchema() - if removed_cache is not None: - schema.context = removed_cache - - return schema.load({ - 'operator': operator - })['operator'] - - -def dump_operator(op): - ns = types.SimpleNamespace(operator=op) - return OperatorSchema().dump(ns)['operator'] - - -class ModuleField(fields.Field): - def _serialize(self, value, attr, obj, **kwargs): - if value is None: - return "" - - state = { - 'type': value.__class__.__name__ - } - for a in dir(value): - if a.startswith('_'): - continue - - v = getattr(value, a) - if attrs(v): - v = from_namespace(v) - - state[a] = v - - return state - - def _deserialize(self, value, attr, data, **kwargs): - from ._jsonpatch import update - cls = getattr(modules, value['type']) - - removed_cache = self.context.get('modules', {}) - mod = cls(**value) - - # Do we a module that we can recurrect? - if mod.id in removed_cache: - mod = update(mod, removed_cache.pop(mod.id)) - - return mod - - @property - def context(self): - # Fall back to an empty dictionary if no context exists on the root - return getattr(self.root, 'context', {}) - - -class ModuleSchema(Schema): - module = ModuleField() - - -def load_module(module, removed_cache=None): - schema = ModuleSchema() - if removed_cache is not None: - schema.context = removed_cache - return schema.load({ - 'module': module - })['module'] - - -def dump_module(module): - ns = types.SimpleNamespace(module=module) - return ModuleSchema().dump(ns)['module'] - - -class ColorMap2DBoxSchema(Schema): - x = fields.Float() - y = fields.Float() - height = fields.Float() - width = fields.Float() - - @post_load - def make_namespace(self, data, **kwargs): - return to_namespaces(data) - - class Meta: - unknown = INCLUDE - - -class ColorOpacityMap(Schema): - colorSpace = fields.String() - colors = fields.List(fields.Float) - points = fields.List(fields.Float) - - @post_load - def make_namespace(self, data, **kwargs): - return to_namespaces(data) - - class Meta: - unknown = INCLUDE - - -class GradientOpacityMap(Schema): - points = fields.List(fields.Float) - - @post_load - def make_namespace(self, data, **kwargs): - return to_namespaces(data) - - class Meta: - unknown = INCLUDE - - -class SubsampleSettingsSchema(Schema): - # TODO: maybe we can force 'strides' to be of length 3, - # and volumeBounds to be of length 6? - strides = fields.List(fields.Int()) - volumeBounds = fields.List(fields.Int()) - - -class ReaderSchema(Schema): - fileNames = fields.List(fields.String) - name = fields.String() - subsampleSettings = fields.Nested(SubsampleSettingsSchema()) - - @post_load - def make_reader(self, data, **kwargs): - return Reader(**data) - - @post_dump - def remove_empty(self, data, **kwargs): - if 'name' in data and data['name'] is None: - del data['name'] - - return data - - -class DataSourceSchema(Schema): - operators = fields.List(OperatorField) - colorMap2DBox = fields.Nested(ColorMap2DBoxSchema) - colorOpacityMap = fields.Nested(ColorOpacityMap) - gradientOpacityMap = fields.Nested(GradientOpacityMap) - modules = fields.List(ModuleField) - useDetachedColorMap = fields.Boolean() - spacing = fields.List(fields.Float) - units = fields.String() - id = fields.String() - label = fields.String() - reader = fields.Nested(ReaderSchema) - active = fields.Boolean() - - @post_load - def make_datasource(self, data, **kwargs): - from ._jsonpatch import update - - ds = DataSource(**data) - # Has this data source been removed, if so recurrent it, - # rather than creating a new one. - removed_cache = getattr(self, 'context', {}).get('dataSources', {}) - if ds.id in removed_cache: - update(ds, removed_cache.pop(ds.id)) - - return ds - - @post_dump - def remove_empty(self, data, **kwargs): - remove_if_empty = ['operators', 'modules'] - - return { - k: v for k, v in data.items() if k not in remove_if_empty or v != [] - } - - class Meta: - unknown = EXCLUDE - - -def load_datasource(datasource, removed_cache=None): - schema = DataSourceSchema() - if removed_cache is not None: - schema.context = removed_cache - - return DataSourceSchema().load(datasource) - - -def dump_datasource(datasource): - return DataSourceSchema().dump(datasource) - - -class PipelineSchema(Schema): - dataSource = fields.Nested(DataSourceSchema) - - # The following @pre_load and @post_dump are need to allow use to have - # a Pipeline object with a datasource attribute. The JSON object in - # the serialized state that represents a pipeline has not such attribute. - # Using these two hooks allows use to achieve the object graph we want from - # the serialize state. - @pre_load - def wrap_datasource(self, data, **kwargs): - """ - Add the DataSource as an attribute of the pipeline. - """ - return { - 'dataSource': data - } - - @post_dump - def unwrap_datasource(self, data, **kwargs): - """ - Extract DataSource from pipeline attribute. - """ - - return data['dataSource'] - - @post_load - def make_pipeline(self, data, **kwargs): - return Pipeline(data['dataSource']) - - class Meta: - unknown = EXCLUDE - - -class TomvizSchema(Schema): - pipelines = fields.List(fields.Nested(PipelineSchema), - data_key='dataSources') - - @post_load - def make_tomviz(self, data, **kwargs): - return Tomviz(data['pipelines']) - - class Meta: - unknown = EXCLUDE diff --git a/tomviz/python/tomviz/state/_utils.py b/tomviz/python/tomviz/state/_utils.py deleted file mode 100644 index e23c3deef..000000000 --- a/tomviz/python/tomviz/state/_utils.py +++ /dev/null @@ -1,55 +0,0 @@ -from collections.abc import Mapping -from types import SimpleNamespace - - -def iter_paths(tree, parent_path=()): - for path, node in tree.items(): - current_path = parent_path + (path,) - if isinstance(node, Mapping): - for inner_path in iter_paths(node, current_path): - yield inner_path - else: - yield current_path - - -def to_namespaces(dct): - root = SimpleNamespace() - for path in iter_paths(dct): - prop_name = path[-1] - path = path[:-1] - current_namespace = root - - for p in path: - if hasattr(current_namespace, p): - current_namespace = getattr(current_namespace, p) - else: - new_namespace = SimpleNamespace() - setattr(current_namespace, p, new_namespace) - current_namespace = new_namespace - - v = dct - for k in path + (prop_name,): - v = v[k] - - setattr(current_namespace, prop_name, v) - - return root - - -def from_namespace(namespace): - d = {} - - for a in attrs(namespace): - v = getattr(namespace, a) - if attrs(v): - d[a] = from_namespace(v) - else: - d[a] = v - - return d - - -def attrs(o): - return [x for x in dir(o) if not x.startswith('_') and - not callable(getattr(o, x)) and - not isinstance(o, (int, float, str, dict))] diff --git a/tomviz/python/tomviz/state/_views.py b/tomviz/python/tomviz/state/_views.py deleted file mode 100644 index 97ba23284..000000000 --- a/tomviz/python/tomviz/state/_views.py +++ /dev/null @@ -1,172 +0,0 @@ -from enum import Enum -from paraview.simple import ( - Render, - SaveScreenshot -) - - -class Palette(Enum): - Current = "" - TransparentBackground = "TransparentBackground" - BlackBackground = "BlackBackground" - DefaultBackground = "DefaultBackground" - GradientBackground = "GradientBackground" - GrayBackground = "GrayBackground" - PrintBackground = "PrintBackground" - WhiteBackground = "WhiteBackground" - - -class Camera(object): - def __init__(self, render_view): - self._render_view = render_view - self._camera = render_view.GetActiveCamera() - - def _validate_vec3(self, pos, name): - if not isinstance(pos, (tuple, list)): - raise TypeError('%s should be tuple or list' % name) - - if len(pos) != 3: - raise ValueError('%s must have 3 components' % name) - - def dolly(self, value): - """ - Divide the camera's distance from the focal point by the given dolly - value - """ - self._camera.Dolly(value) - Render(self._render_view) - - @property - def roll(self): - """ - Rotate the camera about the direction of projection. - """ - return self._camera.GetRoll() - - @roll.setter - def roll(self, angle): - self._camera.SetRoll(angle) - Render(self._render_view) - - def azimuth(self, angle): - """ - Rotate the camera about the view up vector centered at the focal point. - """ - self._camera.Azimuth(angle) - Render(self._render_view) - - def yaw(self, angle): - """ - Rotate the focal point about the view up vector, using the camera's - position as the center of rotation. - """ - self._camera.Yaw(angle) - Render(self._render_view) - - def elevation(self, angle): - """ - Rotate the camera about the cross product of the negative of the - direction of projection and the view up vector, using the focal point as - the center of rotation. - """ - self._camera.Elevation(angle) - Render(self._render_view) - - def pitch(self, angle): - """ - Rotate the focal point about the cross product of the view up vector and - the direction of projection, using the camera's position as the center - of rotation. - """ - self._camera.Pitch(angle) - Render(self._render_view) - - @property - def position(self): - """ - Set/Get the position of the camera in world coordinates. - """ - return self._camera.GetPosition() - - @position.setter - def position(self, pos): - self._validate_vec3(pos, 'Position') - [x, y, z] = pos - self._camera.SetPosition(x, y, z) - Render(self._render_view) - - @property - def focal_point(self): - """ - Set/Get the focal of the camera in world coordinates. - """ - return self._camera.GetFocalPoint() - - @focal_point.setter - def focal_point(self, point): - self._validate_vec3(point, 'Point') - [x, y, z] = point - self._camera.SetFocalPoint(x, y, z) - Render(self._render_view) - - @property - def view_up(self): - """ - Set/Get the view up direction for the camera. - """ - return self._camera.GetViewUp() - - @view_up.setter - def view_up(self, direction): - self._validate_vec3(direction, 'Direction') - [vx, vy, vz] = direction - self._camera.SetViewUp(vx, vy, vz) - Render(self._render_view) - - @property - def distance(self): - """ - Move the focal point so that it is the specified distance from the - camera position - """ - return self._camera.GetDistance() - - @distance.setter - def distance(self, distance): - self._camera.SetDistance(distance) - Render(self._render_view) - - def zoom(self, factor): - """ - Decrease the view angle by the specified factor. A value greater than 1 - is a zoom-in, a value less than 1 is a zoom-out. - """ - self._camera.Zoom(factor) - Render(self._render_view) - - -class View(object): - def __init__(self, render_view): - self._render_view = render_view - - def save_screenshot(self, file_path, palette=Palette.Current, width=-1, - height=-1, resolution=None): - options = { - 'OverrideColorPalette': palette.value - } - - if width > 0 and height > 0: - options['ImageResolution'] = (width, height) - - if resolution is not None: - if not isinstance(resolution, (tuple, list)): - raise TypeError('Resolution must be a tuple or list') - if len(resolution) != 2: - raise ValueError('Resolution must have 2 components') - options['ImageResolution'] = resolution - - SaveScreenshot(file_path, viewOrLayout=self._render_view, **options) - - @property - def camera(self): - return Camera(self._render_view)