From 2cfd1fb45ddaa87303caea6ec4f8ec3932c3646d Mon Sep 17 00:00:00 2001 From: OpenSRA Migration Date: Wed, 8 Jul 2026 09:11:42 -0700 Subject: [PATCH 1/7] Qt6 / QGIS 4 / CMake migration Port from Qt 5.15.2 + prebuilt Qt5 QGISPlugin (QMake) to Qt 6.10.2 + the NHERI-SimCenter QGIS 4.1.0 fork built from source, via CMake + Conan v2. - New build system: CMakeLists.txt, OpenSRA.cmake, OpenSRAWorkflow.cmake (selective Workflow module - avoids duplicate LocalApplication/ MainWindowWorkflowApp classes), conanfile.py (Conan v2), msvc_fix.h, cmake/FindQCA.cmake. OpenSRA.pro kept as legacy reference only. - Removes dead code: UIWidgets/OpenSRAPostProcessor_old.{cpp,h}, arcgisruntime.pri, the dead PATH_TO_QGIS_PLUGIN include. - Qt6 API updates across the tree (QRegExpValidator includes, Qt::endl, setContentsMargins, Qt::SkipEmptyParts, QScreen instead of QDesktopWidget, Qgis::RasterResamplingMethod, OPENSRA_PREFERENCES_H include-guard rename). - MIGRATION_NOTES.md: full engineering log (decisions, runtime DLL manifest, known issues). Requires sibling checkouts of R2DTool, SimCenterCommon (each with a small patch - see MIGRATION_NOTES.md) and the built NHERI-SimCenter/QGIS fork. Squashed from local migration work on a snapshot-based repo; original session history retained locally. --- .gitignore | 7 +- CMakeLists.txt | 239 ++++ GeneralInformationWidget.cpp | 13 +- JsonWidgets/JsonDefinedWidget.cpp | 4 +- JsonWidgets/JsonLabel.cpp | 1 - JsonWidgets/JsonLineEdit.cpp | 1 - JsonWidgets/SimCenterJsonWidget.cpp | 10 +- LocalApplication.cpp | 55 +- MIGRATION_NOTES.md | 279 ++++ MainWindowWorkflowApp.cpp | 3 +- OpenSRA.cmake | 260 ++++ OpenSRA.pro | 19 +- OpenSRACommon.pri | 2 + OpenSRAPreferences.h | 4 +- OpenSRAWorkflow.cmake | 54 + ResultsWidget.cpp | 2 +- UIWidgets/AddToRunListWidget.cpp | 6 +- UIWidgets/CustomVisualizationWidget.cpp | 6 +- UIWidgets/DamageMeasureWidget.cpp | 2 +- UIWidgets/DecisionVariableWidget.cpp | 2 +- UIWidgets/EDPLandslideWidget.cpp | 10 +- UIWidgets/EngDemandParamWidget.cpp | 2 +- .../EngineeringDemandParameterWidget.cpp | 2 +- UIWidgets/FixedResidualsSamplingWidget.cpp | 1 - UIWidgets/GenericModelWidget.cpp | 4 +- UIWidgets/IntensityMeasureWidget.cpp | 7 +- UIWidgets/LosAngelesPipelineWidget.cpp | 2 +- UIWidgets/MultiComponentDMWidget.cpp | 4 +- UIWidgets/MultiComponentDVWidget.cpp | 4 +- UIWidgets/MultiComponentEDPWidget.cpp | 4 +- UIWidgets/OpenSHAWidget.cpp | 2 +- UIWidgets/OpenSRAPostProcessor.cpp | 2 +- UIWidgets/OpenSRAPostProcessor_old.cpp | 1133 ----------------- UIWidgets/OpenSRAPostProcessor_old.h | 193 --- UIWidgets/OpenSRAPreProcessor.cpp | 4 +- UIWidgets/PipelineDLWidget.cpp | 2 +- UIWidgets/PipelineNetworkWidget.cpp | 12 +- UIWidgets/RandomVariablesWidget.cpp | 4 +- UIWidgets/SourceCharacterizationWidget.cpp | 2 +- UIWidgets/UncertaintyQuantificationWidget.cpp | 4 +- UIWidgets/WidgetFactory.cpp | 16 +- WorkflowAppOpenSRA.cpp | 2 +- arcgisruntime.pri | 27 - cmake/FindQCA.cmake | 101 ++ conanfile.py | 41 + main.cpp | 2 +- msvc_fix.h | 23 + 47 files changed, 1134 insertions(+), 1445 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 MIGRATION_NOTES.md create mode 100644 OpenSRA.cmake create mode 100644 OpenSRAWorkflow.cmake delete mode 100644 UIWidgets/OpenSRAPostProcessor_old.cpp delete mode 100644 UIWidgets/OpenSRAPostProcessor_old.h delete mode 100644 arcgisruntime.pri create mode 100644 cmake/FindQCA.cmake create mode 100644 conanfile.py create mode 100644 msvc_fix.h diff --git a/.gitignore b/.gitignore index d58186a..1395642 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ # Sensitive Information OpenSRAUserPass.h -*.pro.user \ No newline at end of file +*.pro.user + +# CMake / Conan build output (Qt6/QGIS4 migration) +build/ +CMakeUserPresets.json +*.log \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9d2405a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,239 @@ +cmake_minimum_required(VERSION 3.21) + +project(OpenSRA VERSION 1.0.0 LANGUAGES CXX C) + +# ---- C++20 (mirror R2D; SimCenterCommon helper modules compile as C++20) ---- +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if(MSVC) + # Force-include msvc_fix.h before any Windows header (byte/RPC clash + lean+mean/nominmax) + file(TO_NATIVE_PATH "${CMAKE_SOURCE_DIR}/msvc_fix.h" MSVC_FIX_PATH) + add_compile_options("/FI${MSVC_FIX_PATH}") + add_compile_definitions(WIN32_LEAN_AND_MEAN) + add_compile_definitions(NOMINMAX) +endif() + +# ---- QGIS-fork dependency roots (sibling layout) appended to CMAKE_PREFIX_PATH ---- +# QCA + Qt6Keychain + Qwt + QScintilla come from the SimCenter deps tree (QGIS/DEPS); +# gdal/geos/proj headers come from the QGIS fork's vcpkg tree. Appended (not prepended) +# so conan-provided packages keep priority. +set(PATH_TO_QGIS "${CMAKE_CURRENT_LIST_DIR}/../QGIS") +list(APPEND CMAKE_PREFIX_PATH + "${PATH_TO_QGIS}/DEPS" + "${PATH_TO_QGIS}/build/vcpkg_installed/x64-windows") + +# ---- Qt ---- +# Mirrors R2D's component set, minus SerialPort (OpenSRA's compiled sources do not use +# QtSerialPort; only the QGIS fork does, which is linked, not compiled, here). +find_package(Qt6 REQUIRED COMPONENTS + Core Gui Widgets + Charts Concurrent Network Sql Xml WebEngineWidgets + 3DCore 3DRender 3DExtras + OpenGL OpenGLWidgets Svg + Positioning QuickWidgets +) + +# Qt6Keychain resolves from the SimCenter deps tree (QGIS/DEPS/lib/cmake/Qt6Keychain), +# now on CMAKE_PREFIX_PATH. REQUIRED for the link (Session 4). +find_package(Qt6Keychain REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +# ---- Paths (single-".." sibling layout: OpenSRA_GUI/{OpenSRAFrontEnd,R2DTool,SimCenterCommon,QGIS}) ---- +set(PATH_TO_COMMON "${CMAKE_CURRENT_LIST_DIR}/../SimCenterCommon") +set(PATH_TO_R2D "${CMAKE_CURRENT_LIST_DIR}/../R2DTool") +# NOTE: no PATH_TO_QGIS_DEPS. SimCenterQGIS.cmake sources QGIS dependency headers from the +# QGIS fork's own build/vcpkg_installed tree, so OpenSRA needs no separate QGIS_DEPS repo. + +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +# ---- Conan packages (via conanfile.py -> CMakeDeps) ---- +find_package(jansson REQUIRED) +find_package(ZLIB REQUIRED) +find_package(CURL REQUIRED) +find_package(nlohmann_json CONFIG REQUIRED) + +# ---- QCA (Qt Cryptography Architecture) from the SimCenter deps tree ---- +# Uses the installed CMake config package QGIS/DEPS/lib/cmake/Qca-qt6 (imported target qca-qt6, +# which carries its QtCrypto include dir + Qt6::Core). REQUIRED for the link (Session 4). +find_package(Qca-qt6 CONFIG REQUIRED) +set(QCA_FOUND TRUE) +set(QCA_LIBRARY qca-qt6) # imported target +set(QCA_INCLUDE_DIR "${PATH_TO_QGIS}/DEPS/include/Qca-qt6/QtCrypto") + +# ---- Qwt + QScintilla from the SimCenter deps tree (mirror R2D's -D flags) ---- +set(QWT_INCLUDE_DIR "${PATH_TO_QGIS}/DEPS/include/qwt" CACHE PATH "Qwt include dir") +set(QWT_LIBRARY "${PATH_TO_QGIS}/DEPS/lib/qwt.lib" CACHE FILEPATH "Qwt import library") +set(QSCINTILLA_INCLUDE_DIR "${PATH_TO_QGIS}/DEPS/include" CACHE PATH "QScintilla include root (has Qsci/)") +set(QSCINTILLA_LIBRARY "${PATH_TO_QGIS}/DEPS/lib/qscintilla2_qt6.lib" CACHE FILEPATH "QScintilla import library") + +find_package(QCA QUIET) +if(QCA_FOUND AND Qt6Keychain_FOUND) + set(OPENSRA_HAVE_QGIS_DEPS TRUE) +else() + set(OPENSRA_HAVE_QGIS_DEPS FALSE) + message(WARNING + "OpenSRA: QCA and/or Qt6Keychain not found yet (QCA_FOUND=${QCA_FOUND}, " + "Qt6Keychain_FOUND=${Qt6Keychain_FOUND}). These come from the QGIS fork's vcpkg build. " + "Configure proceeds, but a full LINK requires them — add the QGIS vcpkg tree to " + "CMAKE_PREFIX_PATH (e.g. /QGIS/build/vcpkg_installed/x64-windows) once built.") +endif() + +# ---- Target ---- +set(TARGET_NAME ${PROJECT_NAME}) # "OpenSRA" + +set(SOURCES + main.cpp +) + +set(HEADERS + # populated by the helper modules below +) + +set(QRC_FILES + images.qrc + styles.qrc +) + +# ---- App icon ---- +set(APP_ICON "") +if(APPLE) + set(APP_ICON "${CMAKE_CURRENT_LIST_DIR}/icons/openSRA-icon.icns") + if(EXISTS "${APP_ICON}") + set_source_files_properties("${APP_ICON}" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + else() + message(WARNING "App icon not found: ${APP_ICON}") + set(APP_ICON "") + endif() +endif() + +# ---- Executable ---- +qt_add_executable(${TARGET_NAME} + WIN32 + MACOSX_BUNDLE + ${SOURCES} + ${HEADERS} + ${QRC_FILES} + ${APP_ICON} +) + +# ---- Defines (mirror OpenSRA.pro) ---- +target_compile_definitions(${TARGET_NAME} PRIVATE + APP_VERSION="${PROJECT_VERSION}" + OpenSRA + Q_GIS +) + +if(WIN32) + target_compile_definitions(${TARGET_NAME} PRIVATE CURL_STATICLIB) +endif() + +# ---- Shared modules + QGIS + app (mirror R2D's include/call order) ---- +# Common +include("${PATH_TO_COMMON}/Common/Common.cmake") +simcenter_add_common(${TARGET_NAME}) + +# RandomVariables +include("${PATH_TO_COMMON}/RandomVariables/RandomVariables.cmake") +simcenter_add_randomvariables(${TARGET_NAME}) + +# SimCenterQGIS (shared QGIS widgets + QGIS fork include paths) +include("${PATH_TO_COMMON}/QGIS/SimCenterQGIS.cmake") +simcenter_add_qgis(${TARGET_NAME}) + +# OpenSRA app sources (own + borrowed R2D subset) +include("${CMAKE_CURRENT_LIST_DIR}/OpenSRA.cmake") +simcenter_add_opensra(${TARGET_NAME}) + +# Common Workflow — OpenSRA-SELECTIVE set (NOT the full simcenter_add_workflow, which would +# double-compile OpenSRA's own LocalApplication/MainWindowWorkflowApp and pull in unused SIM +# building models -> duplicate-class/moc conflicts). Mirrors OpenSRACommon.pri's Workflow entries. +include("${CMAKE_CURRENT_LIST_DIR}/OpenSRAWorkflow.cmake") +simcenter_add_opensra_workflow(${TARGET_NAME}) + +# ---- Include dirs ---- +if(QCA_FOUND) + target_include_directories(${TARGET_NAME} PRIVATE ${QCA_INCLUDE_DIR}) +endif() +target_include_directories(${TARGET_NAME} PRIVATE + ${QSCINTILLA_INCLUDE_DIR} + ${QWT_INCLUDE_DIR} +) + +# minizip/ZipUtils (SimCenterCommon) must see conan's STATIC zlib.h (plain symbols), not the +# vcpkg SHARED zlib.h that SimCenterQGIS adds to the include path (which yields __imp_* dllimport +# refs unsatisfied by the linked static conan zlib). BEFORE puts conan's zlib include first. +get_target_property(_opensra_zlib_inc ZLIB::ZLIB INTERFACE_INCLUDE_DIRECTORIES) +if(_opensra_zlib_inc) + target_include_directories(${TARGET_NAME} BEFORE PRIVATE ${_opensra_zlib_inc}) +endif() +message(STATUS "OpenSRA: prepended conan zlib include = ${_opensra_zlib_inc}") + +# ---- Qt link libs ---- +target_link_libraries(${TARGET_NAME} PRIVATE + Qt6::Core Qt6::Gui Qt6::Widgets + Qt6::Charts Qt6::Concurrent Qt6::Network Qt6::Sql Qt6::Xml + Qt6::WebEngineWidgets + Qt6::3DCore Qt6::3DRender Qt6::3DExtras + Qt6::OpenGL Qt6::OpenGLWidgets Qt6::Svg + Qt6::Positioning Qt6::QuickWidgets +) + +# qtkeychain (from the QGIS vcpkg tree) — linked only when available; required for link. +if(Qt6Keychain_FOUND) + target_link_libraries(${TARGET_NAME} PRIVATE qt6keychain) +endif() + +# ---- QGIS libraries (PARAMETERIZED; USER sets QGIS_LIB_DIR in Session 4) ---- +# Empty default keeps the configure clean and skips QGIS linking until the fork is built. +set(QGIS_LIB_DIR "${PATH_TO_QGIS}/build/src" CACHE PATH "Path to built QGIS libs (Windows: /QGIS/build/src; macOS: .../Frameworks)") +set(QGIS_VERSION "4.1.0" CACHE STRING "QGIS library version") + +if(QGIS_LIB_DIR) + if(APPLE) + foreach(m app gui native analysis 3d core) + target_link_libraries(${TARGET_NAME} PRIVATE "${QGIS_LIB_DIR}/libqgis_${m}.${QGIS_VERSION}.dylib") + endforeach() + elseif(WIN32) + foreach(m app gui native core) + target_link_libraries(${TARGET_NAME} PRIVATE "${QGIS_LIB_DIR}/${m}/qgis_${m}.lib") + endforeach() + endif() +else() + message(STATUS "OpenSRA: QGIS_LIB_DIR not set -> skipping QGIS library linking (set it in Session 4).") +endif() + +# ---- External libs (prefer find_package results) ---- +target_link_libraries(${TARGET_NAME} PRIVATE + ${QWT_LIBRARY} + ${QSCINTILLA_LIBRARY} + CURL::libcurl + ZLIB::ZLIB + jansson::jansson + nlohmann_json::nlohmann_json +) + +# minizip/ZipUtils (SimCenterCommon) compiles against the vcpkg (shared) zlib headers on the +# QGIS include path and references __imp_* zlib symbols that conan's STATIC zlib doesn't provide. +# Satisfy them with the vcpkg zlib import lib (zlib1.dll is already in-process via the QGIS libs). +if(WIN32 AND EXISTS "${PATH_TO_QGIS}/build/vcpkg_installed/x64-windows/lib/zlib.lib") + target_link_libraries(${TARGET_NAME} PRIVATE "${PATH_TO_QGIS}/build/vcpkg_installed/x64-windows/lib/zlib.lib") +endif() + +# QCA (from the QGIS vcpkg tree) — linked only when available; required for link. +if(QCA_FOUND) + target_link_libraries(${TARGET_NAME} PRIVATE ${QCA_LIBRARY}) +endif() + +if(WIN32) + target_link_libraries(${TARGET_NAME} PRIVATE Advapi32) +elseif(APPLE) + target_link_libraries(${TARGET_NAME} PRIVATE lapack blas) +endif() + +# Qt 6.10 auto-finalizes qt_add_executable targets; an explicit qt_finalize_executable here +# would double-finalize. Left to automatic finalization. diff --git a/GeneralInformationWidget.cpp b/GeneralInformationWidget.cpp index 758f1a3..0b95537 100644 --- a/GeneralInformationWidget.cpp +++ b/GeneralInformationWidget.cpp @@ -62,7 +62,7 @@ GeneralInformationWidget::GeneralInformationWidget(QWidget *parent) : SimCenterA { QVBoxLayout *mainLayout = new QVBoxLayout(); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); mainLayout->setContentsMargins(5,0,0,0); @@ -113,8 +113,15 @@ bool GeneralInformationWidget::outputToJSON(QJsonObject &jsonObj) QDir workDir(workingDirectoryLineEdit->text()); directoryObj.insert("Working",workDir.absolutePath()); - directoryObj.insert("OpenSRAData",OpenSRAPreferences::getInstance()->getAppDataDir()); - directoryObj.insert("NDAData",OpenSRAPreferences::getInstance()->getNDADataDir()); + // OpenSRA datasets folder (Preferences); kept in a variable so NDAData can be compared to it. + auto openSRAData = OpenSRAPreferences::getInstance()->getAppDataDir(); + // Emit OpenSRAData unchanged (only refactored from the original one-liner to reuse the variable). + directoryObj.insert("OpenSRAData", openSRAData); + // NDA datasets folder (Preferences); this field defaults blank or to the OpenSRAData folder. + auto ndaData = OpenSRAPreferences::getInstance()->getNDADataDir(); + // Emit NDAData only when non-empty AND distinct, else the backend merges the dataset folder + // with itself and crashes; a genuine prepackaged-NDA folder is still preserved. + if(!ndaData.isEmpty() && ndaData != openSRAData) directoryObj.insert("NDAData", ndaData); outputObj.insert("AnalysisID",analysisLineEdit->text()); // outputObj.insert("UnitSystem",unitsCombo->currentText()); diff --git a/JsonWidgets/JsonDefinedWidget.cpp b/JsonWidgets/JsonDefinedWidget.cpp index d32e91a..2f38769 100644 --- a/JsonWidgets/JsonDefinedWidget.cpp +++ b/JsonWidgets/JsonDefinedWidget.cpp @@ -52,7 +52,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. JsonDefinedWidget::JsonDefinedWidget(QWidget* parent, const QJsonObject& obj, const QString parentKey) : JsonWidget(parent) { layout = new QVBoxLayout(this); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0,0,0,0); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -100,7 +100,7 @@ JsonDefinedWidget::JsonDefinedWidget(QWidget* parent, const QJsonObject& obj, co auto returnParam = returnObj.value("Params").toArray(); QVBoxLayout *returnParamLayout = new QVBoxLayout(); - returnParamLayout->setMargin(0); + returnParamLayout->setContentsMargins(0, 0, 0, 0); returnParamLayout->setSpacing(4); if (returnParam.size() > 0) diff --git a/JsonWidgets/JsonLabel.cpp b/JsonWidgets/JsonLabel.cpp index 7bc2022..d93a22c 100644 --- a/JsonWidgets/JsonLabel.cpp +++ b/JsonWidgets/JsonLabel.cpp @@ -38,7 +38,6 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. #include "JsonLabel.h" -#include #include JsonLabel::JsonLabel(QWidget* parent) : QLabel(parent) diff --git a/JsonWidgets/JsonLineEdit.cpp b/JsonWidgets/JsonLineEdit.cpp index 00a9962..8e8612b 100644 --- a/JsonWidgets/JsonLineEdit.cpp +++ b/JsonWidgets/JsonLineEdit.cpp @@ -37,7 +37,6 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. // Written by: Dr. Stevan Gavrilovic, UC Berkeley #include "JsonLineEdit.h" -#include #include JsonLineEdit::JsonLineEdit(QWidget* parent) : QLineEdit(parent) diff --git a/JsonWidgets/SimCenterJsonWidget.cpp b/JsonWidgets/SimCenterJsonWidget.cpp index 76790d1..71b8818 100644 --- a/JsonWidgets/SimCenterJsonWidget.cpp +++ b/JsonWidgets/SimCenterJsonWidget.cpp @@ -95,7 +95,7 @@ SimCenterJsonWidget::SimCenterJsonWidget(QString methodName, QJsonObject jsonObj QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0,0,0,0); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); auto mainWidget = this->getWidgetBox(jsonObj); @@ -152,7 +152,7 @@ QGroupBox* SimCenterJsonWidget::getWidgetBox(const QJsonObject jsonObj) if(nameToDisplay == "Landslide Induced Pipe Strain") { QVBoxLayout* inputLayout = new QVBoxLayout(); - inputLayout->setMargin(0); + inputLayout->setContentsMargins(0, 0, 0, 0); strainLandslideCheckBox = new QCheckBox("1) Consider strain preloading (checked = yes)?"); strainLandslideCheckBox->setChecked(false); @@ -164,10 +164,10 @@ QGroupBox* SimCenterJsonWidget::getWidgetBox(const QJsonObject jsonObj) if(nameToDisplay == "Fault Rupture Induced Pipe Strain") { QVBoxLayout* inputLayout = new QVBoxLayout(); - inputLayout->setMargin(0); + inputLayout->setContentsMargins(0, 0, 0, 0); QHBoxLayout* bufferLayout = new QHBoxLayout(); - bufferLayout->setMargin(0); + bufferLayout->setContentsMargins(0, 0, 0, 0); bufferCheckBox = new QCheckBox("1) Increase buffer on fault traces?"); bufferCheckBox->setChecked(false); auto bufferPrimaryLabel = new QLabel("Buffer for Primary Hazard (m):"); @@ -210,7 +210,7 @@ QGroupBox* SimCenterJsonWidget::getWidgetBox(const QJsonObject jsonObj) { QVBoxLayout* inputLayout = new QVBoxLayout(); - inputLayout->setMargin(0); + inputLayout->setContentsMargins(0, 0, 0, 0); // widget for additional landslide parameters for deformation polygons to use defPolyLineEdit = new QLineEdit(); diff --git a/LocalApplication.cpp b/LocalApplication.cpp index 2602727..e4f19f4 100644 --- a/LocalApplication.cpp +++ b/LocalApplication.cpp @@ -243,14 +243,30 @@ bool LocalApplication::setupDoneRunPreprocessing(QString &workingDir, QString &/ errorMessage("First time after installation of OpenSRA, this step will take an additional 1 minute (approx.) to complete background tasks..."); statusMessage(""); // switch back to status message format - procEnv.insert("PATH", python); + // Resolve the conda env root (the directory that contains python.exe). + QFileInfo pythonPath(python); + auto condaPrefix = pythonPath.absoluteDir().absolutePath(); + + // PATH must include the conda env's native-DLL dirs (Library\bin holds the GDAL/GEOS/ + // PROJ DLLs); setting it to just python.exe lets steps 1-9 run but crashes the backend + // at the spatial crossing step. Prepend the standard conda activation dirs, then the + // inherited system PATH so OS DLLs stay reachable. +#ifdef Q_OS_WIN + QStringList condaPathDirs = { + condaPrefix, + condaPrefix + "\\Library\\bin", + condaPrefix + "\\Library\\mingw-w64\\bin", + condaPrefix + "\\Library\\usr\\bin", + condaPrefix + "\\Scripts", + condaPrefix + "\\bin" + }; + procEnv.insert("PATH", condaPathDirs.join(";") + ";" + sysEnv.value("PATH")); +#else + procEnv.insert("PATH", condaPrefix + ":" + sysEnv.value("PATH")); +#endif procEnv.insert("PYTHONPATH", python); procEnv.insert("USERNAME", "opensra_user"); procEnv.insert("LOCALAPPDATA", QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); - - // other environment variables needed for OpenSRA - QFileInfo pythonPath(python); - auto condaPrefix = pythonPath.absoluteDir().absolutePath(); procEnv.insert("CONDA_PREFIX", condaPrefix); @@ -423,13 +439,28 @@ bool LocalApplication::setupDoneRunApplication(QString &tmpDirectory, QString &i errorMessage("First time after installation of OpenSRA, this step will take an additional 10 minutes (approx.) to complete background tasks..."); statusMessage(""); // switch back to status message format - procEnv.insert("PATH", python); - procEnv.insert("PYTHONPATH", python); - procEnv.insert("LOCALAPPDATA", QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); - - // other environment variables needed for OpenSRA + // Resolve the conda env root (the directory that contains python.exe). QFileInfo pythonPath(python); auto condaPrefix = pythonPath.absoluteDir().absolutePath(); + + // PATH must include the conda env's native-DLL dirs (Library\bin holds the GDAL/GEOS/ + // PROJ DLLs); setting it to just python.exe crashes the backend at the spatial crossing + // step. Prepend the standard conda activation dirs, then the inherited system PATH. +#ifdef Q_OS_WIN + QStringList condaPathDirs = { + condaPrefix, + condaPrefix + "\\Library\\bin", + condaPrefix + "\\Library\\mingw-w64\\bin", + condaPrefix + "\\Library\\usr\\bin", + condaPrefix + "\\Scripts", + condaPrefix + "\\bin" + }; + procEnv.insert("PATH", condaPathDirs.join(";") + ";" + sysEnv.value("PATH")); +#else + procEnv.insert("PATH", condaPrefix + ":" + sysEnv.value("PATH")); +#endif + procEnv.insert("PYTHONPATH", python); + procEnv.insert("LOCALAPPDATA", QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); procEnv.insert("CONDA_PREFIX", condaPrefix); QString projDataPath = condaPrefix + QDir::separator() + "Library" + QDir::separator() + "share" + QDir::separator() + "proj"; procEnv.insert("PROJ_DATA", projDataPath); @@ -521,7 +552,7 @@ int LocalApplication::handlePreprocessDone(int res) if(res ==0) emit preprocessingDone(); else - this->errorMessage("Error at the \"PREPROCESSING\" step with result "+QString(res)); + this->errorMessage("Error at the \"PREPROCESSING\" step with result "+QString::number(res)); return 0; } @@ -534,7 +565,7 @@ int LocalApplication::handleApplicationRunDone(int res) if(res ==0) emit processResults(QString(),QString(),QString()); else - this->errorMessage("Error at the \"PERFORM ANALYSIS\" step with result "+QString(res)); + this->errorMessage("Error at the \"PERFORM ANALYSIS\" step with result "+QString::number(res)); return 0; } diff --git a/MIGRATION_NOTES.md b/MIGRATION_NOTES.md new file mode 100644 index 0000000..7df7565 --- /dev/null +++ b/MIGRATION_NOTES.md @@ -0,0 +1,279 @@ +# OpenSRA Qt5→Qt6 / QGIS3→QGIS4 / QMake→CMake migration notes + +Living record of the migration. Records what changed, why, every divergence from the +CLAUDE.md §6 skeleton, and anything observed/needed **outside** `OpenSRAFrontEnd` (for +upstream reporting). Branch: `qt6-qgis4-migration` (off `csv-import-fix`). + +--- + +## Status by session + +| Session | Scope | State | +|---|---|---| +| 1 | Pre-flight | done (prereqs were initially missing; later satisfied by the user) | +| 2 | CMake authoring → clean **configure**; remove dead ArcGIS/plugin paths | **DONE — configure+generate exit 0** | +| 3 | Qt6 renames (§4); delete dead code | **DONE** (only non-QGIS code; full compile pending QGIS deps) | +| 4 | Link → full Release build | **DONE — clean build, `build/Release/OpenSRA.exe` produced** (see "Session 4" below) | + +--- + +## Actual workspace layout (differs from CLAUDE.md §2) + +Siblings live under `C:\dev\OpenSRA_GUI\`: +``` +OpenSRA_GUI/ +├── OpenSRAFrontEnd/ (this repo — target) +├── R2DTool/ (reference; FLAT layout, has CMakeLists.txt + R2D.cmake) +├── SimCenterCommon/ (shared; has QGIS/ + the *.cmake helper modules) +└── QGIS/ (SimCenter QGIS 4 fork; USER is building it) +``` +So under CMake the paths are **single `..`**: `PATH_TO_COMMON=../SimCenterCommon`, +`PATH_TO_R2D=../R2DTool`. (The old QMake `OpenSRA.pro` used `../../…` for the previous +nested layout — not carried over.) **There is no `QGIS_DEPS` repo** (confirmed with the user). + +--- + +## Files added (build system) + +- `CMakeLists.txt` — top-level, mirrors `R2DTool/CMakeLists.txt`. +- `OpenSRA.cmake` — `simcenter_add_opensra(target)`, mirrors `R2D.cmake`. Adds OpenSRA's own + sources (from `OpenSRA.pro`, minus `main.cpp`) **plus** the borrowed R2D source subset + (from `OpenSRACommon.pri`'s `$$PATH_TO_R2D` entries). The SimCenterCommon Workflow / + RandomVariables / Common sources that `OpenSRACommon.pri` + `Common.pri` used to pull are + **not** listed here — they are added by the helper modules (avoids double-listing). +- `conanfile.py` — Conan **v2** (CMakeDeps + CMakeToolchain), mirrors + `R2DTool/conanfile2.py`. Requires jansson, zlib, nlohmann_json, libcurl(non-Linux). +- `msvc_fix.h` — copied verbatim from R2D (`byte`/RPC clash fix + WIN32_LEAN_AND_MEAN/NOMINMAX), + force-included on MSVC via `/FI`. +- `cmake/FindQCA.cmake` — copied verbatim from R2D. +- `.gitignore` — added `build/` and `CMakeUserPresets.json`. + +## Files changed + +- `OpenSRA.pro` — removed the dead `PATH_TO_QGIS_PLUGIN=../../QGISPlugin` + its + `include($$PATH_TO_QGIS_PLUGIN/QGIS.pri)` (the missing Qt5 plugin). Kept as legacy reference + only; CMake is now authoritative. +- `main.cpp:46` — `endl` → `Qt::endl`. +- `UIWidgets/FixedResidualsSamplingWidget.cpp` — removed dead `#include ` + (only usages were already commented out). +- `JsonWidgets/JsonLabel.cpp`, `JsonWidgets/JsonLineEdit.cpp` — removed dead + `#include ` (no live usage in either file). + +## Files deleted (dead code) + +- `UIWidgets/OpenSRAPostProcessor_old.cpp` + `.h` — legacy postprocessor, not in the build; + held all the Qt5/QGIS3 hot-spots (QtCharts namespace, setPaperSize/get/setPageMargins old + forms, `mainCanvas->extent()`). Deleted per CLAUDE.md §6 (do not port). +- `arcgisruntime.pri` — orphaned ESRI ArcGIS Runtime locator; never `include()`d. Deleted. + +--- + +## Divergences from the CLAUDE.md §6 CMake skeleton (with reasons) + +1. **Conan v2, not v1.** R2D's active recipe is `conanfile2.py` (v2, CMakeDeps+CMakeToolchain); + the v1 `conanfile.py` (generators="qmake") is stale. OpenSRA uses a single v2 `conanfile.py`. +2. **No `QGIS_DEPS/include`.** `SimCenterCommon/QGIS/SimCenterQGIS.cmake` reads **no** caller + variables and sources all QGIS dependency headers from the fork's own + `…/QGIS/build/vcpkg_installed/x64-windows/include` (derived from its own location + `…/SimCenterCommon/QGIS/../../QGIS`). So the skeleton's `${PATH_TO_QGIS_DEPS}/include` was + dropped entirely. +3. **`SerialPort` dropped from Qt components.** Grep shows no `QSerialPort` use anywhere in + OpenSRA's own / SimCenterCommon / borrowed-R2D compiled sources (only inside the QGIS fork, + which is linked, not compiled). Per the brief's "only if used." Re-add only if Session-4 + linking against `qgis_gui` needs it. +4. **`WebEngineWidgets` kept.** `simcenter_add_workflow` adds `SimCenterCommon/Workflow/TOOLS/ + ShakerMaker.*` + `DRM_Model.*`, which use QtWebEngine — so it is transitively required. +5. **QCA + Qt6Keychain are optional at configure, required at link.** Both come from the QGIS + fork's vcpkg build, which is not done yet. `find_package(QCA QUIET)` / `find_package(Qt6Keychain + QUIET)` + an `OPENSRA_HAVE_QGIS_DEPS` guard let configure stay clean now; their include/link + usage is wrapped in `if(QCA_FOUND)` / `if(Qt6Keychain_FOUND)`. **This is a temporary staging + adaptation** — once the QGIS vcpkg tree exists, add it to `CMAKE_PREFIX_PATH` and they engage + automatically (and are enforced at link). +6. **`QGIS_LIB_DIR` parameterized + guarded.** Empty cache default; the link block is wrapped in + `if(QGIS_LIB_DIR)` so configure is clean before the fork is built. WIN32 links 4 modules + (`app gui native core`) from `${QGIS_LIB_DIR}//qgis_.lib`; APPLE links 6. No hard-coded + developer paths (R2D's CMakeLists hard-codes `C:/Users/fmcke/...` — intentionally **not** + mirrored). +7. **libcurl uses the `schannel` SSL backend on Windows** (`-o libcurl/*:with_ssl=schannel`) + instead of the default openssl. Avoids building openssl (which needs nasm + strawberryperl) + and sidesteps an SSL-cert failure reaching conancenter (see Environment issues). Deviation + from R2D's default; revisit if openssl is specifically required. +8. **No explicit `qt_finalize_executable`.** Qt 6.10 auto-finalizes `qt_add_executable`; the + explicit call double-finalized (warning). Removed. +9. **C++20** (mirrors R2D). Note: the Conan profile sets `compiler.cppstd=14`; CMakeLists + overrides to 20 (benign configure warning). Consider setting cppstd=20 in the Conan profile. +10. **`OpenSRAUserPass.h` added conditionally** (`if(EXISTS)`) — it is gitignored/sensitive and + absent on clean checkouts; mirrors R2D's `R2DUserPass.h` handling. +11. **Helper modules invoked:** `simcenter_add_common`, `simcenter_add_randomvariables`, + `simcenter_add_qgis`, `simcenter_add_opensra`, `simcenter_add_workflow` (R2D's order, minus + `simcenter_add_inputsheet` — OpenSRA's QMake never used InputSheetBM; add if a later compile + needs it). + +--- + +## Punch list A — Qt5 deprecated sites (final) + +All live sites resolved; remaining hits were in the now-deleted `_old` file. +| Site | Action | +|---|---| +| `main.cpp:46` bare `endl` | → `Qt::endl` ✓ | +| `FixedResidualsSamplingWidget.cpp:9` `#include ` | removed (dead) ✓ | +| `JsonLabel.cpp:41` `#include ` | removed (dead) ✓ | +| `JsonLineEdit.cpp:40` `#include ` | removed (dead) ✓ | +| `OpenSRAPostProcessor_old.cpp/.h` (QtCharts ns, setPaperSize, set/getPageMargins) | file deleted ✓ | + +No `QStringRef`, `QtCharts::`, `QPalette::Background`, `QFileInfo f = QFile(`, or `.indexIn(` +anywhere in the live tree. + +## Punch list B — QGIS-including files (for Session 5) + +13 files include QGIS headers; **all are "light"** (they pass geometry types as strings to the +shared `QGISVisualizationWidget` and delegate canvas/extent/layer-removal to it). No live file +uses `QgsWkbTypes::*Geometry`, direct canvas `setExtent`/`extent()`, or `removeMapLayer` — the +only file that did was `OpenSRAPostProcessor_old.cpp` (deleted). Several already use the new +`Qgis::` enums (`Qgis::SymbolType`, `Qgis::MarkerShape`, `Qgis::RasterResamplingStage`). So +Session 5 should be mostly header/include verification once the build links; the §5 runtime +fixes live in the already-ported `SimCenterCommon/QGIS/QGISVisualizationWidget`. + +Files: `OpenSRAPreProcessor.cpp`, `OpenSRAPostProcessor.cpp`, `CustomVisualizationWidget.cpp`, +`UserInputCPTWidget.cpp/.h`, `UserDefinedGroundMotionWidget.cpp/.h`, `BayAreaPipelineWidget.cpp`, +`LosAngelesPipelineWidget.cpp`, `NDABayAreaPipelineWidget.cpp`, `NDAStateWidePipelineWidget.cpp`, +`StateWidePipelineWidget.cpp` (+ trivial `main.cpp`, `WorkflowAppOpenSRA.cpp`). + +--- + +## Configure procedure (reproducible) + +``` +cd OpenSRA_GUI/OpenSRAFrontEnd +conan install . --output-folder=build --build=missing -nr -s build_type=Release -o "libcurl/*:with_ssl=schannel" +cmake -S . -B build -G "Visual Studio 17 2022" \ + -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake \ + -DCMAKE_POLICY_DEFAULT_CMP0091=NEW \ + -DCMAKE_PREFIX_PATH="C:/Qt/6.10.2/msvc2022_64" +``` +Result: `-- Configuring done` / `-- Generating done`, exit 0, `build/OpenSRA.sln` generated. +MSVC 19.44 (VS 2022 BuildTools) + Qt 6.10.2 (all components present) detected. Once the QGIS +vcpkg tree exists, append `;C:/dev/OpenSRA_GUI/QGIS/build/vcpkg_installed/x64-windows` to +`-DCMAKE_PREFIX_PATH` (and set `-DQGIS_LIB_DIR=...` in Session 4). + +--- + +## Changes / observations OUTSIDE OpenSRAFrontEnd (for upstream reporting) + +- (Sessions 1–3 were read-only across repos.) **Session 4 required minimal edits to R2DTool** + (borrowed source with Qt5 residue) — see the "Session 4" section below. SimCenterCommon and the + QGIS fork were **not** edited (the workflow-composition divergence was handled OpenSRA-side). +- `SimCenterCommon/QGIS/SimCenterQGIS.cmake` hard-assumes the QGIS fork is a sibling **two + levels up** (`SimCenterCommon/QGIS/../../QGIS`) and already **built** (references `build/…` + and `build/vcpkg_installed//include`). Holds for our layout. No fork header-export + patch needed so far. +- `R2DTool/CMakeLists.txt` hard-codes developer QGIS paths (`C:/Users/fmcke/...`, + `/Users/fmckenna/...`). OpenSRA parameterizes via `QGIS_LIB_DIR` instead — recommend the same + cleanup upstream in R2D. + +## Environment issues for the user to resolve + +- **Conan ↔ conancenter SSL failure:** `CERTIFICATE_VERIFY_FAILED` reaching + `center2.conan.io`. Worked around for now by building from local cache (`-nr`) + schannel + (no openssl). To restore full Conan remote access (needed if deps aren't cached), fix the CA + bundle, e.g. set `conan config` `core.net.http:cacert_path` or env `SSL_CERT_FILE` / + `REQUESTS_CA_BUNDLE` to the corporate/system CA. This will also matter for the QGIS vcpkg build. +- **QCA + Qt6Keychain** come from the QGIS fork's vcpkg build (not done yet). Configure is clean + without them; LINK (Session 4) needs them on `CMAKE_PREFIX_PATH`. + +--- + +## Session 4 (link) — COMPLETE → `build/Release/OpenSRA.exe` (clean build, 0 errors) + +QGIS fork now built (`../QGIS/build/src` + `../QGIS/DEPS` + `../QGIS/build/vcpkg_installed`). Drove +the compile+link from 186 errors to a clean build. Generator: **VS 17 2022**, conan toolchain, **Release**. + +**CMakeLists.txt — DEPS/QGIS wiring (this repo):** +- Appended `../QGIS/DEPS` and `../QGIS/build/vcpkg_installed/x64-windows` to `CMAKE_PREFIX_PATH`. +- QCA via `find_package(Qca-qt6 CONFIG REQUIRED)` (imported target `qca-qt6`); Qt6Keychain via its config + (target `qt6keychain`); Qwt/QScintilla pointed at `DEPS/lib` + `DEPS/include`; `QGIS_LIB_DIR=../QGIS/build/src`. + (Supersedes the §6/divergence-#5 "optional QCA/Qt6Keychain" staging — they are now REQUIRED and resolve from DEPS.) + +**CMake composition — replaced `simcenter_add_workflow`:** OpenSRA ships its OWN `LocalApplication`, +`MainWindowWorkflowApp`, `GeneralInformationWidget`; the full `simcenter_add_workflow` also compiled +SimCenterCommon's same-named classes + SIM building models → duplicate-class/AUTOMOC conflicts. Added +**`OpenSRAFrontEnd/OpenSRAWorkflow.cmake`** (`simcenter_add_opensra_workflow`) with only the 7 Workflow +sources `OpenSRACommon.pri` used (incl. `AnimatedStackedWidget.h` for AUTOMOC). SimCenterCommon untouched. + +**OpenSRA-side Qt6/QGIS4 source fixes:** `setMargin`→`setContentsMargins` (48 sites); `QString::SkipEmptyParts` +→`Qt::SkipEmptyParts`; `QgsRasterDataProvider::ResamplingMethod::Bilinear`→`Qgis::RasterResamplingMethod::Bilinear`; +`QString(int)`→`QString::number`; ``/`QApplication::desktop()->screenGeometry()`→ +`QGuiApplication::primaryScreen()->geometry()`; `QJsonValue["x"]`→`.toObject().value("x")`; `jsonKeyword` +(private in R2D `MultiComponentR2D`) → inline the per-subclass literal; `insertWidgetIntoLayout` commented +(matches siblings); `IntensityMeasureWidget.cpp` += `#include "QGISVisualizationWidget.h"`; +**`OpenSRAPreferences.h` include-guard renamed** `SIMCENTER_PREFERENCES_H`→`OPENSRA_PREFERENCES_H` (it collided +with SimCenterCommon's `SimCenterPreferences.h` guard, blanking that class in the AUTOMOC aggregation → 24 errors). + +**R2DTool — minimal Qt6 edits (borrowed source; user-approved):** +- `UIWidgets/AssetInputWidget.cpp` — `setMargin`→`setContentsMargins`; `hideCRS_Selection()`/`hideAssetFilePath()` + used `QGridLayout` API (`columnCount`/`itemAtPosition`) on a `QVBoxLayout` → rewritten to `itemAt(index)`. +- `UIWidgets/{GISGasNetworkInputWidget,GISAboveGroundGasComponentInputWidget,GISWellsCaprocksInputWidget}.cpp` — `setMargin`→`setContentsMargins`. +- `ModelViewItems/MutuallyExclusiveListWidget.cpp` — `QString::SkipEmptyParts`→`Qt::SkipEmptyParts`. + +**SimCenterCommon — runtime-crash fix (shared source; user-approved, upstream-reportable):** +- `QGIS/SimCenterMapcanvasWidget.cpp` (~line 68) — disabled the two connections that wire the embedded + `legendTreeView`'s `selectionModel` to `QgisApp::updateNewLayerInsertionPoint` (`currentChanged`) and + `QgisApp::legendLayerSelectionChanged` (`selectionChanged`). `legendTreeView` is a *second* `QgsLayerTreeView` + sharing QgisApp's model; on layer removal it emits those signals for the just-deleted (dangling) node, and + QgisApp's slots then read QgisApp's *own* layer-tree view against that node → `0xC0000005`. QgisApp's own view + already drives these with correct timing and the embedded viz is read-only, so the connections are unneeded. + **Fixes the ~24s startup crash** (verified: 60 s under cdb, no fatal exception — see Session 7 below). Likely + benefits R2D too; candidate for upstream. + +**zlib link:** minizip/ZipUtils (SimCenterCommon) compiles against the vcpkg (shared) zlib headers on the QGIS +include path and references `__imp_*` zlib symbols that conan's STATIC zlib doesn't provide. Linked the vcpkg +zlib import lib (`../QGIS/build/vcpkg_installed/x64-windows/lib/zlib.lib`); its `zlib1.dll` is already in-process +via QGIS. (The earlier `BEFORE`-prepend of conan's zlib include is redundant — conan marks its includes +`/external:I`, searched after the regular `/I`; harmless, can be removed in cleanup.) + +**Result:** `cmake --build build --config Release` → 0 errors → `build/Release/OpenSRA.exe` (2.1 MB). +**Not yet done:** running the app (needs the runtime DLLs on PATH: Qt6 `bin`, `../QGIS/build/output/bin`, +`../QGIS/build/vcpkg_installed/x64-windows/bin`, `../QGIS/DEPS/bin`) + QGIS providers/resources — Session 6/7. + +--- + +## Session 7 (runtime / packaging) — bundle manifest + a runtime crash + +**Launch:** OpenSRA.exe LAUNCHES (main window "OpenSRA" shows). It originally **crashed ~24s later, on its own**, +with `0xC0000005` in `qgis_gui.dll` (`QgisApp::updateNewLayerInsertionPoint`). **RESOLVED** by the +`SimCenterMapcanvasWidget.cpp` connection fix above — verified by running under cdb (`sxd av; g`) for 60 s with +**no fatal exception** (app loaded the platform plugin / SQLite / GPU stack and kept running; only benign +`QObject::connect(... invalid nullptr ...)` warnings). The original-crash details below are kept for the record. +Reproduces with PATH or self-contained +bundle, with or without qt.conf → it is a runtime bug, not a packaging gap. + +**Runtime DLL manifest (static closure = 110 non-system DLLs), by source:** +- **Qt (35)** — `C:\Qt\6.10.2\msvc2022_64\bin`, deployed via `windeployqt`. Incl. Qt6Core5Compat (QCA needs it). +- **QGIS + vcpkg deps (71)** — `../QGIS/build/output/bin` and `../QGIS/build/output` (root). qgis_core/gui/app/3d/analysis/native, + gdal, geos/geos_c, proj_9, spatialite, sqlite3, zlib1, **libcrypto-3-x64 + libssl-3-x64** (qca-ossl runtime), arrow/parquet, poppler, etc. + (`vcpkg_installed/x64-windows/bin` is redundant — output already has them.) +- **DEPS (3)** — `../QGIS/DEPS/bin`: qca-qt6, qscintilla2_qt6, qt6keychain. +- **qwt.dll (1)** — `../QGIS/DEPS/**lib**` (GOTCHA: Qwt installs its DLL to `lib/`, not `bin/`). Sole reason a 4-folder PATH gave `0xC0000135`. + +**Qt plugins:** `windeployqt` under-deploys for a QGIS host (it only follows the EXE's own Qt deps). The **full** +`C:\Qt\6.10.2\msvc2022_64\plugins` tree is required — most importantly **`sqldrivers\qsqlite.dll`** (QGIS opens srs.db/gpkg via QSQLITE); +without it `qgis_gui` crashes in init. Bundle = copy the whole `plugins` tree next to the exe. + +**QGIS providers + resources:** the SimCenter QGIS code calls `QgsApplication::initQgis()` but never `setPrefixPath`, so on THIS +machine QGIS uses its compiled-in prefix `../QGIS/build/output` → providers in `output/plugins`, resources in `output/data` +(`data/resources/qgis.db`, `data/svg`). For a PORTABLE bundle: copy `output/plugins` + `output/data` next to the exe and set +`QGIS_PREFIX_PATH` (or add `QgsApplication::setPrefixPath(applicationDirPath(), true)`). NOTE: `srs.db` is at `../QGIS/build/resources/srs.db`, +not under `output/data/resources` — verify QGIS finds the CRS db at runtime. + +**QCA ossl plugin (runtime auth, not needed for the gpkg visual test):** `../QGIS/DEPS/lib/qca-qt6/crypto/qca-ossl.dll` (+ libcrypto/libssl, already in the DLL set). + +**Bundle assembled into `OpenSRAFrontEnd/build/Release`:** windeployqt (Qt + plugins) + the 110-DLL closure + full Qt plugin tree. +A clean-PATH (self-contained) launch loads all modules — but hits the same ~24s crash below. + +**RUNTIME CRASH (blocker, Session 5/6):** ~24s after launch, AV `0xC0000005`, faulting `qgis_gui.dll`. cdb stack +(nearest-export symbols; no QGIS PDBs): +`QgsLayerTreeRegistryBridge::layersWillBeRemoved → QgsLayerTreeGroup::removeChildNode → QgsLayerTreeNode::removeChildrenPrivate +→ QMetaObject::activate → QgisApp::updateNewLayerInsertionPoint → AV`. I.e. a layer-removal signal drives the embedded +`QgisApp`'s new-layer-insertion-point slot into an invalid deref. Diagnose vs R2D's QgisApp/layer-tree setup; a RelWithDebInfo +QGIS (PDBs) would give exact lines. diff --git a/MainWindowWorkflowApp.cpp b/MainWindowWorkflowApp.cpp index 856a743..7b22d54 100644 --- a/MainWindowWorkflowApp.cpp +++ b/MainWindowWorkflowApp.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -472,7 +471,7 @@ void MainWindowWorkflowApp::about() // // adjust size of application window to the available display // - QRect rec = QApplication::desktop()->screenGeometry(); + QRect rec = QGuiApplication::primaryScreen()->geometry(); int height = 0.50*rec.height(); int width = 0.50*rec.width(); dlg->resize(width, height); diff --git a/OpenSRA.cmake b/OpenSRA.cmake new file mode 100644 index 0000000..979fa03 --- /dev/null +++ b/OpenSRA.cmake @@ -0,0 +1,260 @@ +# OpenSRA.cmake +# Usage: +# include(path/to/OpenSRA.cmake) +# simcenter_add_opensra() +# +# Mirrors R2DTool/R2D.cmake. Adds OpenSRA's own sources/headers (from OpenSRA.pro, minus +# main.cpp which is listed at top level) plus the subset of R2DTool sources OpenSRA borrows +# (from OpenSRACommon.pri's $$PATH_TO_R2D entries). +# +# NOTE: the SimCenterCommon Workflow + RandomVariables + Common sources that OpenSRACommon.pri +# and Common.pri used to pull in are intentionally NOT listed here. They are supplied by the +# helper modules invoked from CMakeLists.txt (simcenter_add_workflow / simcenter_add_randomvariables +# / simcenter_add_common). Listing them here too would double-add the same source files. + +set(OPENSRA_MODULE_DIR "${CMAKE_CURRENT_LIST_DIR}") + +function(simcenter_add_opensra target) + + set(dir "${OPENSRA_MODULE_DIR}") + set(r2d "${dir}/../R2DTool") + + target_include_directories(${target} PRIVATE + # OpenSRA's own dirs (OpenSRA.pro INCLUDEPATH) + "${dir}" + "${dir}/Utils" + "${dir}/styles" + "${dir}/UIWidgets" + "${dir}/JsonWidgets" + "${dir}/ModelViewItems" + # Borrowed R2DTool dirs (OpenSRACommon.pri INCLUDEPATH) + "${r2d}" + "${r2d}/UIWidgets" + "${r2d}/Tools" + "${r2d}/Events" + "${r2d}/Events/UI" + "${r2d}/GraphicElements" + "${r2d}/ModelViewItems" + "${r2d}/systemPerformanceWidgets" + ) + + set(SOURCES + # ---- OpenSRA's own sources (OpenSRA.pro, minus main.cpp) ---- + "${dir}/JsonWidgets/JsonDefinedWidget.cpp" + "${dir}/JsonWidgets/JsonGroupBoxWidget.cpp" + "${dir}/JsonWidgets/JsonComboBox.cpp" + "${dir}/JsonWidgets/JsonLineEdit.cpp" + "${dir}/JsonWidgets/JsonLabel.cpp" + "${dir}/JsonWidgets/JsonCheckBox.cpp" + "${dir}/JsonWidgets/JsonWidget.cpp" + "${dir}/JsonWidgets/JsonStackedWidget.cpp" + "${dir}/JsonWidgets/SimCenterJsonWidget.cpp" + "${dir}/ModelViewItems/ComboBoxDelegate.cpp" + "${dir}/ModelViewItems/ButtonDelegate.cpp" + "${dir}/ModelViewItems/RV.cpp" + "${dir}/ModelViewItems/RVTableView.cpp" + "${dir}/ModelViewItems/RVTableModel.cpp" + "${dir}/ModelViewItems/MixedDelegate.cpp" + "${dir}/ModelViewItems/LineEditDelegate.cpp" + "${dir}/ModelViewItems/LabelDelegate.cpp" + "${dir}/ModelViewItems/StringListDelegate.cpp" + "${dir}/UIWidgets/AddToRunListWidget.cpp" + "${dir}/UIWidgets/ClickableLabel.cpp" + "${dir}/UIWidgets/EDPLandslideWidget.cpp" + "${dir}/UIWidgets/MonteCarloSamplingWidget.cpp" + "${dir}/UIWidgets/FixedResidualsSamplingWidget.cpp" + "${dir}/UIWidgets/OpenSHAWidget.cpp" + "${dir}/UIWidgets/OpenSRAComponentSelection.cpp" + "${dir}/UIWidgets/OpenSRAPostProcessor.cpp" + "${dir}/UIWidgets/OpenSRAPreProcessor.cpp" + "${dir}/UIWidgets/UncertaintyQuantificationWidget.cpp" + "${dir}/UIWidgets/WidgetFactory.cpp" + "${dir}/UIWidgets/DecisionVariableWidget.cpp" + "${dir}/UIWidgets/SourceCharacterizationWidget.cpp" + "${dir}/UIWidgets/CustomVisualizationWidget.cpp" + "${dir}/UIWidgets/PipelineNetworkWidget.cpp" + "${dir}/UIWidgets/EngDemandParamWidget.cpp" + "${dir}/UIWidgets/EngineeringDemandParameterWidget.cpp" + "${dir}/UIWidgets/MultiComponentEDPWidget.cpp" + "${dir}/UIWidgets/MultiComponentDMWidget.cpp" + "${dir}/UIWidgets/MultiComponentDVWidget.cpp" + "${dir}/UIWidgets/IntensityMeasureWidget.cpp" + "${dir}/UIWidgets/DamageMeasureWidget.cpp" + "${dir}/UIWidgets/RandomVariablesWidget.cpp" + "${dir}/UIWidgets/UserInputCPTWidget.cpp" + "${dir}/UIWidgets/GeospatialDataWidget.cpp" + "${dir}/UIWidgets/GenericModelWidget.cpp" + "${dir}/UIWidgets/StateWidePipelineWidget.cpp" + "${dir}/UIWidgets/BayAreaPipelineWidget.cpp" + "${dir}/UIWidgets/LosAngelesPipelineWidget.cpp" + "${dir}/UIWidgets/NDAStateWidePipelineWidget.cpp" + "${dir}/UIWidgets/NDABayAreaPipelineWidget.cpp" + "${dir}/UIWidgets/UserDefinedGroundMotionWidget.cpp" + "${dir}/Utils/EventFilter.cpp" + "${dir}/ResultsWidget.cpp" + "${dir}/WorkflowAppOpenSRA.cpp" + "${dir}/WorkflowAppWidget.cpp" + "${dir}/MainWindowWorkflowApp.cpp" + "${dir}/LocalApplication.cpp" + "${dir}/OpenSRAPreferences.cpp" + "${dir}/GeneralInformationWidget.cpp" + "${dir}/RunWidget.cpp" + + # ---- Borrowed R2DTool sources (OpenSRACommon.pri, $$PATH_TO_R2D) ---- + "${r2d}/Tools/CSVReaderWriter.cpp" + "${r2d}/Tools/XMLAdaptor.cpp" + "${r2d}/Tools/AssetInputDelegate.cpp" + "${r2d}/Tools/AssetFilterDelegate.cpp" + "${r2d}/Tools/ComponentDatabase.cpp" + "${r2d}/Tools/ComponentDatabaseManager.cpp" + "${r2d}/Tools/TablePrinter.cpp" + "${r2d}/Tools/SimCenterUnitsCombo.cpp" + "${r2d}/Events/UI/SiteConfig.cpp" + "${r2d}/Events/UI/site.cpp" + "${r2d}/Events/UI/SiteGrid.cpp" + "${r2d}/Events/UI/GridDivision.cpp" + "${r2d}/Events/UI/Location.cpp" + "${r2d}/Events/UI/SiteScatter.cpp" + "${r2d}/UIWidgets/LoadResultsDialog.cpp" + "${r2d}/UIWidgets/MultiComponentR2D.cpp" + "${r2d}/GraphicElements/GridNode.cpp" + "${r2d}/GraphicElements/NodeHandle.cpp" + "${r2d}/GraphicElements/RectangleGrid.cpp" + "${r2d}/UIWidgets/LineAssetInputWidget.cpp" + "${r2d}/UIWidgets/PointAssetInputWidget.cpp" + "${r2d}/UIWidgets/CSVWellsCaprocksInputWidget.cpp" + "${r2d}/UIWidgets/GISWellsCaprocksInputWidget.cpp" + "${r2d}/UIWidgets/CSVAboveGroundGasComponentInputWidget.cpp" + "${r2d}/UIWidgets/GISAboveGroundGasComponentInputWidget.cpp" + "${r2d}/UIWidgets/AssetInputWidget.cpp" + "${r2d}/UIWidgets/CRSSelectionWidget.cpp" + "${r2d}/UIWidgets/ShakeMapWidget.cpp" + "${r2d}/UIWidgets/GISAssetInputWidget.cpp" + "${r2d}/UIWidgets/GISMapWidget.cpp" + "${r2d}/UIWidgets/GISGasNetworkInputWidget.cpp" + "${r2d}/UIWidgets/GroundMotionStation.cpp" + "${r2d}/UIWidgets/GroundMotionTimeHistory.cpp" + "${r2d}/UIWidgets/SimCenterUnitsWidget.cpp" + "${r2d}/UIWidgets/UserInputFaultWidget.cpp" + "${r2d}/ModelViewItems/CustomListWidget.cpp" + "${r2d}/ModelViewItems/MutuallyExclusiveListWidget.cpp" + "${r2d}/ModelViewItems/ComponentTableModel.cpp" + "${r2d}/ModelViewItems/ComponentTableView.cpp" + "${r2d}/ModelViewItems/ListTreeModel.cpp" + ) + + set(HEADERS + # ---- OpenSRA's own headers (OpenSRA.pro) ---- + "${dir}/JsonWidgets/JsonDefinedWidget.h" + "${dir}/JsonWidgets/JsonComboBox.h" + "${dir}/JsonWidgets/JsonLineEdit.h" + "${dir}/JsonWidgets/JsonLabel.h" + "${dir}/JsonWidgets/JsonCheckBox.h" + "${dir}/JsonWidgets/JsonWidget.h" + "${dir}/JsonWidgets/JsonStackedWidget.h" + "${dir}/JsonWidgets/SimCenterJsonWidget.h" + "${dir}/JsonWidgets/JsonGroupBoxWidget.h" + "${dir}/ModelViewItems/ComboBoxDelegate.h" + "${dir}/ModelViewItems/ButtonDelegate.h" + "${dir}/ModelViewItems/RV.h" + "${dir}/UIWidgets/AddToRunListWidget.h" + "${dir}/UIWidgets/ClickableLabel.h" + "${dir}/UIWidgets/EDPLandslideWidget.h" + "${dir}/UIWidgets/MonteCarloSamplingWidget.h" + "${dir}/UIWidgets/FixedResidualsSamplingWidget.h" + "${dir}/UIWidgets/OpenSHAWidget.h" + "${dir}/UIWidgets/OpenSRAComponentSelection.h" + "${dir}/UIWidgets/OpenSRAPostProcessor.h" + "${dir}/UIWidgets/OpenSRAPreProcessor.h" + "${dir}/ResultsWidget.h" + "${dir}/UIWidgets/UncertaintyQuantificationWidget.h" + "${dir}/UIWidgets/WidgetFactory.h" + "${dir}/UIWidgets/RandomVariablesWidget.h" + "${dir}/UIWidgets/UserInputCPTWidget.h" + "${dir}/UIWidgets/GeospatialDataWidget.h" + "${dir}/UIWidgets/GenericModelWidget.h" + "${dir}/UIWidgets/StateWidePipelineWidget.h" + "${dir}/UIWidgets/BayAreaPipelineWidget.h" + "${dir}/UIWidgets/LosAngelesPipelineWidget.h" + "${dir}/UIWidgets/NDAStateWidePipelineWidget.h" + "${dir}/UIWidgets/NDABayAreaPipelineWidget.h" + "${dir}/Utils/EventFilter.h" + "${dir}/WorkflowAppOpenSRA.h" + "${dir}/WorkflowAppWidget.h" + "${dir}/MainWindowWorkflowApp.h" + "${dir}/LocalApplication.h" + "${dir}/OpenSRAPreferences.h" + "${dir}/RunWidget.h" + "${dir}/UIWidgets/DecisionVariableWidget.h" + "${dir}/UIWidgets/SourceCharacterizationWidget.h" + "${dir}/UIWidgets/CustomVisualizationWidget.h" + "${dir}/UIWidgets/PipelineNetworkWidget.h" + "${dir}/UIWidgets/EngDemandParamWidget.h" + "${dir}/UIWidgets/EngineeringDemandParameterWidget.h" + "${dir}/UIWidgets/MultiComponentEDPWidget.h" + "${dir}/UIWidgets/MultiComponentDMWidget.h" + "${dir}/UIWidgets/MultiComponentDVWidget.h" + "${dir}/GeneralInformationWidget.h" + "${dir}/UIWidgets/IntensityMeasureWidget.h" + "${dir}/UIWidgets/DamageMeasureWidget.h" + "${dir}/UIWidgets/UserDefinedGroundMotionWidget.h" + "${dir}/ModelViewItems/RVTableView.h" + "${dir}/ModelViewItems/RVTableModel.h" + "${dir}/ModelViewItems/MixedDelegate.h" + "${dir}/ModelViewItems/LineEditDelegate.h" + "${dir}/ModelViewItems/LabelDelegate.h" + "${dir}/ModelViewItems/StringListDelegate.h" + + # ---- Borrowed R2DTool headers (OpenSRACommon.pri, $$PATH_TO_R2D) ---- + "${r2d}/Tools/XMLAdaptor.h" + "${r2d}/Tools/CSVReaderWriter.h" + "${r2d}/Tools/AssetInputDelegate.h" + "${r2d}/Tools/AssetFilterDelegate.h" + "${r2d}/Tools/ComponentDatabase.h" + "${r2d}/Tools/TablePrinter.h" + "${r2d}/Tools/SimCenterUnitsCombo.h" + "${r2d}/Tools/ComponentDatabaseManager.h" + "${r2d}/Events/UI/SiteConfig.h" + "${r2d}/Events/UI/site.h" + "${r2d}/Events/UI/SiteGrid.h" + "${r2d}/Events/UI/GridDivision.h" + "${r2d}/Events/UI/Location.h" + "${r2d}/Events/UI/SiteScatter.h" + "${r2d}/Events/UI/JsonSerializable.h" + "${r2d}/UIWidgets/LoadResultsDialog.h" + "${r2d}/UIWidgets/MultiComponentR2D.h" + "${r2d}/GraphicElements/GridNode.h" + "${r2d}/GraphicElements/NodeHandle.h" + "${r2d}/GraphicElements/RectangleGrid.h" + "${r2d}/UIWidgets/LineAssetInputWidget.h" + "${r2d}/UIWidgets/PointAssetInputWidget.h" + "${r2d}/UIWidgets/CSVWellsCaprocksInputWidget.h" + "${r2d}/UIWidgets/GISWellsCaprocksInputWidget.h" + "${r2d}/UIWidgets/CSVAboveGroundGasComponentInputWidget.h" + "${r2d}/UIWidgets/GISAboveGroundGasComponentInputWidget.h" + "${r2d}/UIWidgets/AssetInputWidget.h" + "${r2d}/UIWidgets/CRSSelectionWidget.h" + "${r2d}/UIWidgets/ShakeMapWidget.h" + "${r2d}/UIWidgets/GISAssetInputWidget.h" + "${r2d}/UIWidgets/GISMapWidget.h" + "${r2d}/UIWidgets/GISGasNetworkInputWidget.h" + "${r2d}/UIWidgets/GroundMotionStation.h" + "${r2d}/UIWidgets/GroundMotionTimeHistory.h" + "${r2d}/UIWidgets/SimCenterUnitsWidget.h" + "${r2d}/UIWidgets/UserInputFaultWidget.h" + "${r2d}/ModelViewItems/CustomListWidget.h" + "${r2d}/ModelViewItems/MutuallyExclusiveListWidget.h" + "${r2d}/ModelViewItems/ComponentTableModel.h" + "${r2d}/ModelViewItems/ComponentTableView.h" + "${r2d}/ModelViewItems/ListTreeModel.h" + ) + + target_sources(${target} PRIVATE ${SOURCES} ${HEADERS}) + + # OpenSRAUserPass.h holds credentials and is gitignored (absent on clean checkouts). + # Add it only when present, mirroring R2D's R2DUserPass.h handling. + if(EXISTS "${dir}/OpenSRAUserPass.h") + target_sources(${target} PRIVATE "${dir}/OpenSRAUserPass.h") + endif() + +endfunction() diff --git a/OpenSRA.pro b/OpenSRA.pro index 767d31a..6139e6e 100644 --- a/OpenSRA.pro +++ b/OpenSRA.pro @@ -45,14 +45,15 @@ win32 { # GIS plugin DEFINES += Q_GIS -PATH_TO_QGIS_PLUGIN=../../R2DTool/qgisplugin -include($$PATH_TO_QGIS_PLUGIN/QGIS.pri) +# NOTE (Qt6/QGIS4 migration): the old prebuilt Qt5 QGIS plugin path was removed. +# The CMake build now provides QGIS via SimCenterCommon/QGIS/SimCenterQGIS.cmake + the +# from-source QGIS 4 fork. This legacy .pro is kept for reference only; build with CMake. # Specify the path to R2D and common PATH_TO_R2D=../../R2DTool/R2DTool PATH_TO_COMMON=../../SimCenterCommon -PATH_TO_BACKEND=../../OpenSRA +PATH_TO_BACKEND=../../OpenSRABackend #PATH_TO_BACKEND=../OpenSRA_dev # To avoid code copying, include the common SimCenter code @@ -191,7 +192,9 @@ INCLUDEPATH += $$PWD/Utils \ $$PWD/UIWidgets \ $$PWD/JsonWidgets \ $$PWD/ModelViewItems \ + "C:/Program Files (x86)/GnuWin32/include" +LIBS += -L"C:/Program Files (x86)/GnuWin32/lib" -lz # Copies over the examples folder into the build directory #win32 { @@ -227,9 +230,9 @@ INCLUDEPATH += $$PWD/Utils \ #QMAKE_EXTRA_TARGETS += first copyExamples copyBackEnd -#win32:CONFIG(release, debug|release): LIBS += -L$$PWD/'../../../../../../Program Files (x86)/GnuWin32/lib/' -lzlib -#else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/'../../../../../../Program Files (x86)/GnuWin32/lib/' -lzlib -#else:unix: LIBS += -L$$PWD/'../../../../../../Program Files (x86)/GnuWin32/lib/' -lzlib +win32:CONFIG(release, debug|release): LIBS += -L$$PWD/'../../../../../../Program Files (x86)/GnuWin32/lib/' -lzlib +else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/'../../../../../../Program Files (x86)/GnuWin32/lib/' -lzlib +else:unix: LIBS += -L$$PWD/'../../../../../../Program Files (x86)/GnuWin32/lib/' -lzlib -#INCLUDEPATH += $$PWD/'../../../../../../Program Files (x86)/GnuWin32/include' -#DEPENDPATH += $$PWD/'../../../../../../Program Files (x86)/GnuWin32/include' +INCLUDEPATH += $$PWD/'../../../../../../Program Files (x86)/GnuWin32/include' +DEPENDPATH += $$PWD/'../../../../../../Program Files (x86)/GnuWin32/include' diff --git a/OpenSRACommon.pri b/OpenSRACommon.pri index 76b2150..2802f55 100644 --- a/OpenSRACommon.pri +++ b/OpenSRACommon.pri @@ -13,6 +13,8 @@ INCLUDEPATH += $$PATH_TO_R2D \ $$PATH_TO_COMMON/Workflow/GRAPHICS \ $$PATH_TO_COMMON/Workflow/WORKFLOW/ModelViewItems \ $$PATH_TO_COMMON/RandomVariables \ + $$PATH_TO_R2D/systemPerformanceWidgets \ + $$PATH_TO_COMMON/Workflow/WORKFLOW/Utils \ SOURCES += $$PATH_TO_R2D/Tools/CSVReaderWriter.cpp \ diff --git a/OpenSRAPreferences.h b/OpenSRAPreferences.h index 6cb2bbd..a4f3396 100644 --- a/OpenSRAPreferences.h +++ b/OpenSRAPreferences.h @@ -1,5 +1,5 @@ -#ifndef SIMCENTER_PREFERENCES_H -#define SIMCENTER_PREFERENCES_H +#ifndef OPENSRA_PREFERENCES_H +#define OPENSRA_PREFERENCES_H /* ***************************************************************************** Copyright (c) 2016-2017, The Regents of the University of California (Regents). diff --git a/OpenSRAWorkflow.cmake b/OpenSRAWorkflow.cmake new file mode 100644 index 0000000..4ed4873 --- /dev/null +++ b/OpenSRAWorkflow.cmake @@ -0,0 +1,54 @@ +# OpenSRAWorkflow.cmake +# OpenSRA-specific, SELECTIVE replacement for SimCenterCommon's simcenter_add_workflow(). +# +# Why this exists (do NOT just call simcenter_add_workflow): +# OpenSRA ships its OWN LocalApplication, MainWindowWorkflowApp and GeneralInformationWidget, +# and does NOT do building-damage analysis. SimCenterCommon's simcenter_add_workflow compiles +# EXECUTION/LocalApplication.cpp, WORKFLOW/MainWindowWorkflowApp.cpp and the SIM/*BuildingModel +# sources, which (a) DUPLICATE OpenSRA's own same-named classes (shared include guards -> +# AUTOMOC mocs the wrong header) and (b) drag in building-damage widgets that need InputSheetBM's +# GeneralInformationWidget singleton. That is the root of ~100 of the first-pass build errors. +# +# This module adds ONLY the SimCenterCommon/Workflow sources the original QMake build used +# (OpenSRACommon.pri: the $$PATH_TO_COMMON/Workflow entries). Mirror OpenSRACommon.pri, not R2D. +# +# Usage: +# include(path/to/OpenSRAWorkflow.cmake) +# simcenter_add_opensra_workflow() + +set(OPENSRA_WF_DIR "${CMAKE_CURRENT_LIST_DIR}/../SimCenterCommon/Workflow") + +function(simcenter_add_opensra_workflow target) + set(wf "${OPENSRA_WF_DIR}") + + # Include paths (OpenSRACommon.pri INCLUDEPATH $$PATH_TO_COMMON/Workflow*) + target_include_directories(${target} PRIVATE + "${wf}" + "${wf}/WORKFLOW" + "${wf}/EXECUTION" + "${wf}/GRAPHICS" + "${wf}/WORKFLOW/ModelViewItems" + "${wf}/WORKFLOW/Utils" + ) + + # The exact 7 Workflow sources OpenSRA.pro/OpenSRACommon.pri compiled (+ their headers for AUTOMOC). + # EXECUTION/Application is the base class OpenSRA's own LocalApplication derives from. + target_sources(${target} PRIVATE + "${wf}/WORKFLOW/SimCenterComponentSelection.cpp" + "${wf}/WORKFLOW/CustomizedItemModel.cpp" + "${wf}/EXECUTION/Application.cpp" + "${wf}/GRAPHICS/SimCenterGraphPlot.cpp" + "${wf}/GRAPHICS/qcustomplot.cpp" + "${wf}/WORKFLOW/ModelViewItems/TreeItem.cpp" + "${wf}/WORKFLOW/ModelViewItems/CheckableTreeModel.cpp" + + "${wf}/WORKFLOW/SimCenterComponentSelection.h" + "${wf}/WORKFLOW/AnimatedStackedWidget.h" + "${wf}/WORKFLOW/CustomizedItemModel.h" + "${wf}/EXECUTION/Application.h" + "${wf}/GRAPHICS/SimCenterGraphPlot.h" + "${wf}/GRAPHICS/qcustomplot.h" + "${wf}/WORKFLOW/ModelViewItems/TreeItem.h" + "${wf}/WORKFLOW/ModelViewItems/CheckableTreeModel.h" + ) +endfunction() diff --git a/ResultsWidget.cpp b/ResultsWidget.cpp index 686e75a..bfbce17 100644 --- a/ResultsWidget.cpp +++ b/ResultsWidget.cpp @@ -69,7 +69,7 @@ ResultsWidget::ResultsWidget(QWidget *parent, QGISVisualizationWidget* visWidget mainStackedWidget = new QStackedWidget(this); mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setContentsMargins(5,0,0,0); // Header layout and objects diff --git a/UIWidgets/AddToRunListWidget.cpp b/UIWidgets/AddToRunListWidget.cpp index 5de62a3..c6bcf19 100644 --- a/UIWidgets/AddToRunListWidget.cpp +++ b/UIWidgets/AddToRunListWidget.cpp @@ -60,7 +60,7 @@ AddToRunListWidget::AddToRunListWidget(QWidget* parent) : QWidget(parent) "Combining multiple methods may not be necessarily yield a model that is (log)normally distributed. Please limit to using just one method." ); warningLabel->setWordWrap(true); - warningLayout->setMargin(0); + warningLayout->setContentsMargins(0, 0, 0, 0); warningLayout->addWidget(warningLabel); auto weightLabel = new QLabel("Model Weight:"); @@ -90,7 +90,7 @@ AddToRunListWidget::AddToRunListWidget(QWidget* parent) : QWidget(parent) QHBoxLayout* inputLayout = new QHBoxLayout(); - inputLayout->setMargin(0); + inputLayout->setContentsMargins(0, 0, 0, 0); inputLayout->addWidget(weightLabel); inputLayout->addWidget(weightLineEdit); @@ -109,7 +109,7 @@ AddToRunListWidget::AddToRunListWidget(QWidget* parent) : QWidget(parent) connect(addRunListButton,&QPushButton::clicked, this, [=](){emit addToRunListButtonPressed();}); QVBoxLayout* mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addItem(smallVSpacer); diff --git a/UIWidgets/CustomVisualizationWidget.cpp b/UIWidgets/CustomVisualizationWidget.cpp index 80e11b1..33355ba 100644 --- a/UIWidgets/CustomVisualizationWidget.cpp +++ b/UIWidgets/CustomVisualizationWidget.cpp @@ -106,7 +106,7 @@ CustomVisualizationWidget::CustomVisualizationWidget(QGISVisualizationWidget* vi theLeftHandWidget->setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Expanding); QVBoxLayout *theLeftHandLayout = new QVBoxLayout(theLeftHandWidget); - theLeftHandLayout->setMargin(0); + theLeftHandLayout->setContentsMargins(0, 0, 0, 0); theLeftHandLayout->addWidget(visSelectBox); @@ -135,7 +135,7 @@ CustomVisualizationWidget::CustomVisualizationWidget(QGISVisualizationWidget* vi auto buttonHandle = new QToolButton(handleLeft); QVBoxLayout *layout = new QVBoxLayout(handleLeft); layout->setSpacing(0); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); theVizLayout->setHandleWidth(15); @@ -167,7 +167,7 @@ CustomVisualizationWidget::CustomVisualizationWidget(QGISVisualizationWidget* vi auto buttonHandleRight = new QToolButton(handleRight); QVBoxLayout *layoutRight = new QVBoxLayout(handleRight); layoutRight->setSpacing(0); - layoutRight->setMargin(0); + layoutRight->setContentsMargins(0, 0, 0, 0); buttonHandleRight->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); buttonHandleRight->setDown(false); diff --git a/UIWidgets/DamageMeasureWidget.cpp b/UIWidgets/DamageMeasureWidget.cpp index ce2d0fb..ec21903 100644 --- a/UIWidgets/DamageMeasureWidget.cpp +++ b/UIWidgets/DamageMeasureWidget.cpp @@ -59,7 +59,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. DamageMeasureWidget::DamageMeasureWidget(QJsonObject mainObj, QWidget *parent): SimCenterAppWidget(parent) { QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); mainLayout->setContentsMargins(5,0,0,0); diff --git a/UIWidgets/DecisionVariableWidget.cpp b/UIWidgets/DecisionVariableWidget.cpp index f0802bd..ea02f08 100644 --- a/UIWidgets/DecisionVariableWidget.cpp +++ b/UIWidgets/DecisionVariableWidget.cpp @@ -59,7 +59,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. DecisionVariableWidget::DecisionVariableWidget(QJsonObject mainObj, QWidget *parent): SimCenterAppWidget(parent) { QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); mainLayout->setContentsMargins(5,0,0,0); diff --git a/UIWidgets/EDPLandslideWidget.cpp b/UIWidgets/EDPLandslideWidget.cpp index 53bd539..37dd304 100644 --- a/UIWidgets/EDPLandslideWidget.cpp +++ b/UIWidgets/EDPLandslideWidget.cpp @@ -73,14 +73,14 @@ EDPLandslideWidget::EDPLandslideWidget(QJsonObject obj, QWidget* parent) : SimCe connect(listWidget,&QAbstractItemView::clicked,this,&EDPLandslideWidget::handleListItemSelected); QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setContentsMargins(0,0,0,0); QWidget* mainWidget = new QWidget(); mainWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); QVBoxLayout* inputLayout = new QVBoxLayout(mainWidget); - inputLayout->setMargin(0); + inputLayout->setContentsMargins(0, 0, 0, 0); inputLayout->setContentsMargins(5,0,0,0); auto boxWidget = this->getWidgetBox(obj); @@ -213,14 +213,14 @@ QWidget* EDPLandslideWidget::getWidgetBox(QJsonObject& obj) QWidget* yieldAccWidget = new QWidget(); QHBoxLayout* yieldAccLayout = new QHBoxLayout(yieldAccWidget); - yieldAccLayout->setMargin(0); + yieldAccLayout->setContentsMargins(0, 0, 0, 0); yieldAccLayout->addWidget(yieldAccMethodWidget); yieldAccLayout->addWidget(yieldAccParametersWidget); yieldAccLayout->setStretch(0,1); yieldAccLayout->setStretch(1,1); QVBoxLayout* inputLayout = new QVBoxLayout(); - inputLayout->setMargin(0); + inputLayout->setContentsMargins(0, 0, 0, 0); inputLayout->addWidget(methodWidget); inputLayout->addWidget(yieldAccWidget); // inputLayout->setStretch(0,1); @@ -398,7 +398,7 @@ JsonWidget* EDPLandslideWidget::getYieldMethodWidget(const QJsonObject& obj) kyWidget->setObjectName(methodKyStr); QVBoxLayout* kyWidgetLayout = new QVBoxLayout(yieldMethodWidget); - kyWidgetLayout->setMargin(0); + kyWidgetLayout->setContentsMargins(0, 0, 0, 0); kyWidgetLayout->addWidget(widgetLabel); kyWidgetLayout->addWidget(kyWidget); diff --git a/UIWidgets/EngDemandParamWidget.cpp b/UIWidgets/EngDemandParamWidget.cpp index 5748a3c..57011a2 100644 --- a/UIWidgets/EngDemandParamWidget.cpp +++ b/UIWidgets/EngDemandParamWidget.cpp @@ -54,7 +54,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. EngDemandParamWidget::EngDemandParamWidget(QJsonObject mainObj, QWidget *parent) : SimCenterAppWidget(parent) { QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); mainLayout->setContentsMargins(5,0,0,0); diff --git a/UIWidgets/EngineeringDemandParameterWidget.cpp b/UIWidgets/EngineeringDemandParameterWidget.cpp index ccd7f95..1146243 100644 --- a/UIWidgets/EngineeringDemandParameterWidget.cpp +++ b/UIWidgets/EngineeringDemandParameterWidget.cpp @@ -61,7 +61,7 @@ EngineeringDemandParameterWidget::EngineeringDemandParameterWidget(QJsonObject m this->setObjectName("EngineeringDemandParameter"); QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); mainLayout->setContentsMargins(5,0,0,0); diff --git a/UIWidgets/FixedResidualsSamplingWidget.cpp b/UIWidgets/FixedResidualsSamplingWidget.cpp index 3f77d48..5818314 100644 --- a/UIWidgets/FixedResidualsSamplingWidget.cpp +++ b/UIWidgets/FixedResidualsSamplingWidget.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include diff --git a/UIWidgets/GenericModelWidget.cpp b/UIWidgets/GenericModelWidget.cpp index be72400..8ca43fc 100644 --- a/UIWidgets/GenericModelWidget.cpp +++ b/UIWidgets/GenericModelWidget.cpp @@ -65,7 +65,7 @@ GenericModelWidget::GenericModelWidget(QString parName, QJsonObject &methodObj, { this->setObjectName("Generic Model Widget of "+parName); verticalLayout = new QVBoxLayout(this); - verticalLayout->setMargin(2); + verticalLayout->setContentsMargins(2, 2, 2, 2); verticalLayout->setSpacing(2); this->makeRVWidget(methodObj); @@ -193,7 +193,7 @@ void GenericModelWidget::makeRVWidget(QJsonObject &methodObj) // model definition table // title & add button QHBoxLayout *titleLayout = new QHBoxLayout(); - //titleLayout->setMargin(10); + //titleLayout->setContentsMargins(10, 10, 10, 10); SectionTitle *title=new SectionTitle(); title->setText(tr("Generic Model Definition")); diff --git a/UIWidgets/IntensityMeasureWidget.cpp b/UIWidgets/IntensityMeasureWidget.cpp index 737bc9f..7e42c5a 100644 --- a/UIWidgets/IntensityMeasureWidget.cpp +++ b/UIWidgets/IntensityMeasureWidget.cpp @@ -37,6 +37,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. // Written by: Stevan Gavrilovic #include "IntensityMeasureWidget.h" +#include "QGISVisualizationWidget.h" #include "sectiontitle.h" #include "OpenSHAWidget.h" #include "ShakeMapWidget.h" @@ -84,12 +85,12 @@ IntensityMeasureWidget::IntensityMeasureWidget(QGISVisualizationWidget* visWidge QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(0); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setContentsMargins(5,0,0,0); QHBoxLayout *theHeaderLayout = new QHBoxLayout(); theHeaderLayout->setContentsMargins(0,0,0,0); - theHeaderLayout->setMargin(0); + theHeaderLayout->setContentsMargins(0, 0, 0, 0); theHeaderLayout->setSpacing(0); SectionTitle *label = new SectionTitle(); label->setText(QString("Intensity Measure (IM)")); @@ -116,7 +117,7 @@ IntensityMeasureWidget::IntensityMeasureWidget(QGISVisualizationWidget* visWidge mainPanel = new QStackedWidget(); mainPanel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); mainPanel->setContentsMargins(5,0,0,0); - mainPanel->layout()->setMargin(0); + mainPanel->layout()->setContentsMargins(0, 0, 0, 0); mainPanel->layout()->setSpacing(0); mainPanel->addWidget(openSHA); diff --git a/UIWidgets/LosAngelesPipelineWidget.cpp b/UIWidgets/LosAngelesPipelineWidget.cpp index 61b6989..1eaefd9 100644 --- a/UIWidgets/LosAngelesPipelineWidget.cpp +++ b/UIWidgets/LosAngelesPipelineWidget.cpp @@ -123,7 +123,7 @@ void LosAngelesPipelineWidget::createComponentsBox(void) // QHBoxLayout* regionSitesLayout = new QHBoxLayout(regionLoadWidget); // Insert the widget three rows from the bottom - insertWidgetIntoLayout(regionLoadWidget,3); + //insertWidgetIntoLayout(regionLoadWidget,3); // helper removed in migration; matches sibling pipeline widgets } diff --git a/UIWidgets/MultiComponentDMWidget.cpp b/UIWidgets/MultiComponentDMWidget.cpp index 4fa5278..58bd514 100644 --- a/UIWidgets/MultiComponentDMWidget.cpp +++ b/UIWidgets/MultiComponentDMWidget.cpp @@ -59,7 +59,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. MultiComponentDMWidget::MultiComponentDMWidget(QWidget *parent) : MultiComponentR2D("DamageMeasure",parent) { - theMainLayout->setMargin(0); + theMainLayout->setContentsMargins(0, 0, 0, 0); theMainLayout->setSpacing(0); theMainLayout->setContentsMargins(5,0,0,0); @@ -159,7 +159,7 @@ bool MultiComponentDMWidget::outputAppDataToJSON(QJsonObject &jsonObject) bool MultiComponentDMWidget::inputAppDataFromJSON(QJsonObject &jsonObject) { - auto keyJsonObj = jsonObject[jsonKeyword].toObject(); + auto keyJsonObj = jsonObject[QStringLiteral("DamageMeasure")].toObject(); return this->inputFromJSON(keyJsonObj); } diff --git a/UIWidgets/MultiComponentDVWidget.cpp b/UIWidgets/MultiComponentDVWidget.cpp index 54297aa..0739e7f 100644 --- a/UIWidgets/MultiComponentDVWidget.cpp +++ b/UIWidgets/MultiComponentDVWidget.cpp @@ -58,7 +58,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. MultiComponentDVWidget::MultiComponentDVWidget(QWidget *parent) : MultiComponentR2D("DecisionVariable",parent) { - theMainLayout->setMargin(0); + theMainLayout->setContentsMargins(0, 0, 0, 0); theMainLayout->setSpacing(0); theMainLayout->setContentsMargins(5,0,0,0); @@ -158,7 +158,7 @@ bool MultiComponentDVWidget::outputAppDataToJSON(QJsonObject &jsonObject) bool MultiComponentDVWidget::inputAppDataFromJSON(QJsonObject &jsonObject) { - auto keyJsonObj = jsonObject[jsonKeyword].toObject(); + auto keyJsonObj = jsonObject[QStringLiteral("DecisionVariable")].toObject(); return this->inputFromJSON(keyJsonObj); } diff --git a/UIWidgets/MultiComponentEDPWidget.cpp b/UIWidgets/MultiComponentEDPWidget.cpp index 7072fdf..12e065c 100644 --- a/UIWidgets/MultiComponentEDPWidget.cpp +++ b/UIWidgets/MultiComponentEDPWidget.cpp @@ -59,7 +59,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. MultiComponentEDPWidget::MultiComponentEDPWidget(QWidget *parent) : MultiComponentR2D("EngineeringDemandParameter",parent) { - theMainLayout->setMargin(0); + theMainLayout->setContentsMargins(0, 0, 0, 0); theMainLayout->setSpacing(0); theMainLayout->setContentsMargins(5,0,0,0); @@ -159,7 +159,7 @@ bool MultiComponentEDPWidget::outputAppDataToJSON(QJsonObject &jsonObject) bool MultiComponentEDPWidget::inputAppDataFromJSON(QJsonObject &jsonObject) { - auto keyJsonObj = jsonObject[jsonKeyword].toObject(); + auto keyJsonObj = jsonObject[QStringLiteral("EngineeringDemandParameter")].toObject(); return this->inputFromJSON(keyJsonObj); } diff --git a/UIWidgets/OpenSHAWidget.cpp b/UIWidgets/OpenSHAWidget.cpp index 5de74c8..7aaed75 100644 --- a/UIWidgets/OpenSHAWidget.cpp +++ b/UIWidgets/OpenSHAWidget.cpp @@ -11,7 +11,7 @@ OpenSHAWidget::OpenSHAWidget(QWidget* parent) : SimCenterAppWidget(parent) auto layout = new QVBoxLayout(this); layout->setSpacing(0); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); auto mWidget = this->getMainWidget(); diff --git a/UIWidgets/OpenSRAPostProcessor.cpp b/UIWidgets/OpenSRAPostProcessor.cpp index 7a7ba2b..c559b34 100644 --- a/UIWidgets/OpenSRAPostProcessor.cpp +++ b/UIWidgets/OpenSRAPostProcessor.cpp @@ -88,7 +88,7 @@ OpenSRAPostProcessor::OpenSRAPostProcessor(QWidget *parent, QGISVisualizationWid listWidget->header()->resizeSections(QHeaderView::ResizeToContents); QVBoxLayout* mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(listWidget); diff --git a/UIWidgets/OpenSRAPostProcessor_old.cpp b/UIWidgets/OpenSRAPostProcessor_old.cpp deleted file mode 100644 index 889f1d4..0000000 --- a/UIWidgets/OpenSRAPostProcessor_old.cpp +++ /dev/null @@ -1,1133 +0,0 @@ -/* ***************************************************************************** -Copyright (c) 2016-2021, The Regents of the University of California (Regents). -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of the FreeBSD Project. - -REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS -PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, -UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - -*************************************************************************** */ - -// Written by: Stevan Gavrilovic - -#include "CSVReaderWriter.h" -#include "AssetInputWidget.h" -#include "GeneralInformationWidget.h" -#include "MainWindowWorkflowApp.h" -#include "OpenSRAPostProcessor.h" -#include "REmpiricalProbabilityDistribution.h" -#include "TablePrinter.h" -#include "TreeItem.h" -#include "QGISVisualizationWidget.h" -#include "ComponentDatabaseManager.h" -#include "WorkflowAppOpenSRA.h" -#include "EmbeddedMapViewWidget.h" -#include "MutuallyExclusiveListWidget.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 - -// GIS headers -#include -#include -#include -#include -#include - -using namespace QtCharts; - -OpenSRAPostProcessor::OpenSRAPostProcessor(QWidget *parent, QGISVisualizationWidget* visWidget) : SimCenterAppWidget(parent), theVisualizationWidget(visWidget) -{ - PGVTreeItem = nullptr; - PGDTreeItem = nullptr; - totalTreeItem = nullptr; - defaultItem = nullptr; - thePipelineDb = nullptr; - // The first n columns is information about the component and not results - numInfoCols = 8; - - QVBoxLayout* mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); - mainLayout->setContentsMargins(0, 0, 0, 0); - - // Create a view menu for the dockable windows - mainWidget = new QSplitter(); - mainWidget->setOrientation(Qt::Horizontal); - mainWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); - - listWidget = new MutuallyExclusiveListWidget(this, "Results"); - - connect(listWidget, &MutuallyExclusiveListWidget::itemChecked, this, &OpenSRAPostProcessor::handleListSelection); - connect(listWidget, &MutuallyExclusiveListWidget::clearAll, this, &OpenSRAPostProcessor::clearAll); - connect(theVisualizationWidget,&VisualizationWidget::emitScreenshot,this,&OpenSRAPostProcessor::assemblePDF); - - listWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); - - // Create the table that will show the Component information - tableWidget = new QWidget(this); - - PGVResultsTableWidget = new QTableWidget(this); - PGVResultsTableWidget->verticalHeader()->setVisible(false); - PGVResultsTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - PGVResultsTableWidget->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents); - PGVResultsTableWidget->setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum); - PGVResultsTableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - PGVResultsTableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - PGVResultsTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); - PGVResultsTableWidget->setVisible(false); - - PGDResultsTableWidget = new QTableWidget(this); - PGDResultsTableWidget->verticalHeader()->setVisible(false); - PGDResultsTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - PGDResultsTableWidget->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents); - PGDResultsTableWidget->setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum); - PGDResultsTableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - PGDResultsTableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - PGDResultsTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); - PGDResultsTableWidget->setVisible(false); - - // Get the map view widget - auto mapView = theVisualizationWidget->getMapViewWidget("ResultsWidget"); - mapViewSubWidget = std::unique_ptr(mapView); - - // Enable the selection tool - mapViewSubWidget->enableSelectionTool(); - - // Popup stuff - // Once map is set, connect to MapQuickView mouse clicked signal - // connect(mapViewSubWidget.get(), &MapViewSubWidget::mouseClick, theVisualizationWidget, &VisualizationWidget::onMouseClickedGlobal); - - mainWidget->addWidget(mapViewSubWidget.get()); - - QWidget* rightHandWidget = new QWidget(); - QVBoxLayout* rightHandLayout = new QVBoxLayout(rightHandWidget); - rightHandLayout->setMargin(0); - rightHandLayout->setContentsMargins(0, 0, 0, 0); - - rightHandLayout->addWidget(listWidget); - - QPushButton* modifyLegendButton = new QPushButton("Modify Legend",this); - connect(modifyLegendButton, &QPushButton::clicked ,this, &OpenSRAPostProcessor::handleModifyLegend); - - rightHandLayout->addWidget(modifyLegendButton); - - // auto legView = theVisualizationWidget->getLegendView(); - // if(legView != nullptr) - // { - // QLabel* legLabel = new QLabel("Legend",this); - // rightHandLayout->addWidget(legLabel); - // rightHandLayout->addWidget(legView); - // } - - mainWidget->addWidget(rightHandWidget); - - mainLayout->addWidget(mainWidget); - - // The number of header rows in the Pelicun results file - numHeaderRows = 1; - - mainWidget->setStretchFactor(0,2); - - // Now add the splitter handle - // Note: index 0 handle is always hidden, index 1 is between the two widgets - QSplitterHandle *handle = mainWidget->handle(1); - - if(handle == nullptr) - { - qDebug()<<"Error getting the handle"; - return; - } - - auto buttonHandle = new QToolButton(handle); - QVBoxLayout *layout = new QVBoxLayout(handle); - layout->setSpacing(0); - layout->setMargin(0); - - mainWidget->setHandleWidth(15); - - buttonHandle->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); - buttonHandle->setDown(false); - buttonHandle->setAutoRaise(false); - buttonHandle->setCheckable(false); - buttonHandle->setArrowType(Qt::LeftArrow); - buttonHandle->setStyleSheet("QToolButton{border:0px solid}; QToolButton:pressed {border:0px solid}"); - buttonHandle->setIconSize(buttonHandle->size()); - layout->addWidget(buttonHandle); - -} - - -void OpenSRAPostProcessor::handleModifyLegend(void) -{ - -// auto pipelineInputWidget = theVisualizationWidget->getComponentWidget("GASPIPELINES"); - -// if(pipelineInputWidget == nullptr) -// return; - -// auto pipelineLayer = pipelineInputWidget->getSelectedFeatureLayer(); - -// if(pipelineLayer == nullptr) -// return; - -// auto layerId = pipelineLayer->layerId(); - -// theVisualizationWidget->handlePlotColorChange(layerId); -} - - -void OpenSRAPostProcessor::showEvent(QShowEvent *e) -{ - auto mainCanvas = mapViewSubWidget->getMainCanvas(); - - auto mainExtent = mainCanvas->extent(); - - mapViewSubWidget->mapCanvas()->zoomToFeatureExtent(mainExtent); - QWidget::showEvent(e); -} - - -int OpenSRAPostProcessor::importResultVisuals(const QString& pathToResults) -{ - - QDir resultsDir(pathToResults); - - // Get the existing files in the folder - QStringList acceptableFileExtensions = {"*.csv"}; - QStringList existingCSVFiles = resultsDir.entryList(acceptableFileExtensions, QDir::Files); - - if(existingCSVFiles.empty()) - { - errorMessage("The results folder is empty. Did you include DV's in the analysis?"); - return -1; - } - - for(auto&& it : existingCSVFiles) - { - QString pathToFile = pathToResults+ QDir::separator() + it; - - if(it.startsWith("ScenarioTraces")) - { - this->importScenarioTraces(pathToFile); - } - else if(it.startsWith("FaultCrossings")) - { - this->importFaultCrossings(pathToFile); - } - } - - return 0; -} - - -int OpenSRAPostProcessor::importScenarioTraces(const QString& pathToFile) -{ - - if(pathToFile.isEmpty()) - return 0; - - CSVReaderWriter csvTool; - - QString errMsg; - - auto traces = csvTool.parseCSVFile(pathToFile, errMsg); - if(!errMsg.isEmpty()) - { - errorMessage(errMsg); - return -1; - } - - if(traces.size() < 2) - { - statusMessage("No fault traces available."); - return 0; - } - - QgsFields featFields; - featFields.append(QgsField("AssetType", QVariant::String)); - featFields.append(QgsField("TabName", QVariant::String)); - featFields.append(QgsField("SourceIndex", QVariant::String)); - - // Create the pipelines layer - auto mainLayer = theVisualizationWidget->addVectorLayer("linestring","Scenario Faults"); - - if(mainLayer == nullptr) - { - this->errorMessage("Error adding a vector layer"); - return -1; - } - - QList attribFields; - for(int i = 0; idataProvider(); - - mainLayer->startEditing(); - - auto res = pr->addAttributes(attribFields); - - if(!res) - this->errorMessage("Error adding attributes to the layer" + mainLayer->name()); - - mainLayer->updateFields(); // tell the vector layer to fetch changes from the provider - - auto headers = traces.front(); - - auto indexListofTraces = headers.indexOf("ListOfTraces"); - - traces.pop_front(); - - auto numAtrb = attribFields.size(); - - for(auto&& it : traces) - { - auto coordString = it.at(indexListofTraces); - - auto geometry = theVisualizationWidget->getMultilineStringGeometryFromJson(coordString); - - if(geometry.isEmpty()) - { - QString msg ="Error getting the feature geometry for scenario faults layer"; - this->errorMessage(msg); - - return -1; - } - - // create the feature attributes - QgsAttributes featureAttributes(numAtrb); - - featureAttributes[0] = QVariant("SCENARIO_TRACES"); - featureAttributes[1] = QVariant("Scenario Traces"); - featureAttributes[2] = QVariant(it.at(0)); - - - QgsFeature feature; - feature.setFields(featFields); - - feature.setGeometry(geometry); - - feature.setAttributes(featureAttributes); - - if(!feature.isValid()) - return -1; - - auto res = pr->addFeature(feature, QgsFeatureSink::FastInsert); - if(!res) - { - this->errorMessage("Error adding the feature to the layer"); - return -1; - } - } - - mainLayer->commitChanges(true); - mainLayer->updateExtents(); - - QgsLineSymbol* markerSymbol = new QgsLineSymbol(); - - QColor featureColor = QColor(0,0,0,200); - auto weight = 1.0; - - markerSymbol->setWidth(weight); - markerSymbol->setColor(featureColor); - theVisualizationWidget->createSimpleRenderer(markerSymbol,mainLayer); - - - return 0; -} - -int OpenSRAPostProcessor::importFaultCrossings(const QString& pathToFile) -{ - - return 0; -} - - - -void OpenSRAPostProcessor::importResults(const QString& pathToResults) -{ - qDebug() << "OpenSRAPostProcessor: " << pathToResults; - - QString errMsg; - - - // Get the pipelines database - thePipelineDb = ComponentDatabaseManager::getInstance()->getAssetDb("GasNetworkPipelines"); - - if(thePipelineDb == nullptr) - { - errMsg = "Could not get the pipeline database"; - throw errMsg; - } - - QString pathToDv = pathToResults + QDir::separator() + "DV"; - - try { - this->importDVresults(pathToDv); - } - catch (QString err) { - errorMessage(err); - } - - - QString pathToScenarioTraces = pathToResults + QDir::separator() + "IM" + QDir::separator() + "SeismicSource"; - this->importResultVisuals(pathToScenarioTraces); - - - listWidget->expandAll(); -} - - -int OpenSRAPostProcessor::importDVresults(const QString& pathToResults) -{ - - QDir resultsDir(pathToResults); - - QString errMsg; - - // Get the existing files in the folder - QStringList acceptableFileExtensions = {"*.csv"}; - QStringList existingCSVFiles = resultsDir.entryList(acceptableFileExtensions, QDir::Files); - - if(existingCSVFiles.empty()) - { - errMsg = "The results folder is empty. Did you include DV's in the analysis?"; - throw errMsg; - } - - QString PGDResultsSheet; - QString PGVResultsSheet; - QString AllResultsSheet; - - for(auto&& it : existingCSVFiles) - { - if(it.startsWith("RepairRatePGD")) - PGDResultsSheet = it; - else if(it.startsWith("RepairRatePGV")) - PGVResultsSheet = it; - else if(it.startsWith("AllResults")) - AllResultsSheet = it; - } - - // Create the CSV reader/writer tool - CSVReaderWriter csvTool; - - // Vector to hold the attributes - QVector< QgsAttributes > fieldAttributes; - QStringList fieldNames; - - if(!PGVResultsSheet.isEmpty()) - { - RepairRatePGV = csvTool.parseCSVFile(pathToResults + QDir::separator() + PGVResultsSheet,errMsg); - if(!errMsg.isEmpty()) - throw errMsg; - - if(!RepairRatePGV.empty()) - this->processPGVResults(RepairRatePGV,fieldNames,fieldAttributes); - else - { - errMsg = "The PGV results are empty"; - throw errMsg; - } - } - - if(!PGDResultsSheet.isEmpty()) - { - RepairRatePGD = csvTool.parseCSVFile(pathToResults + QDir::separator() + PGDResultsSheet,errMsg); - if(!errMsg.isEmpty()) - throw errMsg; - - if(!RepairRatePGD.empty()) - this->processPGDResults(RepairRatePGD,fieldNames,fieldAttributes); - else - { - errMsg = "The PGD results are empty"; - throw errMsg; - } - } - - if(!AllResultsSheet.isEmpty()) - { - RepairRateAll = csvTool.parseCSVFile(pathToResults + QDir::separator() + AllResultsSheet,errMsg); - if(!errMsg.isEmpty()) - throw errMsg; - - if(!RepairRateAll.empty()) - this->processTotalResults(RepairRateAll,fieldNames,fieldAttributes); - else - { - errMsg = "The total results are empty"; - throw errMsg; - } - } - - // Get the pipelines database - - auto thePipelineDB = ComponentDatabaseManager::getInstance()->getAssetDb("GasNetworkPipelines"); - - if(thePipelineDB == nullptr) - { - QString msg = "Error getting the pipeline database from the input widget!"; - throw msg; - } - - if(thePipelineDB->isEmpty()) - { - QString msg = "Pipeline database is empty"; - throw msg; - } - - auto selFeatLayer = thePipelineDB->getSelectedLayer(); - mapViewSubWidget->setCurrentLayer(selFeatLayer); - -// mapViewSubWidget->addLayerToLegend(selFeatLayer); - - // Starting editing - thePipelineDB->startEditing(); - - auto res = thePipelineDB->addNewComponentAttributes(fieldNames,fieldAttributes,errMsg); - if(!res) - throw errMsg; - - // Commit the changes - thePipelineDB->commitChanges(); - - defaultItem->setState(2); - - listWidget->expandAll(); - - return 0; -} - - -int OpenSRAPostProcessor::processPGVResults(const QVector& DVResults, QStringList& fieldNames, QVector& fieldAttributes) -{ - - auto numRows = DVResults.size(); - - // Check if there is data in the results, and not just the header rows - if(numRows < numHeaderRows) - { - QString msg = "No results to import!"; - throw msg; - } - - auto numHeaderColumns = DVResults.at(0).size(); - - if(numHeaderColumns < numInfoCols) - { - QString msg = "No results to import!"; - throw msg; - } - - if(PGVTreeItem == nullptr) - { - PGVTreeItem = listWidget->addItem("Shaking Induced"); - PGVTreeItem->setIsCheckable(false); - } - - QStringList tableHeadings; - - // Add the ID heading - tableHeadings<addItem(itemStr,PGVTreeItem); - - // Set the header string as a property so I can find the header value later - item->setProperty("HeaderString",headerStr); - } - - PGVResultsTableWidget->setColumnCount(tableHeadings.size()); - PGVResultsTableWidget->setHorizontalHeaderLabels(tableHeadings); - PGVResultsTableWidget->setRowCount(DVResults.size()-numHeaderRows); - - QVector< QgsAttributes > attributes(DVResults.size()-numHeaderRows, QgsAttributes(numHeaderColumns-numInfoCols)); - - // Start at the row where headers end - for(int row = numHeaderRows, count = 0; rowsetItem(row-1,0, tableIDItem); - - auto& rowData = attributes[count]; - - // Populate the table and database with the results - for(int j = 1, k = numInfoCols; ksetItem(row-1,j, tableItem); - - // Add the result to the database - auto value = inputRow.at(k); - - rowData[j-1] = QVariant(value.toDouble()); - } - } - - // Append the new fields and data - - // We do not need the first column that contains the id - tableHeadings.pop_front(); - - fieldNames.append(tableHeadings); - - fieldAttributes.append(attributes); - - return 0; -} - - -int OpenSRAPostProcessor::processPGDResults(const QVector& DVResults, QStringList& fieldNames, QVector& fieldAttributes) -{ - - auto numRows = DVResults.size(); - - // Check if there is data in the results, and not just the header rows - if(numRows < numHeaderRows) - { - QString msg = "No results to import!"; - throw msg; - } - - auto numHeaderColumns = DVResults.at(0).size(); - - if(numHeaderColumns < numInfoCols) - { - QString msg = "No results to import!"; - throw msg; - } - - if(PGDTreeItem == nullptr) - { - PGDTreeItem = listWidget->addItem("Deformation Induced"); - PGDTreeItem->setIsCheckable(false); - } - - QStringList tableHeadings; - - // Add the ID heading - tableHeadings<addItem(itemStr,PGDTreeItem); - - // Set the header string as a property so I can find the header value later - item->setProperty("HeaderString",headerStr); - } - - auto numNewAttributes = numRows-numHeaderRows; - - PGDResultsTableWidget->setColumnCount(tableHeadings.size()); - PGDResultsTableWidget->setHorizontalHeaderLabels(tableHeadings); - PGDResultsTableWidget->setRowCount(numNewAttributes); - - // Start at the row where headers end - for(int row = numHeaderRows, count = 0; rowsetItem(row-1,0, tableIDItem); - - auto& rowData = fieldAttributes[count]; - rowData.reserve(rowData.size()+numNewAttributes); - - // Populate the table and database with the results - for(int j = 1, k = numInfoCols; ksetItem(row-1,j, tableItem); - - // Add the result to the database - auto value = inputRow.at(k); - - rowData.push_back(QVariant(value.toDouble())); - } - } - - // Append the new fields - // We do not need the first column that contains the id - tableHeadings.pop_front(); - - fieldNames.append(tableHeadings); - - return 0; -} - - -int OpenSRAPostProcessor::processTotalResults(const QVector& DVResults, QStringList& fieldNames, QVector& fieldAttributes) -{ - - auto numRows = DVResults.size(); - - // Check if there is data in the results, and not just the header rows - if(numRows < numHeaderRows) - { - QString msg = "No results to import!"; - throw msg; - } - - auto numHeaderColumns = DVResults.at(0).size(); - - if(numHeaderColumns < numInfoCols) - { - QString msg = "No results to import!"; - throw msg; - } - - if(totalTreeItem == nullptr) - { - totalTreeItem = listWidget->addItem("Total Repair Rates"); - totalTreeItem->setIsCheckable(false); - } - - QStringList tableHeadings = DVResults.at(0); - - QString headerStr = "TotalRepairRateForAllDemands"; - - auto indexOfTotals = tableHeadings.indexOf(headerStr,-1); - - if(indexOfTotals == -1) - { - QString msg = "Error getting index to total repairs"; - throw msg; - } - - QString itemStr = "All demands"; - - defaultItem = listWidget->addItem(itemStr,totalTreeItem); - - // Set the header string as a property so I can find the header value later - defaultItem->setProperty("HeaderString",headerStr); - - // Start at the row where headers end - for(int row = numHeaderRows, count = 0; rowtakeScreenShot(); - - return 0; -} - - -void OpenSRAPostProcessor::processResultsSubset(const std::set& selectedComponentIDs) -{ - - if(selectedComponentIDs.empty()) - return; - - if(RepairRatePGD.size() < numHeaderRows) - { - QString msg = "No results to import!"; - throw msg; - } - - if(RepairRatePGD.at(numHeaderRows).isEmpty() || RepairRatePGD.last().isEmpty()) - { - QString msg = "No values in the cells"; - throw msg; - } - - auto firstID = objectToInt(RepairRatePGD.at(numHeaderRows).at(0)); - - auto lastID = objectToInt(RepairRatePGD.last().at(0)); - - QVector DVsubset(&RepairRatePGD[0],&RepairRatePGD[numHeaderRows]); - - for(auto&& id : selectedComponentIDs) - { - // Check that the ID falls within the bounds of the data - if(idlastID) - { - QString msg = "ID " + QString::number(id) + " is out of bounds of the results"; - throw msg; - } - - auto found = false; - for(int i = numHeaderRows; iprocessPGVResults(DVsubset); -} - - -int OpenSRAPostProcessor::assemblePDF(QImage screenShot) -{ - // The printer - QPrinter printer(QPrinter::HighResolution); - printer.setOutputFormat(QPrinter::PdfFormat); - printer.setPaperSize(QPrinter::Letter); - printer.setPageMargins(25.4, 25.4, 25.4, 25.4, QPrinter::Millimeter); - printer.setFullPage(true); - qreal leftMargin, topMargin; - printer.getPageMargins(&leftMargin,&topMargin,nullptr,nullptr,QPrinter::Point); - printer.setOutputFileName(outputFilePath); - - // Create a new document - QTextDocument* document = new QTextDocument(); - QTextCursor cursor(document); - document->setDocumentMargin(25.4); - document->setDefaultFont(QFont("Helvetica")); - - // Define font styles - QTextCharFormat normalFormat; - normalFormat.setFontWeight(QFont::Normal); - - QTextCharFormat titleFormat; - titleFormat.setFontWeight(QFont::Bold); - titleFormat.setFontCapitalization(QFont::AllUppercase); - titleFormat.setFontPointSize(normalFormat.fontPointSize() * 2.0); - - QTextCharFormat captionFormat; - captionFormat.setFontWeight(QFont::Light); - captionFormat.setFontPointSize(normalFormat.fontPointSize() / 2.0); - captionFormat.setFontItalic(true); - - QTextCharFormat boldFormat; - boldFormat.setFontWeight(QFont::Bold); - - QFontMetrics normMetrics(normalFormat.font()); - auto lineSpacing = normMetrics.lineSpacing(); - - // Define alignment formats - QTextBlockFormat alignCenter; - alignCenter.setLineHeight(lineSpacing, QTextBlockFormat::LineDistanceHeight) ; - alignCenter.setAlignment(Qt::AlignCenter); - - QTextBlockFormat alignLeft; - alignLeft.setAlignment(Qt::AlignLeft); - alignLeft.setLineHeight(lineSpacing, QTextBlockFormat::LineDistanceHeight) ; - - cursor.movePosition(QTextCursor::Start); - - cursor.insertBlock(alignCenter); - - cursor.insertText("\nOpenSRA Tool\n",titleFormat); - - cursor.insertText("Results Summary\n",boldFormat); - - cursor.movePosition( QTextCursor::End ); - - // Ratio of the page width that is printable - auto useablePageWidth = printer.pageRect(QPrinter::Point).width()-(1.5*leftMargin); - - QRect viewPortRect(0, mapViewSubWidget->height(), mapViewSubWidget->width(), mapViewSubWidget->height()); - QImage cropped = screenShot.copy(viewPortRect); - document->addResource(QTextDocument::ImageResource,QUrl("Figure1"),cropped); - QTextImageFormat imageFormatFig1; - imageFormatFig1.setName("Figure1"); - imageFormatFig1.setQuality(600); - imageFormatFig1.setWidth(useablePageWidth); - - cursor.setBlockFormat(alignCenter); - - cursor.insertImage(imageFormatFig1); - - cursor.insertText("Regional map visualization.\n",captionFormat); - - cursor.setBlockFormat(alignLeft); - - TablePrinter prettyTablePrinter; - prettyTablePrinter.printToTable(&cursor, PGVResultsTableWidget,"PGV Results"); - - cursor.insertText("\n\n",normalFormat); - - prettyTablePrinter.printToTable(&cursor, PGDResultsTableWidget,"PGD Results"); - - document->print(&printer); - - return 0; -} - - -void OpenSRAPostProcessor::sortTable(int index) -{ - if(index == 0) - PGVResultsTableWidget->sortByColumn(index,Qt::AscendingOrder); - else - PGVResultsTableWidget->sortByColumn(index,Qt::DescendingOrder); - -} - - -void OpenSRAPostProcessor::clear(void) -{ - RepairRatePGV.clear(); - RepairRatePGD.clear(); - RepairRateAll.clear(); - outputFilePath.clear(); - if(thePipelineDb) - thePipelineDb->clear(); - PGVResultsTableWidget->clear(); - listWidget->clear(); - - if(PGVTreeItem != nullptr) - { - delete PGVTreeItem; - PGVTreeItem = nullptr; - } - if(PGDTreeItem != nullptr) - { - delete PGDTreeItem; - PGDTreeItem = nullptr; - } - if(totalTreeItem != nullptr) - { - delete totalTreeItem; - totalTreeItem = nullptr; - } - if(defaultItem != nullptr) - { - delete defaultItem; - defaultItem = nullptr; - } - - mapViewSubWidget->clear(); -} - - -void OpenSRAPostProcessor::handleListSelection(const TreeItem* itemSelected) -{ - if(itemSelected == nullptr) - return; - - auto headerString = itemSelected->property("HeaderString").toString(); - - if(headerString.isEmpty()) - { - this->errorMessage("Could not find the property "+headerString+" in item "+itemSelected->getName()); - return; - } - - // Get the pipelines database - auto thePipelineDB = ComponentDatabaseManager::getInstance()->getAssetDb("GasNetworkPipelines"); - - if(thePipelineDB == nullptr) - { - this->errorMessage("Error getting the pipeline database from the input widget!"); - return; - } - - if(thePipelineDB->isEmpty()) - { - this->errorMessage("Pipeline database is empty"); - return; - } - - auto selFeatLayer = thePipelineDB->getSelectedLayer(); - if(selFeatLayer == nullptr) - { - this->errorMessage("Layer is a nullptr in handleListSelection"); - return; - } - - // Check to see if that field exists in the layer - auto idx = selFeatLayer->dataProvider()->fieldNameIndex(headerString); - - if(idx == -1) - { - this->errorMessage("Could not find the field "+headerString+" in layer "+selFeatLayer->name()); - return; - } - - auto layerRenderer = selFeatLayer->renderer(); - if(layerRenderer == nullptr) - { - this->errorMessage("No layer renderer available in layer "+selFeatLayer->name()); - return; - } - - // Create a graduated renderer if one does not exist - if(layerRenderer->type().compare("graduatedSymbol") != 0) - { - QVector>classBreaks; - QVector colors; - - classBreaks.append(QPair(0.0, 1E-03)); - classBreaks.append(QPair(1.00E-03, 1.00E-02)); - classBreaks.append(QPair(1.00E-02, 1.00E-01)); - classBreaks.append(QPair(1.00E-01, 1.00E+00)); - classBreaks.append(QPair(1.00E+00, 1.00E+01)); - classBreaks.append(QPair(1.00E+01, 1.00E+10)); - - colors.push_back(Qt::darkBlue); - colors.push_back(QColor(255,255,178)); - colors.push_back(QColor(253,204,92)); - colors.push_back(QColor(253,141,60)); - colors.push_back(QColor(240,59,32)); - colors.push_back(QColor(189,0,38)); - - // createCustomClassBreakRenderer(const QString attrName, const QVector>& classBreaks, const QVector& colors, QgsVectorLayer * vlayer) - theVisualizationWidget->createCustomClassBreakRenderer(headerString,selFeatLayer,Qgis::SymbolType::Line,classBreaks,colors); - } - else if(auto graduatedRender = dynamic_cast(layerRenderer)) - { - graduatedRender->setClassAttribute(headerString); - } - else - { - this->errorMessage("Unrecognized type of layer renderer available in layer "+selFeatLayer->name()); - return; - } - - - theVisualizationWidget->markDirty(); -} - - -void OpenSRAPostProcessor::clearAll(void) -{ - - // Get the pipelines database - auto thePipelineDB = ComponentDatabaseManager::getInstance()->getAssetDb("GasNetworkPipelines"); - - if(thePipelineDB == nullptr) - { - this->errorMessage("Error getting the pipeline database from the input widget!"); - return; - } - - if(thePipelineDB->isEmpty()) - { - this->errorMessage("Pipeline database is empty"); - return; - } - - auto selFeatLayer = thePipelineDB->getSelectedLayer(); - if(selFeatLayer == nullptr) - { - this->errorMessage("Layer is a nullptr in handleListSelection"); - return; - } - - QgsLineSymbol* selectedLayerMarkerSymbol = new QgsLineSymbol(); - - selectedLayerMarkerSymbol->setWidth(2.0); - selectedLayerMarkerSymbol->setColor(Qt::darkBlue); - theVisualizationWidget->createSimpleRenderer(selectedLayerMarkerSymbol,selFeatLayer); - - theVisualizationWidget->markDirty(); -} diff --git a/UIWidgets/OpenSRAPostProcessor_old.h b/UIWidgets/OpenSRAPostProcessor_old.h deleted file mode 100644 index 3d7107f..0000000 --- a/UIWidgets/OpenSRAPostProcessor_old.h +++ /dev/null @@ -1,193 +0,0 @@ -#ifndef OpenSRAPostProcessor_H -#define OpenSRAPostProcessor_H -/* ***************************************************************************** -Copyright (c) 2016-2021, The Regents of the University of California (Regents). -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of the FreeBSD Project. - -REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS -PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, -UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - -*************************************************************************** */ - -// Written by: Stevan Gavrilovic - -#include "ComponentDatabase.h" -#include "SimCenterAppWidget.h" -#include "SimCenterMapcanvasWidget.h" - -#include - -#include -#include - -class TreeItem; -class REmpiricalProbabilityDistribution; -class QGISVisualizationWidget; -class MutuallyExclusiveListWidget; - -class QSplitter; -class QTableWidget; -class QGridLayout; -class QLabel; -class QComboBox; - -namespace QtCharts -{ -class QChartView; -class QBarSet; -class QChart; -} - -namespace Esri -{ -namespace ArcGISRuntime -{ -class Map; -class MapGraphicsView; -} -} - - -class OpenSRAPostProcessor : public SimCenterAppWidget -{ - Q_OBJECT - -public: - - OpenSRAPostProcessor(QWidget *parent, QGISVisualizationWidget* visWidget); - - void importResults(const QString& pathToResults); - - int importDVresults(const QString& pathToResults); - - int printToPDF(const QString& outputPath); - - // Function to convert a QString and QVariant to double - // Throws an error exception if conversion fails - template - auto objectToDouble(T obj) - { - // Assume a zero value if the string is empty - if(obj.isNull()) - return 0.0; - - bool OK; - auto val = obj.toDouble(&OK); - - if(!OK) - throw QString("Could not convert the object to a double"); - - return val; - } - - - template - auto objectToInt(T obj) - { - // Assume a zero value if the string is empty - if(obj.isNull()) - return 0; - - bool OK; - auto val = obj.toInt(&OK); - - if(!OK) - throw QString("Could not convert the object to an integer"); - - return val; - } - - void processResultsSubset(const std::set& selectedComponentIDs); - - void clear(void); - -protected: - - void showEvent(QShowEvent *e); - -private slots: - - int assemblePDF(QImage screenShot); - - void sortTable(int index); - - void handleListSelection(const TreeItem* itemSelected); - - void clearAll(void); - - void handleModifyLegend(void); - -private: - - int processPGVResults(const QVector& DVResults, QStringList& fieldNames, QVector& fieldAttributes); - - int processPGDResults(const QVector& DVResults, QStringList& fieldNames, QVector& fieldAttributes); - - int processTotalResults(const QVector& DVResults, QStringList& fieldNames, QVector& fieldAttributes); - - int importResultVisuals(const QString& pathToResults); - int importFaultCrossings(const QString& pathToFile); - int importScenarioTraces(const QString& pathToFile); - - - QVector RepairRatePGD; - QVector RepairRatePGV; - QVector RepairRateAll; - - TreeItem* PGVTreeItem; - TreeItem* PGDTreeItem; - TreeItem* totalTreeItem; - - TreeItem* defaultItem; - - QString outputFilePath; - - QSplitter* mainWidget; - - MutuallyExclusiveListWidget* listWidget; - - QWidget *tableWidget; - QTableWidget* PGVResultsTableWidget; - QTableWidget* PGDResultsTableWidget; - - QGISVisualizationWidget* theVisualizationWidget; - - std::unique_ptr mapViewSubWidget; - - //The number of header rows in the results file - int numHeaderRows; - - // The number of columns that contain component information - int numInfoCols; - - ComponentDatabase* thePipelineDb; -}; - -#endif // OpenSRAPostProcessor_H diff --git a/UIWidgets/OpenSRAPreProcessor.cpp b/UIWidgets/OpenSRAPreProcessor.cpp index 77f1cbe..3da30c5 100644 --- a/UIWidgets/OpenSRAPreProcessor.cpp +++ b/UIWidgets/OpenSRAPreProcessor.cpp @@ -281,9 +281,9 @@ QgsRasterLayer* OpenSRAPreProcessor::loadRaster(const QString& rasterFilePath, c rasterlayer->setOpacity(0.5); - rasterlayer->dataProvider()->setZoomedInResamplingMethod(QgsRasterDataProvider::ResamplingMethod::Bilinear); + rasterlayer->dataProvider()->setZoomedInResamplingMethod(Qgis::RasterResamplingMethod::Bilinear); - rasterlayer->dataProvider()->setZoomedOutResamplingMethod(QgsRasterDataProvider::ResamplingMethod::Bilinear); + rasterlayer->dataProvider()->setZoomedOutResamplingMethod(Qgis::RasterResamplingMethod::Bilinear); rasterlayer->dataProvider()->enableProviderResampling(true); diff --git a/UIWidgets/PipelineDLWidget.cpp b/UIWidgets/PipelineDLWidget.cpp index e8fb6b0..bcbfd65 100644 --- a/UIWidgets/PipelineDLWidget.cpp +++ b/UIWidgets/PipelineDLWidget.cpp @@ -55,7 +55,7 @@ PipelineDLWidget::PipelineDLWidget(QWidget *parent) : MultiComponentR2D("OpenSRA { this->setContentsMargins(0,0,0,0); - theMainLayout->setMargin(0); + theMainLayout->setContentsMargins(0, 0, 0, 0); theMainLayout->setContentsMargins(5,0,0,0); theMainLayout->setSpacing(0); diff --git a/UIWidgets/PipelineNetworkWidget.cpp b/UIWidgets/PipelineNetworkWidget.cpp index c98e1de..394d292 100644 --- a/UIWidgets/PipelineNetworkWidget.cpp +++ b/UIWidgets/PipelineNetworkWidget.cpp @@ -85,13 +85,13 @@ PipelineNetworkWidget::PipelineNetworkWidget(VisualizationWidget* visWidget, QWi { this->setContentsMargins(0,0,0,0); - theMainLayout->setMargin(0); + theMainLayout->setContentsMargins(0, 0, 0, 0); theMainLayout->setContentsMargins(5,0,0,0); theMainLayout->setSpacing(0); QHBoxLayout *theHeaderLayout = new QHBoxLayout(); theHeaderLayout->setContentsMargins(0,0,0,0); - theHeaderLayout->setMargin(0); + theHeaderLayout->setContentsMargins(0, 0, 0, 0); theHeaderLayout->setSpacing(0); SectionTitle *label = new SectionTitle(); label->setText(QString("Infrastructure")); @@ -313,7 +313,7 @@ bool PipelineNetworkWidget::outputAppDataToJSON(QJsonObject &jsonObject) return false; } - jsonObject[jsonKeyword] = compObj; + jsonObject[QStringLiteral("GasNetwork")] = compObj; return true; } @@ -322,13 +322,13 @@ bool PipelineNetworkWidget::outputAppDataToJSON(QJsonObject &jsonObject) bool PipelineNetworkWidget::inputAppDataFromJSON(QJsonObject &jsonObject) { - if (!jsonObject.contains(jsonKeyword)) + if (!jsonObject.contains(QStringLiteral("GasNetwork"))) { - this->errorMessage("Missing the json keyword "+jsonKeyword+" in input file"); + this->errorMessage("Missing the json keyword "+QStringLiteral("GasNetwork")+" in input file"); return false; } - auto gasNetworkObj = jsonObject[jsonKeyword].toObject(); + auto gasNetworkObj = jsonObject[QStringLiteral("GasNetwork")].toObject(); QStringList keys = gasNetworkObj.keys(); if (keys.size() == 1) { diff --git a/UIWidgets/RandomVariablesWidget.cpp b/UIWidgets/RandomVariablesWidget.cpp index 4431781..e570c71 100644 --- a/UIWidgets/RandomVariablesWidget.cpp +++ b/UIWidgets/RandomVariablesWidget.cpp @@ -62,7 +62,7 @@ UPDATES, ENHANCEMENTS, OR MODIFICATIONS. RandomVariablesWidget::RandomVariablesWidget(QWidget *parent) : SimCenterAppWidget(parent) { verticalLayout = new QVBoxLayout(this); - verticalLayout->setMargin(2); + verticalLayout->setContentsMargins(2, 2, 2, 2); verticalLayout->setSpacing(2); RVTableHeaders = QStringList({"Name","Description","Source","Distribution Type","Mean or Median","Sigma","CoV","Distribution Min","Distribution Max","From Model"}); @@ -108,7 +108,7 @@ void RandomVariablesWidget::makeRVWidget(void) // title & add button QHBoxLayout *titleLayout = new QHBoxLayout(); - //titleLayout->setMargin(10); + //titleLayout->setContentsMargins(10, 10, 10, 10); SectionTitle *title=new SectionTitle(); title->setText(tr("Input Variables")); diff --git a/UIWidgets/SourceCharacterizationWidget.cpp b/UIWidgets/SourceCharacterizationWidget.cpp index 0dd49f0..9dfbffa 100644 --- a/UIWidgets/SourceCharacterizationWidget.cpp +++ b/UIWidgets/SourceCharacterizationWidget.cpp @@ -56,7 +56,7 @@ SourceCharacterizationWidget::SourceCharacterizationWidget(QWidget *parent) : SimCenterAppWidget(parent) { QVBoxLayout *mainLayout = new QVBoxLayout(); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); auto sourceLayout = this->getSourceLayout(); diff --git a/UIWidgets/UncertaintyQuantificationWidget.cpp b/UIWidgets/UncertaintyQuantificationWidget.cpp index 63b4cbb..185028a 100644 --- a/UIWidgets/UncertaintyQuantificationWidget.cpp +++ b/UIWidgets/UncertaintyQuantificationWidget.cpp @@ -1,4 +1,4 @@ -/* ***************************************************************************** +/* ***************************************************************************** Copyright (c) 2016-2021, The Regents of the University of California (Regents). All rights reserved. @@ -49,7 +49,7 @@ UncertaintyQuantificationWidget::UncertaintyQuantificationWidget(QWidget *parent : SimCenterAppWidget(parent) { auto layout = new QVBoxLayout(this); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(5,0,0,0); QHBoxLayout *theHeaderLayout = new QHBoxLayout(); diff --git a/UIWidgets/WidgetFactory.cpp b/UIWidgets/WidgetFactory.cpp index 2349392..24aac3f 100644 --- a/UIWidgets/WidgetFactory.cpp +++ b/UIWidgets/WidgetFactory.cpp @@ -1,4 +1,4 @@ -/* ***************************************************************************** +/* ***************************************************************************** Copyright (c) 2016-2017, The Regents of the University of California (Regents). All rights reserved. @@ -125,7 +125,7 @@ QWidget* WidgetFactory::getComboBoxWidget(const QJsonObject& obj, const QString& mainWidget->setObjectName(parentKey); QVBoxLayout* mainLayout = new QVBoxLayout(mainWidget); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(1); JsonComboBox* comboWidget = new JsonComboBox(mainWidget); @@ -291,7 +291,7 @@ QWidget* WidgetFactory::getCheckBoxWidget(const QJsonObject& obj, const QString& checkBoxWidget->setMethodAndParamJsonObj(obj); QHBoxLayout* mainLayout = new QHBoxLayout(mainWidget); - mainLayout->setMargin(0); + mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(4); auto text = obj.value("NameToDisplay").toString(); @@ -332,7 +332,7 @@ QLayout* WidgetFactory::getLayoutFromParams(const QJsonObject& params, const QSt else mainLayout = new QHBoxLayout(); - mainLayout->setMargin(4); + mainLayout->setContentsMargins(4, 4, 4, 4); mainLayout->setSpacing(2); // This will create the widgets in a certain order @@ -421,7 +421,7 @@ bool WidgetFactory::addWidgetToLayout(const QJsonObject& paramObj, const QString widgetLabel->setStyleSheet("font-weight: bold; color: black"); QGridLayout* newHLayout = new QGridLayout(); - newHLayout->setMargin(0); + newHLayout->setContentsMargins(0, 0, 0, 0); newHLayout->setSpacing(4); newHLayout->addWidget(widgetLabel,0,0); @@ -442,7 +442,7 @@ bool WidgetFactory::addWidgetToLayout(const QJsonObject& paramObj, const QString QVBoxLayout* newVLayout = new QVBoxLayout(); QGridLayout* newHLayout = new QGridLayout(); - newHLayout->setMargin(0); + newHLayout->setContentsMargins(0, 0, 0, 0); newHLayout->setSpacing(4); newHLayout->addWidget(widgetLabel,0,0); @@ -465,7 +465,7 @@ bool WidgetFactory::addWidgetToLayout(const QJsonObject& paramObj, const QString widgetLabel->setStyleSheet("font-weight: bold; color: black"); QGridLayout* newHLayout = new QGridLayout(); - newHLayout->setMargin(0); + newHLayout->setContentsMargins(0, 0, 0, 0); newHLayout->setSpacing(4); newHLayout->addWidget(widgetLabel,0,0); @@ -490,7 +490,7 @@ bool WidgetFactory::isNestedComboBoxWidget(const QJsonObject& obj) for (option = options.begin(); option != options.end(); ++option) { - auto params = option.value()["Params"].toObject(); + auto params = option.value().toObject().value("Params").toObject(); if(params.size() != 0) return true; diff --git a/WorkflowAppOpenSRA.cpp b/WorkflowAppOpenSRA.cpp index 96ab095..147834e 100644 --- a/WorkflowAppOpenSRA.cpp +++ b/WorkflowAppOpenSRA.cpp @@ -335,7 +335,7 @@ void WorkflowAppOpenSRA::initialize(void) this->setLayout(horizontalLayout); horizontalLayout->setSpacing(0); this->setContentsMargins(0,0,0,0); - horizontalLayout->setMargin(0); + horizontalLayout->setContentsMargins(0, 0, 0, 0); // Create the component selection & add the components to it theComponentSelection = new OpenSRAComponentSelection(this); diff --git a/arcgisruntime.pri b/arcgisruntime.pri deleted file mode 100644 index bbb9f2b..0000000 --- a/arcgisruntime.pri +++ /dev/null @@ -1,27 +0,0 @@ -#------------------------------------------------- -# Copyright 2019 ESRI -# -# All rights reserved under the copyright laws of the United States -# and applicable international laws, treaties, and conventions. -# -# You may freely redistribute and use this sample code, with or -# without modification, provided you include the original copyright -# notice and use restrictions. -# -# See the Sample code usage restrictions document for further information. -#------------------------------------------------- - -contains(QMAKE_HOST.os, Windows):{ - iniPath = $$(ALLUSERSPROFILE)\EsriRuntimeQt\ArcGIS Runtime SDK for Qt $${ARCGIS_RUNTIME_VERSION}.ini -} -else { - userHome = $$system(echo $HOME) - iniPath = $${userHome}/.config/EsriRuntimeQt/ArcGIS Runtime SDK for Qt $${ARCGIS_RUNTIME_VERSION}.ini -} -iniLine = $$cat($${iniPath}, "lines") -dirPath = $$find(iniLine, "InstallDir") -cleanDirPath = $$replace(dirPath, "InstallDir=", "") -priLocation = $$replace(cleanDirPath, '"', "") -!include($$priLocation/sdk/ideintegration/esri_runtime_qt.pri) { - message("Error. Cannot locate ArcGIS Runtime PRI file") -} \ No newline at end of file diff --git a/cmake/FindQCA.cmake b/cmake/FindQCA.cmake new file mode 100644 index 0000000..bf5c8ca --- /dev/null +++ b/cmake/FindQCA.cmake @@ -0,0 +1,101 @@ +# Find QCA (Qt Cryptography Architecture 2+) +# ~~~~~~~~~~~~~~~~ +# When run this will define +# +# QCA_FOUND - system has QCA +# QCA_LIBRARY - the QCA library or framework +# QCA_INCLUDE_DIR - the QCA include directory +# QCA_VERSION_STR - e.g. "2.0.3" +# +# Copyright (c) 2006, Michael Larouche, +# Copyright (c) 2014, Larry Shaffer, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if(QCA_INCLUDE_DIR AND QCA_LIBRARY) + + set(QCA_FOUND TRUE) + +else(QCA_INCLUDE_DIR AND QCA_LIBRARY) + + set(QCA_LIBRARY_NAMES qca-${QT_VERSION_BASE_LOWER} qca2-${QT_VERSION_BASE_LOWER} qca) + set(QCA_PATH_SUFFIXES ${QT_VERSION_BASE_LOWER}/QtCrypto Qca-${QT_VERSION_BASE_LOWER}/QtCrypto qt/Qca-${QT_VERSION_BASE_LOWER}/QtCrypto ${QT_VERSION_BASE_LOWER}/Qca-${QT_VERSION_BASE_LOWER}/QtCrypto QtCrypto) + + find_library(QCA_LIBRARY + NAMES ${QCA_LIBRARY_NAMES} + PATHS + ${LIB_DIR} + $ENV{LIB} + "$ENV{LIB_DIR}" + $ENV{LIB_DIR}/lib + /usr/local/lib + ) + + set(_qca_fw) + if(QCA_LIBRARY MATCHES "/qca.*\\.framework") + string(REGEX REPLACE "^(.*/qca.*\\.framework).*$" "\\1" _qca_fw "${QCA_LIBRARY}") + endif() + + find_path(QCA_INCLUDE_DIR + NAMES QtCrypto + PATHS + "${_qca_fw}/Headers" + ${LIB_DIR}/include + "$ENV{LIB_DIR}/include" + $ENV{INCLUDE} + /usr/local/include + PATH_SUFFIXES ${QCA_PATH_SUFFIXES} + ) + + if(QCA_LIBRARY AND QCA_INCLUDE_DIR) + set(QCA_FOUND TRUE) + endif() + +endif(QCA_INCLUDE_DIR AND QCA_LIBRARY) + +if(NOT QCA_FOUND) + + if(QCA_FIND_REQUIRED) + message(FATAL_ERROR "Could not find QCA") + else() + message(STATUS "Could not find QCA") + endif() + +else(NOT QCA_FOUND) + + # Check version is valid (>= 2.0.3) + # find_package(QCA 2.0.3) works with 2.1.0+, which has a QcaConfigVersion.cmake, but 2.0.3 does not + + # qca_version.h header only available with 2.1.0+ + set(_qca_version_h "${QCA_INCLUDE_DIR}/qca_version.h") + if(EXISTS "${_qca_version_h}") + file(STRINGS "${_qca_version_h}" _qca_version_str REGEX "^.*QCA_VERSION_STR +\"[^\"]+\".*$") + string(REGEX REPLACE "^.*QCA_VERSION_STR +\"([^\"]+)\".*$" "\\1" QCA_VERSION_STR "${_qca_version_str}") + else() + # qca_core.h contains hexadecimal version in <= 2.0.3 + set(_qca_core_h "${QCA_INCLUDE_DIR}/qca_core.h") + if(EXISTS "${_qca_core_h}") + file(STRINGS "${_qca_core_h}" _qca_version_str REGEX "^#define +QCA_VERSION +0x[0-9a-fA-F]+.*") + string(REGEX REPLACE "^#define +QCA_VERSION +0x([0-9a-fA-F]+)$" "\\1" _qca_version_int "${_qca_version_str}") + if("${_qca_version_int}" STREQUAL "020003") + set(QCA_VERSION_STR "2.0.3") + endif() + endif() + endif() + + if(NOT QCA_VERSION_STR) + set(QCA_FOUND FALSE) + if(QCA_FIND_REQUIRED) + message(FATAL_ERROR "Could not find QCA >= 2.0.3") + else() + message(STATUS "Could not find QCA >= 2.0.3") + endif() + else() + if(NOT QCA_FIND_QUIETLY) + message(STATUS "Found QCA: ${QCA_LIBRARY} (${QCA_VERSION_STR})") + endif() + endif() + +endif(NOT QCA_FOUND) diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..6b116f1 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,41 @@ +from conan import ConanFile +from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout +from conan.tools.files import copy +import os + +# Conan v2 recipe for OpenSRA, mirroring R2DTool/conanfile2.py. +# Provides jansson, zlib, nlohmann_json and (non-Linux) libcurl via CMakeDeps so the +# top-level CMakeLists.txt find_package() calls resolve. QCA and the QGIS libraries are +# NOT Conan deps here: QCA is located by cmake/FindQCA.cmake, and the qgis_* libraries are +# linked directly from the user-supplied QGIS_LIB_DIR (see CMakeLists.txt). + +class OpenSRA_Conan(ConanFile): + name = "OpenSRA" + version = "1.0.0" + license = "BSD-3-Clause" + author = "NHERI SimCenter" + url = "https://github.com/NHERI-SimCenter/OpenSRA" + description = "OpenSRA - Open-source Seismic Risk Analysis frontend" + settings = "os", "arch", "compiler", "build_type" + + def requirements(self): + self.requires("jansson/2.13.1") + self.requires("zlib/1.3.1") + self.requires("nlohmann_json/3.12.0") + if self.settings.os != "Linux": + self.requires("libcurl/8.12.1") + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self) + tc.generate() + + if self.settings.os == "Windows": + # Stage dependency DLLs next to the executable (build/) + bindir = os.path.join(self.build_folder, str(self.settings.build_type)) + for dep in self.dependencies.values(): + if dep.cpp_info.bindirs: + source_dir = dep.cpp_info.bindirs[0] + if os.path.exists(source_dir): + copy(self, "*.dll", source_dir, bindir) diff --git a/main.cpp b/main.cpp index a156a92..a72edab 100644 --- a/main.cpp +++ b/main.cpp @@ -43,7 +43,7 @@ void customMessageOutput(QtMsgType type, const QMessageLogContext &context, cons QFile outFile(logFilePath); outFile.open(QIODevice::WriteOnly | QIODevice::Append); QTextStream ts(&outFile); - ts << txt << endl; + ts << txt << Qt::endl; outFile.close(); } else { fprintf(stderr, "%s %s: %s (%s:%u, %s)\n", formattedTimeMsg.constData(), logLevelMsg.constData(), localMsg.constData(), context.file, context.line, context.function); diff --git a/msvc_fix.h b/msvc_fix.h new file mode 100644 index 0000000..81fd5e1 --- /dev/null +++ b/msvc_fix.h @@ -0,0 +1,23 @@ +#pragma once + +#if defined(_WIN32) + // Only process these lines if we are in a C++ file + #ifdef __cplusplus + #include + // Use a more specific guard to avoid colliding with + // the Windows SDK's definition of byte + #ifndef _BYTE_DEFINED + #define _BYTE_DEFINED + #define _RPCNDR_H_ + using byte = unsigned char; + #endif + #endif + + // These macros are safe for both C and C++ + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif +#endif From 52b37fcd4ba4a0ed1c7821b6396ae893d8375c2b Mon Sep 17 00:00:00 2001 From: OpenSRA Migration Date: Thu, 9 Jul 2026 06:10:49 -0700 Subject: [PATCH 2/7] Blank the unused ArcGIS key in OpenSRAUserPass.h The OpenSRA build is QGIS-based and never calls getArcGISKey(); the file carried a historical Esri runtime-lite license key that got committed with the migration squash. Replace it with an empty string (the build guide already documents an empty stub as sufficient). Co-Authored-By: Claude Fable 5 --- OpenSRAUserPass.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenSRAUserPass.h b/OpenSRAUserPass.h index a65d208..82d8b0f 100644 --- a/OpenSRAUserPass.h +++ b/OpenSRAUserPass.h @@ -7,6 +7,8 @@ #include -static QString getArcGISKey(void){return "runtimelite,1000,rud6425635914,none,2K0RJAY3FPJ3R6EJM104";} +// Key intentionally blank: the OpenSRA build is QGIS-based and never calls +// getArcGISKey(); an empty key is sufficient. (Historical ArcGIS key removed.) +static QString getArcGISKey(void){return "";} #endif // OPENSRAUSERPASS_H From cc96f05e0306262bb9f7bba7a0b6fcdec7ae5ca7 Mon Sep 17 00:00:00 2001 From: OpenSRA Migration Date: Thu, 9 Jul 2026 06:10:49 -0700 Subject: [PATCH 3/7] MIGRATION_NOTES: document the frontend half of the CSV-import fix The below-ground CSV round-trip fix spans two repos; the squashed migration commit didn't name the OpenSRAFrontEnd files (GeneralInformationWidget.cpp NDAData guard; LocalApplication.cpp conda-PATH / exit-127 crossing fix). Record them here, matching the R2DTool commit message's pointer to this file. Co-Authored-By: Claude Fable 5 --- MIGRATION_NOTES.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/MIGRATION_NOTES.md b/MIGRATION_NOTES.md index 7df7565..cc51033 100644 --- a/MIGRATION_NOTES.md +++ b/MIGRATION_NOTES.md @@ -59,6 +59,31 @@ nested layout — not carried over.) **There is no `QGIS_DEPS` repo** (confirmed - `JsonWidgets/JsonLabel.cpp`, `JsonWidgets/JsonLineEdit.cpp` — removed dead `#include ` (no live usage in either file). +## CSV-import fix — the frontend half (inherited from `csv-import-fix`) + +This branch descends from `csv-import-fix`, so it carries the **OpenSRAFrontEnd +half** of the below-ground CSV-pipeline round-trip fix. Because the branch was +squashed into a single migration commit, these two files are not called out in the +commit message — recorded here (the R2DTool commit's message points at this file). +The **R2DTool half** (`UIWidgets/LineAssetInputWidget.cpp`: load-CSV-on-import + +emit `DataType`/`SiteDataFile`/flattened six-key `SiteLocationParams`) lives on the +R2DTool `qt6-fixes` branch (commit `a76582b8`). + +- `GeneralInformationWidget.cpp` — emit `NDAData` only when it is non-empty **and** + distinct from `OpenSRAData` (`if(!ndaData.isEmpty() && ndaData != openSRAData)`). + The prior code injected `NDAData == OpenSRAData`, making the backend merge the + dataset folder onto itself. Affects what a GUI-exported SetupConfig writes. +- `LocalApplication.cpp` (two call sites: preprocess ≈L246, main run ≈L442) — + builds the backend subprocess `PATH` from the conda env's native-DLL dirs + (`Library\bin`, `Library\mingw-w64\bin`, `Library\usr\bin`, `Scripts`, …), not + just the `python.exe` directory. Without it a GUI-launched analysis of a + below-ground CSV network dies at the spatial-crossing step with **exit 127** + (GDAL/GEOS/PROJ DLLs not found); steps 1–9 survive because `GDAL_DATA`/`PROJ_DATA` + are set explicitly. + +No backend code change is part of this fix — the GUI now simply emits the six +`SiteLocationParams` keys + `DataType` that `Preprocess.py` already requires. + ## Files deleted (dead code) - `UIWidgets/OpenSRAPostProcessor_old.cpp` + `.h` — legacy postprocessor, not in the build; From 9da4c7ef869c703ff57a59799c74ab021d500285 Mon Sep 17 00:00:00 2001 From: OpenSRA Migration Date: Mon, 20 Jul 2026 21:00:43 -0700 Subject: [PATCH 4/7] Packaging-validation round: app icon, direct launch (QGIS prefix + PROJ data fallbacks), end-of-analysis and save/load hardening, crash guards See the 2026-07-20 section of MIGRATION_NOTES.md for the full list, including the companion SimCenterCommon/R2DTool qt6-fixes commits and backend patch docs. Co-Authored-By: Claude Fable 5 --- CMakeLists.txt | 7 +++ MIGRATION_NOTES.md | 47 +++++++++++++++++++++ OpenSRA.rc | 2 + ResultsWidget.h | 2 +- UIWidgets/CustomVisualizationWidget.cpp | 12 +++++- UIWidgets/GenericModelWidget.cpp | 6 ++- UIWidgets/OpenSRAPostProcessor.cpp | 20 ++++++--- UIWidgets/PipelineNetworkWidget.cpp | 6 ++- UIWidgets/RandomVariablesWidget.cpp | 17 +++++++- UIWidgets/UserDefinedGroundMotionWidget.cpp | 3 +- UIWidgets/UserInputCPTWidget.cpp | 5 +++ main.cpp | 16 +++++++ 12 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 OpenSRA.rc diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d2405a..48c6fc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,13 @@ if(APPLE) message(WARNING "App icon not found: ${APP_ICON}") set(APP_ICON "") endif() +elseif(WIN32) + # .rc embeds icons/openSRA-icon.ico (was RC_ICONS in OpenSRA.pro) + set(APP_ICON "${CMAKE_CURRENT_LIST_DIR}/OpenSRA.rc") + if(NOT EXISTS "${APP_ICON}") + message(WARNING "App icon rc not found: ${APP_ICON}") + set(APP_ICON "") + endif() endif() # ---- Executable ---- diff --git a/MIGRATION_NOTES.md b/MIGRATION_NOTES.md index cc51033..306f3c3 100644 --- a/MIGRATION_NOTES.md +++ b/MIGRATION_NOTES.md @@ -302,3 +302,50 @@ A clean-PATH (self-contained) launch loads all modules — but hits the same ~24 → QMetaObject::activate → QgisApp::updateNewLayerInsertionPoint → AV`. I.e. a layer-removal signal drives the embedded `QgisApp`'s new-layer-insertion-point slot into an invalid deref. Diagnose vs R2D's QgisApp/layer-tree setup; a RelWithDebInfo QGIS (PDBs) would give exact lines. + +--- + +## 2026-07-20 update — packaging validation round (icon, direct launch, PROJ data, crash + serialization fixes) + +Found while packaging the fully self-contained distribution and batch-testing all 33 +bundled backend examples from it. Frontend changes (this repo): + +- **Windows app icon restored** — QMake's `RC_ICONS` had no CMake equivalent; added + `OpenSRA.rc` + a `WIN32` branch in the CMakeLists app-icon block. +- **Direct launch (R2D-style)** — `main.cpp` defaults `QGIS_PREFIX_PATH` to the exe dir + when unset (double-clicking `OpenSRA.exe` now works without the launcher .cmd) and + points `PROJ_DATA` at a shipped `share/proj` when present (the packaged app previously + had no GUI-side proj.db at all; QGIS bundles proj data only on macOS). +- **End-of-analysis hardening** — `importResults` wrapped in try/catch (an uncaught + QString terminated the Qt6 app with no dialog), null guards for empty results-layer + lists (`OpenSRAPostProcessor`, `CustomVisualizationWidget`); results-layer opacity + matched by source URI (all sublayers are renamed on import). +- **Save/load round-trip fixes** — `RandomVariablesWidget` emitted garbage + `\rvs_input.csv` paths (and injected `runDir: null`) in File→Save; + `GenericModelWidget` silently skipped emission when the workdir `Input` was missing + and mis-joined its fallback path; `PipelineNetworkWidget` now propagates + infrastructure serialization failures; `UserDefinedGroundMotionWidget` no longer + rewrites an empty ShakeMap folder to the cwd; crash guards for short CSV rows + (`UserInputCPTWidget`, `RandomVariablesWidget`) and a null line-edit + (`ResultsWidget.h`). + +Changes OUTSIDE this repo (for upstream reporting): + +- **SimCenterCommon `qt6-fixes`**: QGIS-4 use-after-free — `classificationMethodRegistry() + ->method()` now returns an owning `unique_ptr`; the migrated code passed a borrowed + pointer to `setClassificationMethod()` (which takes ownership), crashing on the first + repaint after a graduated renderer was installed (i.e. at end of every analysis). + Fixed with `release()`. Also: `zoomToLayer` guards null extents and catches + `QgsCsException` (was the source of "Could not transform bounding box to target CRS"). +- **R2DTool `qt6-fixes`**: GIS infrastructure serialization emitted a truncated + directory-only `SiteDataFile` (a C++20 `QJsonObject` aliasing no-op: + `obj["a"] = obj["b"]` inserts the LHS key after taking the RHS ref) and assumed a + copy step OpenSRA disables — now emits the absolute source path; `ShakeMapWidget` + bool loader returned `-1` (=true) on failure; per-row bounds checks in + `ComponentTableModel`. +- **Backend (not a git repo)**: 21 patches + 3 data restorations documented in + `Backend_Pandas3_Patches_Round2.md` (pandas-3/numpy-2/geopandas-1 compatibility, + stale UCERF rupture CSV, missing NGA-West2 GMM tables, wrong-results fixes incl. + vs30_source sigma treatment and a positions-as-labels crossing bug). All 28 runnable + examples pass end-to-end from the package; the 5 expected-fail examples fail with + their designed errors. diff --git a/OpenSRA.rc b/OpenSRA.rc new file mode 100644 index 0000000..2a97add --- /dev/null +++ b/OpenSRA.rc @@ -0,0 +1,2 @@ +// Windows application icon (CMake equivalent of the old QMake RC_ICONS line) +IDI_ICON1 ICON "icons/openSRA-icon.ico" diff --git a/ResultsWidget.h b/ResultsWidget.h index e3eb78c..ef2c502 100644 --- a/ResultsWidget.h +++ b/ResultsWidget.h @@ -91,7 +91,7 @@ private slots: QVBoxLayout* mainLayout; QWidget* resultsPageWidget; - AssetInputDelegate* selectComponentsLineEdit; + AssetInputDelegate* selectComponentsLineEdit = nullptr; QGISVisualizationWidget* theVisualizationWidget; std::unique_ptr theOpenSRAPostProcessor; diff --git a/UIWidgets/CustomVisualizationWidget.cpp b/UIWidgets/CustomVisualizationWidget.cpp index 33355ba..710c622 100644 --- a/UIWidgets/CustomVisualizationWidget.cpp +++ b/UIWidgets/CustomVisualizationWidget.cpp @@ -202,7 +202,17 @@ bool CustomVisualizationWidget::inputFromJSON(QJsonObject &jsonObject) int CustomVisualizationWidget::processResults(QString &filenameResults) { - theOpenSRAPostProcessor->importResults(filenameResults); + // importResults throws QString on a missing/unreadable results file; an exception + // escaping this slot would terminate the app under Qt6 (no error dialog) + try { + theOpenSRAPostProcessor->importResults(filenameResults); + } catch (const QString& msg) { + errorMessage(msg); + return -1; + } catch (const std::exception& e) { + errorMessage(QString("Error importing results: ") + e.what()); + return -1; + } this->resultsShow(true); diff --git a/UIWidgets/GenericModelWidget.cpp b/UIWidgets/GenericModelWidget.cpp index 8ca43fc..e61fe77 100644 --- a/UIWidgets/GenericModelWidget.cpp +++ b/UIWidgets/GenericModelWidget.cpp @@ -365,7 +365,9 @@ bool GenericModelWidget::outputToJSON(QJsonObject &jsonObj) { fileDirInfo.setFile(fileDir); if (!fileDirInfo.exists()) { - return true; + // Never silently drop the model (DistType/TableParams/PathToModelInfo all skipped): + // create the staging folder instead and fall through to the export below. + QDir().mkpath(fileDir); // SimCenterAppWidget::errorMessage("Error: In \"GenericModel.cpp\" - cannot determine path to \"Input\" folder in working dir"); // return false; } @@ -589,7 +591,7 @@ bool GenericModelWidget::inputFromJSON(QJsonObject &jsonObj) { // first try using work_dir/Input as reference #ifdef OpenSRA - filePath = OpenSRAPreferences::getInstance()->getLocalWorkDir() + QDir::separator() + "Input" + filePath; + filePath = OpenSRAPreferences::getInstance()->getLocalWorkDir() + QDir::separator() + "Input" + QDir::separator() + filePath; #else filePath = SimCenterPreferences::getInstance()->getLocalWorkDir() + QDir::separator() + "Input" + filePath; #endif diff --git a/UIWidgets/OpenSRAPostProcessor.cpp b/UIWidgets/OpenSRAPostProcessor.cpp index c559b34..9d99a8d 100644 --- a/UIWidgets/OpenSRAPostProcessor.cpp +++ b/UIWidgets/OpenSRAPostProcessor.cpp @@ -97,7 +97,9 @@ OpenSRAPostProcessor::OpenSRAPostProcessor(QWidget *parent, QGISVisualizationWid void OpenSRAPostProcessor::handleModifyLegend(void) { - auto res_layer = results_layers.at(0); + auto res_layer = results_layers.value(0); + if (res_layer == nullptr) + return; auto layerId = res_layer->id(); @@ -119,6 +121,12 @@ int OpenSRAPostProcessor::importResultVisuals(const QString& pathToResults) auto mean_layer = results_layers.value(0); + if (mean_layer == nullptr) + { + this->errorMessage("Error: no layers could be loaded from the results file "+pathToResults); + return -1; + } + auto data_provider = dynamic_cast(mean_layer->dataProvider()); if(data_provider == nullptr) @@ -181,10 +189,12 @@ int OpenSRAPostProcessor::importResultVisuals(const QString& pathToResults) QString name; for(auto&& it: results_layers) { - name = it->name(); - if (name == "deformation_polygons_crossed") + // addVectorInGroup() renames every sublayer to "Results"; identify the original + // sublayer via the provider source URI (...|layername=) instead + name = it->source(); + if (name.contains("layername=deformation_polygons_crossed")) it->setOpacity(0.7); - if (name == "caprocks_with_crossings") + if (name.contains("layername=caprocks_with_crossings")) it->setOpacity(0.7); } @@ -229,7 +239,7 @@ void OpenSRAPostProcessor::handleListSelection(const TreeItem* itemSelected) return; } - auto res_layer = results_layers.at(0); + auto res_layer = results_layers.value(0); if(res_layer == nullptr) { diff --git a/UIWidgets/PipelineNetworkWidget.cpp b/UIWidgets/PipelineNetworkWidget.cpp index 394d292..4c6ee68 100644 --- a/UIWidgets/PipelineNetworkWidget.cpp +++ b/UIWidgets/PipelineNetworkWidget.cpp @@ -258,7 +258,11 @@ bool PipelineNetworkWidget::outputToJSON(QJsonObject &jsonObject) QJsonObject compObj; - theCurrInputWidget->outputToJSON(compObj); + if(!theCurrInputWidget->outputToJSON(compObj)) + { + this->errorMessage("Error in the json output of the infrastructure widget "+typeOfInf); + return false; + } QJsonObject assetObj = compObj.value(theCurrInputWidget->getJsonKeyword()).toObject(); diff --git a/UIWidgets/RandomVariablesWidget.cpp b/UIWidgets/RandomVariablesWidget.cpp index e570c71..5c2a05c 100644 --- a/UIWidgets/RandomVariablesWidget.cpp +++ b/UIWidgets/RandomVariablesWidget.cpp @@ -407,8 +407,15 @@ bool RandomVariablesWidget::outputToJSON(QJsonObject &jsonObject) { //auto finalRVPath = pathToRvFile + QDir::separator() + "rvs_input.csv"; //auto finalFixedPath = pathToFixedFile + QDir::separator() + "fixed_input.csv"; - auto finalRVPath = jsonObject["runDir"].toString() + QDir::separator() + "rvs_input.csv"; - auto finalFixedPath = jsonObject["runDir"].toString() + QDir::separator() + "fixed_input.csv"; + // "runDir" exists only in the run flow (assembleInputFile). In File->Save there is no + // staging dir, so emit bare names matching the shipped examples; inputFromJSON resolves + // them against the config's own folder (loadFile sets QDir::currentPath() to it). + // .value() (const) avoids the non-const operator[] inserting a spurious "runDir": null. + const QString runDir = jsonObject.value("runDir").toString(); + auto finalRVPath = runDir.isEmpty() ? QString("rvs_input.csv") + : runDir + QDir::separator() + "rvs_input.csv"; + auto finalFixedPath = runDir.isEmpty() ? QString("fixed_input.csv") + : runDir + QDir::separator() + "fixed_input.csv"; QJsonObject inputParamObj; @@ -846,6 +853,12 @@ bool RandomVariablesWidget::handleLoadVars(const QString& filePath, RVTableView* return false; } + if(param.size() <= indexOfName) + { + this->errorMessage("Error, a short variable row (missing the 'Name' column) in the input file "+filePath); + return false; + } + auto name = param.at(indexOfName); auto vals = getMapFromVals(param); diff --git a/UIWidgets/UserDefinedGroundMotionWidget.cpp b/UIWidgets/UserDefinedGroundMotionWidget.cpp index 53f373e..54c2181 100644 --- a/UIWidgets/UserDefinedGroundMotionWidget.cpp +++ b/UIWidgets/UserDefinedGroundMotionWidget.cpp @@ -150,7 +150,8 @@ bool UserDefinedGroundMotionWidget::inputFromJSON(QJsonObject &jsonObject) } // set the line - if (userDefJsonPbj.contains("PathToGMDataFolder")) + if (userDefJsonPbj.contains("PathToGMDataFolder") + && !userDefJsonPbj["PathToGMDataFolder"].toString().isEmpty()) { auto data_dir = userDefJsonPbj["PathToGMDataFolder"].toString(); diff --git a/UIWidgets/UserInputCPTWidget.cpp b/UIWidgets/UserInputCPTWidget.cpp index 99ab03e..2799fe7 100644 --- a/UIWidgets/UserInputCPTWidget.cpp +++ b/UIWidgets/UserInputCPTWidget.cpp @@ -846,6 +846,11 @@ void UserInputCPTWidget::loadUserCPTData(void) { QStringList& rowStr = data[i]; + // Skip blank/short rows (e.g., a trailing empty line in the CSV) - the + // header-derived indexes would read past the end of the row otherwise + if(rowStr.size() <= qMax(qMax(indexCPTName, indexLon), indexLat)) + continue; + auto stationName = rowStr[indexCPTName]; // Path to station files, e.g., site0.csv diff --git a/main.cpp b/main.cpp index a72edab..132a666 100644 --- a/main.cpp +++ b/main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,21 @@ int main(int argc, char *argv[]) QgsApplication a( argc, argv, true ); + // Packaged layout ships QGIS providers/resources next to the exe. When no explicit + // QGIS_PREFIX_PATH is set (app launched directly, not via RunOpenSRA-Portable.cmd), + // point QGIS at the exe directory so providers load R2D-style on a double-click. + if (qEnvironmentVariableIsEmpty("QGIS_PREFIX_PATH")) + QgsApplication::setPrefixPath(QCoreApplication::applicationDirPath(), true); + + // GUI-side PROJ (proj_9.dll) has no valid data dir in the packaged layout — QGIS + // only bundles proj data on macOS — so grid-based datum transforms would silently + // degrade. Point PROJ_DATA at the shipped share/proj when present (set before + // initQgis() runs; never overrides a user-provided PROJ_DATA/PROJ_LIB). + if (qEnvironmentVariableIsEmpty("PROJ_DATA") && qEnvironmentVariableIsEmpty("PROJ_LIB")) { + const QString projData = QCoreApplication::applicationDirPath() + "/share/proj"; + if (QFileInfo::exists(projData + "/proj.db")) + qputenv("PROJ_DATA", QDir::toNativeSeparators(projData).toLocal8Bit()); + } auto prefs = OpenSRAPreferences::getInstance(); From d6b301d156dbbe32c0bd7262603c63b17f596e11 Mon Sep 17 00:00:00 2001 From: Tianyu Han Date: Tue, 28 Jul 2026 22:55:00 -0700 Subject: [PATCH 5/7] Sync aleatory/epistemic/weight edits to the selected run-list method Values typed into the Aleatory Variability / Epistemic Uncertainty fields were only captured at the moment "Add to list of methods to run" was clicked; editing them while the method was already in the run list stored nothing, and re-selecting the item reloaded the stale stored values over the user's input. The exported SetupConfig then silently carried "Preferred" instead of the user's sigmas. AddToRunListWidget now emits inputsEdited() on user edits (textEdited, so programmatic setText from item selection cannot echo back), and SimCenterJsonWidget writes the current field values into the selected item's stored JSON and refreshes its label. The write-back is skipped when the input panel is showing a different method than the selected item, so typing ahead of an "Add" cannot mutate the selection. Co-Authored-By: Claude Fable 5 --- JsonWidgets/SimCenterJsonWidget.cpp | 65 +++++++++++++++++++++++++++++ JsonWidgets/SimCenterJsonWidget.h | 2 + UIWidgets/AddToRunListWidget.cpp | 6 +++ UIWidgets/AddToRunListWidget.h | 3 ++ 4 files changed, 76 insertions(+) diff --git a/JsonWidgets/SimCenterJsonWidget.cpp b/JsonWidgets/SimCenterJsonWidget.cpp index 71b8818..b4d8833 100644 --- a/JsonWidgets/SimCenterJsonWidget.cpp +++ b/JsonWidgets/SimCenterJsonWidget.cpp @@ -141,6 +141,8 @@ QGroupBox* SimCenterJsonWidget::getWidgetBox(const QJsonObject jsonObj) connect(addRunListWidget,&AddToRunListWidget::addToRunListButtonPressed, this, &SimCenterJsonWidget::handleAddButtonPressed); + connect(addRunListWidget,&AddToRunListWidget::inputsEdited, this, &SimCenterJsonWidget::handleRunListInputsEdited); + groupBoxLayout->addWidget(scrollWidget); // @@ -875,6 +877,69 @@ void SimCenterJsonWidget::handleListItemSelected(const QModelIndex& index) } +void SimCenterJsonWidget::handleRunListInputsEdited(void) +{ + auto treeItem = listWidget->getCurrentItem(); + + if(treeItem == nullptr) + return; + + auto itemID = treeItem->getItemID(); + + auto itemObj = listWidget->getItemJsonObject(itemID); + + auto itemKey = itemObj.value("Key").toString(); + + if(itemKey.isEmpty() || !itemObj.contains(itemKey)) + return; + + // Only sync when the method shown in the input panel is the selected item's + // method, so typing ahead of an "Add" for a different method cannot mutate + // the selected item + QJsonObject currMethodObj; + methodWidget->outputToJSON(currMethodObj); + auto currMethod = currMethodObj.value("Method"); + QString currKey; + if(currMethod.isObject()) + { + auto currKeys = currMethod.toObject().keys(); + if(currKeys.size() == 1) + currKey = currKeys.front(); + } + else + currKey = currMethod.toString(); + + if(currKey != itemKey) + return; + + // Re-capture the model weight, aleatory variability, and epistemic uncertainty + auto methodObj = itemObj.value(itemKey).toObject(); + addRunListWidget->outputToJSON(methodObj); + itemObj[itemKey] = methodObj; + addRunListWidget->outputToJSON(itemObj); + + listWidget->updateItemJsonObject(itemID, itemObj); + + auto aleJson = itemObj.value("Aleatory"); + auto aleVal = aleJson.isDouble() ? QString::number(aleJson.toDouble()) : aleJson.toString(); + if (aleVal.isEmpty()) + aleVal = "Preferred"; + auto epiJson = itemObj.value("Epistemic"); + auto epiVal = epiJson.isDouble() ? QString::number(epiJson.toDouble()) : epiJson.toString(); + if (epiVal.isEmpty()) + epiVal = "Preferred"; + + QString newItemText = itemObj.value("ModelName").toString() + + "\n - weight="+ QString::number(itemObj.value("ModelWeight").toDouble()) + + "\n - aleatory="+ aleVal + + "\n - epistemic="+ epiVal; + + treeItem->setData(newItemText, 0); + + listWidget->viewport()->update(); +} + + QJsonObject SimCenterJsonWidget::getVars(const QJsonObject& origObj, const QString& key) diff --git a/JsonWidgets/SimCenterJsonWidget.h b/JsonWidgets/SimCenterJsonWidget.h index 6f0af4b..73eb052 100644 --- a/JsonWidgets/SimCenterJsonWidget.h +++ b/JsonWidgets/SimCenterJsonWidget.h @@ -76,6 +76,8 @@ public slots: void handleListItemSelected(const QModelIndex &index); + void handleRunListInputsEdited(void); + private: QJsonObject getGenericModelObj(QJsonObject& paramObj, QJsonObject& variableTypesObj); diff --git a/UIWidgets/AddToRunListWidget.cpp b/UIWidgets/AddToRunListWidget.cpp index c6bcf19..3afb2da 100644 --- a/UIWidgets/AddToRunListWidget.cpp +++ b/UIWidgets/AddToRunListWidget.cpp @@ -108,6 +108,12 @@ AddToRunListWidget::AddToRunListWidget(QWidget* parent) : QWidget(parent) connect(addRunListButton,&QPushButton::clicked, this, [=](){emit addToRunListButtonPressed();}); + // textEdited fires only on user input, not on programmatic setText, so loading + // stored values into the fields cannot echo back into the run list + connect(weightLineEdit, &QLineEdit::textEdited, this, &AddToRunListWidget::inputsEdited); + connect(aleatoryLE, &QLineEdit::textEdited, this, &AddToRunListWidget::inputsEdited); + connect(episLE, &QLineEdit::textEdited, this, &AddToRunListWidget::inputsEdited); + QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); diff --git a/UIWidgets/AddToRunListWidget.h b/UIWidgets/AddToRunListWidget.h index 52247a8..ab7afa2 100644 --- a/UIWidgets/AddToRunListWidget.h +++ b/UIWidgets/AddToRunListWidget.h @@ -65,6 +65,9 @@ class AddToRunListWidget : public QWidget signals: void addToRunListButtonPressed(); + // emitted on user edits to the weight/aleatory/epistemic fields + void inputsEdited(); + private: QLineEdit* aleatoryLE = nullptr; From b7c900a37d30d8c6cf477f383393c719f8bfb4b8 Mon Sep 17 00:00:00 2001 From: Tianyu Han Date: Thu, 30 Jul 2026 23:01:26 -0700 Subject: [PATCH 6/7] Default strain-preload and fault-trace-buffer checkboxes to checked; label buffer option UCERF-only Landslide Induced Pipe Strain: "Consider strain preloading" now defaults to checked. Fault Rupture Induced Pipe Strain: both "Increase buffer on fault traces" and "Consider strain preloading" now default to checked, with the buffer line edits enabled to match the checked state. The buffer checkbox label now reads "Increase buffer on fault traces (UCERF only)?". Saved configs are unaffected: inputFromJSON still applies the stored IncreaseBuffer/UseStrainPreload values on load. Co-Authored-By: Claude Fable 5 --- JsonWidgets/SimCenterJsonWidget.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/JsonWidgets/SimCenterJsonWidget.cpp b/JsonWidgets/SimCenterJsonWidget.cpp index b4d8833..49a8db6 100644 --- a/JsonWidgets/SimCenterJsonWidget.cpp +++ b/JsonWidgets/SimCenterJsonWidget.cpp @@ -157,7 +157,7 @@ QGroupBox* SimCenterJsonWidget::getWidgetBox(const QJsonObject jsonObj) inputLayout->setContentsMargins(0, 0, 0, 0); strainLandslideCheckBox = new QCheckBox("1) Consider strain preloading (checked = yes)?"); - strainLandslideCheckBox->setChecked(false); + strainLandslideCheckBox->setChecked(true); inputLayout->addWidget(strainLandslideCheckBox); groupBoxLayout->addLayout(inputLayout,Qt::AlignCenter); @@ -170,15 +170,15 @@ QGroupBox* SimCenterJsonWidget::getWidgetBox(const QJsonObject jsonObj) QHBoxLayout* bufferLayout = new QHBoxLayout(); bufferLayout->setContentsMargins(0, 0, 0, 0); - bufferCheckBox = new QCheckBox("1) Increase buffer on fault traces?"); - bufferCheckBox->setChecked(false); + bufferCheckBox = new QCheckBox("1) Increase buffer on fault traces (UCERF only)?"); + bufferCheckBox->setChecked(true); auto bufferPrimaryLabel = new QLabel("Buffer for Primary Hazard (m):"); bufferPrimaryLineEdit = new QLineEdit(); - bufferPrimaryLineEdit->setEnabled(false); + bufferPrimaryLineEdit->setEnabled(true); bufferPrimaryLineEdit->setText("100"); auto bufferSecondaryLabel = new QLabel("Buffer for Secondary Hazard (m):"); bufferSecondaryLineEdit = new QLineEdit(); - bufferSecondaryLineEdit->setEnabled(false); + bufferSecondaryLineEdit->setEnabled(true); bufferSecondaryLineEdit->setText("100"); bufferLayout->addWidget(bufferCheckBox); bufferLayout->addWidget(bufferPrimaryLabel); @@ -199,7 +199,7 @@ QGroupBox* SimCenterJsonWidget::getWidgetBox(const QJsonObject jsonObj) }); strainFaultRuptureCheckBox = new QCheckBox("2) Consider strain preloading (checked = yes)?"); - strainFaultRuptureCheckBox->setChecked(false); + strainFaultRuptureCheckBox->setChecked(true); inputLayout->addLayout(bufferLayout); inputLayout->addWidget(strainFaultRuptureCheckBox); From d3accf7e3e9cebd209c56d98199e9b1d5115b2b3 Mon Sep 17 00:00:00 2001 From: Tianyu Han Date: Thu, 30 Jul 2026 23:37:08 -0700 Subject: [PATCH 7/7] Widen DV/DM/EDP panel sidebars to 150 px so long method names are not cut off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tester report: with Wells and Caprocks infrastructure, the Decision Variable item "Shaking Induced Rupture on Wells" rendered as "Shaking Induced Rupture on..." — at the 16 px bold sidebar font it wraps to 4 lines (~84 px), which clips at the fixed 120x70 item size. A font-metrics sweep of every panel label in methods_params_doc (below_ground, above_ground, wells_caprocks) found one sibling: "Shaking Induced Moment on Wells" in the Damage Measure panel clipped the same way. At 150 px wide every current label wraps to at most 3 lines (<= 63 px) and fits the 70 px item. Applied to all three method panels (DV/DM/EDP) so their sidebars stay a uniform width when switching. Verified in the running GUI: DV/DM/EDP screenshots for all three infrastructure types show every label complete. Co-Authored-By: Claude Fable 5 --- UIWidgets/DamageMeasureWidget.cpp | 6 ++++-- UIWidgets/DecisionVariableWidget.cpp | 6 ++++-- UIWidgets/EngineeringDemandParameterWidget.cpp | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/UIWidgets/DamageMeasureWidget.cpp b/UIWidgets/DamageMeasureWidget.cpp index ec21903..3c494a1 100644 --- a/UIWidgets/DamageMeasureWidget.cpp +++ b/UIWidgets/DamageMeasureWidget.cpp @@ -102,8 +102,10 @@ DamageMeasureWidget::DamageMeasureWidget(QJsonObject mainObj, QWidget *parent): vecWidgets.append(newWidget); } - theComponentSelection->setWidth(120); - theComponentSelection->setItemWidthHeight(120,70); + // 150 px so long names like "Shaking Induced Moment on Wells" wrap to + // <= 3 lines of the 16 px bold sidebar font and fit the 70 px item height + theComponentSelection->setWidth(150); + theComponentSelection->setItemWidthHeight(150,70); theComponentSelection->displayComponent(0); diff --git a/UIWidgets/DecisionVariableWidget.cpp b/UIWidgets/DecisionVariableWidget.cpp index ea02f08..023189c 100644 --- a/UIWidgets/DecisionVariableWidget.cpp +++ b/UIWidgets/DecisionVariableWidget.cpp @@ -102,8 +102,10 @@ DecisionVariableWidget::DecisionVariableWidget(QJsonObject mainObj, QWidget *par vecWidgets.append(newWidget); } - theComponentSelection->setWidth(120); - theComponentSelection->setItemWidthHeight(120,70); + // 150 px so long names like "Shaking Induced Rupture on Wells" wrap to + // <= 3 lines of the 16 px bold sidebar font and fit the 70 px item height + theComponentSelection->setWidth(150); + theComponentSelection->setItemWidthHeight(150,70); theComponentSelection->displayComponent(0); diff --git a/UIWidgets/EngineeringDemandParameterWidget.cpp b/UIWidgets/EngineeringDemandParameterWidget.cpp index 1146243..fa15bcd 100644 --- a/UIWidgets/EngineeringDemandParameterWidget.cpp +++ b/UIWidgets/EngineeringDemandParameterWidget.cpp @@ -102,8 +102,10 @@ EngineeringDemandParameterWidget::EngineeringDemandParameterWidget(QJsonObject m vecWidgets.append(newWidget); } - theComponentSelection->setWidth(120); - theComponentSelection->setItemWidthHeight(120,70); + // 150 px to match the DV/DM panel sidebars (long method names wrap to + // <= 3 lines of the 16 px bold sidebar font within the 70 px item height) + theComponentSelection->setWidth(150); + theComponentSelection->setItemWidthHeight(150,70); theComponentSelection->displayComponent(0);