From 96abf77252ffd4742f8bc625a42eb41c128529de Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Sat, 31 Jan 2026 11:11:37 -0700 Subject: [PATCH 01/90] Update actions to work after clearing cache, bump to RC2 (#854) --- .github/workflows/app_build.yml | 4 ++-- CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/app_build.yml b/.github/workflows/app_build.yml index 57e707cb7..04df44fd8 100644 --- a/.github/workflows/app_build.yml +++ b/.github/workflows/app_build.yml @@ -433,8 +433,8 @@ jobs: pip show setuptools || true pip install setuptools --upgrade pip3 install aqtinstall - aqt list-qt mac desktop --arch 6.5.2 - aqt list-qt mac desktop --modules 6.5.2 ${{ matrix.QT_ARCH }} + aqt list-qt ${{ matrix.QT_OS_NAME }} desktop --arch $QT_VERSION + aqt list-qt ${{ matrix.QT_OS_NAME }} desktop --modules $QT_VERSION ${{ matrix.QT_ARCH }} aqt install-qt --outputdir ./build/Qt-install/ ${{ matrix.QT_OS_NAME }} desktop $QT_VERSION ${{ matrix.QT_ARCH }} -m qtwebchannel qtwebengine qtwebview qt5compat qtpositioning qtcharts fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 453b0aa98..8bea07d19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,7 +272,7 @@ endif() # TODO: Modify the more specific variables as needed to indicate prerelease, etc # Keep in beta in-between release cycles. Set to empty string (or comment out) for official) -set(PROJECT_VERSION_PRERELEASE "rc1") +set(PROJECT_VERSION_PRERELEASE "rc2") # OpenStudio version: Only include Major.Minor.Patch, eg "3.0.0", even if you have a prerelease tag set(OPENSTUDIOAPPLICATION_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") From 21e980df22d46620f66693f4af95739a92f48204 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Thu, 12 Feb 2026 21:00:04 -0700 Subject: [PATCH 02/90] Remove rc2 label --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bea07d19..817dc8b5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,7 +272,7 @@ endif() # TODO: Modify the more specific variables as needed to indicate prerelease, etc # Keep in beta in-between release cycles. Set to empty string (or comment out) for official) -set(PROJECT_VERSION_PRERELEASE "rc2") +set(PROJECT_VERSION_PRERELEASE "") # OpenStudio version: Only include Major.Minor.Patch, eg "3.0.0", even if you have a prerelease tag set(OPENSTUDIOAPPLICATION_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") From 04d72fbae88609d5d9b915c48e466875352738a5 Mon Sep 17 00:00:00 2001 From: Dan Macumber Date: Tue, 17 Feb 2026 17:44:30 -0700 Subject: [PATCH 03/90] Update CMakeLists.txt Online editor screwed up resolving conflicts, fixing here --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 153dc4c96..817dc8b5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,11 +272,7 @@ endif() # TODO: Modify the more specific variables as needed to indicate prerelease, etc # Keep in beta in-between release cycles. Set to empty string (or comment out) for official) -<<<<<<< develop set(PROJECT_VERSION_PRERELEASE "") -======= -set(PROJECT_VERSION_PRERELEASE "rc2") ->>>>>>> master # OpenStudio version: Only include Major.Minor.Patch, eg "3.0.0", even if you have a prerelease tag set(OPENSTUDIOAPPLICATION_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") From 49bd93672b4c58d19a391f85980b7bfddb5a16a5 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 2 Mar 2026 12:55:47 +0100 Subject: [PATCH 04/90] #757 - Initial implementation for SiteGroundTemperatureBuildingSurface (WIP) --- src/openstudio_lib/CMakeLists.txt | 6 + src/openstudio_lib/LocationTabController.cpp | 9 + src/openstudio_lib/LocationTabController.hpp | 3 +- .../SiteGroundTemperatureController.cpp | 60 +++ .../SiteGroundTemperatureController.hpp | 34 ++ .../SiteGroundTemperatureWidget.cpp | 408 ++++++++++++++++++ .../SiteGroundTemperatureWidget.hpp | 86 ++++ 7 files changed, 605 insertions(+), 1 deletion(-) create mode 100644 src/openstudio_lib/SiteGroundTemperatureController.cpp create mode 100644 src/openstudio_lib/SiteGroundTemperatureController.hpp create mode 100644 src/openstudio_lib/SiteGroundTemperatureWidget.cpp create mode 100644 src/openstudio_lib/SiteGroundTemperatureWidget.hpp diff --git a/src/openstudio_lib/CMakeLists.txt b/src/openstudio_lib/CMakeLists.txt index 917c074b2..0ac15c3de 100644 --- a/src/openstudio_lib/CMakeLists.txt +++ b/src/openstudio_lib/CMakeLists.txt @@ -278,6 +278,10 @@ set(${target_name}_SRC ServiceWaterScene.hpp SimSettingsTabController.cpp SimSettingsTabController.hpp + SiteGroundTemperatureController.cpp + SiteGroundTemperatureController.hpp + SiteGroundTemperatureWidget.cpp + SiteGroundTemperatureWidget.hpp SimSettingsTabView.cpp SimSettingsTabView.hpp SimSettingsView.cpp @@ -635,6 +639,8 @@ set(${target_name}_moc SimSettingsTabController.hpp SimSettingsTabView.hpp SimSettingsView.hpp + SiteGroundTemperatureController.hpp + SiteGroundTemperatureWidget.hpp SpaceLoadInstancesWidget.hpp SpacesDaylightingGridView.hpp SpacesInteriorPartitionsGridView.hpp diff --git a/src/openstudio_lib/LocationTabController.cpp b/src/openstudio_lib/LocationTabController.cpp index b3efae7df..bdbb944f8 100644 --- a/src/openstudio_lib/LocationTabController.cpp +++ b/src/openstudio_lib/LocationTabController.cpp @@ -7,6 +7,8 @@ #include "LifeCycleCostsTabView.hpp" #include "LocationTabView.hpp" +#include "SiteGroundTemperatureController.hpp" +#include "SiteGroundTemperatureWidget.hpp" #include "UtilityBillsView.hpp" #include "UtilityBillsController.hpp" @@ -29,6 +31,7 @@ LocationTabController::LocationTabController(bool isIP, const model::Model& mode mainContentWidget()->addSubTab(tr("Weather File && Design Days"), WEATHER_FILE); mainContentWidget()->addSubTab(tr("Life Cycle Costs"), LIFE_CYCLE_COSTS); mainContentWidget()->addSubTab(tr("Utility Bills"), UTILITY_BILLS); + mainContentWidget()->addSubTab(tr("Ground Temperatures"), GROUND_TEMPERATURES); // setSubTab(0); auto* locationView = new LocationView(m_isIP, m_model, m_modelTempDir); @@ -100,6 +103,12 @@ void LocationTabController::setSubTab(int index) { } break; } + case 3: { + auto* groundTemperaturesController = new SiteGroundTemperatureController(m_model); + this->mainContentWidget()->setSubTab(groundTemperaturesController->subTabView()); + m_currentView = groundTemperaturesController->subTabView(); + break; + } default: OS_ASSERT(false); break; diff --git a/src/openstudio_lib/LocationTabController.hpp b/src/openstudio_lib/LocationTabController.hpp index caae431b0..5748705b4 100644 --- a/src/openstudio_lib/LocationTabController.hpp +++ b/src/openstudio_lib/LocationTabController.hpp @@ -36,7 +36,8 @@ class LocationTabController : public MainTabController { WEATHER_FILE, LIFE_CYCLE_COSTS, - UTILITY_BILLS + UTILITY_BILLS, + GROUND_TEMPERATURES }; private: diff --git a/src/openstudio_lib/SiteGroundTemperatureController.cpp b/src/openstudio_lib/SiteGroundTemperatureController.cpp new file mode 100644 index 000000000..9218c2c7a --- /dev/null +++ b/src/openstudio_lib/SiteGroundTemperatureController.cpp @@ -0,0 +1,60 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#include "SiteGroundTemperatureController.hpp" + +#include "SiteGroundTemperatureWidget.hpp" +#include "OSItemSelectorButtons.hpp" + +#include +#include +#include +#include + +#include + +namespace openstudio { + +SiteGroundTemperatureController::SiteGroundTemperatureController(const model::Model& model) + : ModelSubTabController(new SiteGroundTemperatureView(model), model) { + subTabView()->itemSelectorButtons()->hideAddButton(); + subTabView()->itemSelectorButtons()->hideRemoveButton(); + subTabView()->itemSelectorButtons()->hideCopyButton(); + subTabView()->itemSelectorButtons()->hidePurgeButton(); + subTabView()->itemSelectorButtons()->hideDropZone(); +} + +void SiteGroundTemperatureController::onAddObject(const openstudio::IddObjectType& iddObjectType) { + model::Model model = this->model(); + if (iddObjectType == model::SiteGroundTemperatureBuildingSurface::iddObjectType()) { + model.getUniqueModelObject(); + } +} + +void SiteGroundTemperatureController::onCopyObject(const openstudio::model::ModelObject& /*modelObject*/) { + // not applicable for unique objects +} + +void SiteGroundTemperatureController::onRemoveObject(openstudio::model::ModelObject modelObject) { + modelObject.remove(); +} + +void SiteGroundTemperatureController::onReplaceObject(openstudio::model::ModelObject /*modelObject*/, const OSItemId& /*replacementItemId*/) { + // not yet implemented +} + +void SiteGroundTemperatureController::onPurgeObjects(const openstudio::IddObjectType& /*iddObjectType*/) { + // purge button is disabled +} + +void SiteGroundTemperatureController::onDrop(const OSItemId& /*itemId*/) { + // drop zone is hidden +} + +void SiteGroundTemperatureController::onInspectItem(OSItem* item) { + subTabView()->inspectorView()->selectItem(item); +} + +} // namespace openstudio diff --git a/src/openstudio_lib/SiteGroundTemperatureController.hpp b/src/openstudio_lib/SiteGroundTemperatureController.hpp new file mode 100644 index 000000000..0df947e7d --- /dev/null +++ b/src/openstudio_lib/SiteGroundTemperatureController.hpp @@ -0,0 +1,34 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#ifndef OPENSTUDIO_SITEGROUNDTEMPERATURECONTROLLER_HPP +#define OPENSTUDIO_SITEGROUNDTEMPERATURECONTROLLER_HPP + +#include "ModelSubTabController.hpp" + +namespace openstudio { + +class SiteGroundTemperatureController : public ModelSubTabController +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureController(const model::Model& model); + + virtual ~SiteGroundTemperatureController() = default; + + protected: + virtual void onAddObject(const openstudio::IddObjectType& iddObjectType) override; + virtual void onCopyObject(const openstudio::model::ModelObject& modelObject) override; + virtual void onRemoveObject(openstudio::model::ModelObject modelObject) override; + virtual void onReplaceObject(openstudio::model::ModelObject modelObject, const OSItemId& replacementItemId) override; + virtual void onPurgeObjects(const openstudio::IddObjectType& iddObjectType) override; + virtual void onDrop(const OSItemId& itemId) override; + virtual void onInspectItem(OSItem* item) override; +}; + +} // namespace openstudio + +#endif // OPENSTUDIO_SITEGROUNDTEMPERATURECONTROLLER_HPP diff --git a/src/openstudio_lib/SiteGroundTemperatureWidget.cpp b/src/openstudio_lib/SiteGroundTemperatureWidget.cpp new file mode 100644 index 000000000..4a3274e06 --- /dev/null +++ b/src/openstudio_lib/SiteGroundTemperatureWidget.cpp @@ -0,0 +1,408 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#include "SiteGroundTemperatureWidget.hpp" + +#include "ModelObjectItem.hpp" +#include "ModelObjectListView.hpp" +#include "ModelObjectTypeItem.hpp" +#include "OSCollapsibleItemHeader.hpp" +#include "OSItem.hpp" +#include "OSItemSelectorButtons.hpp" + +#include "../shared_gui_components/OSDoubleEdit.hpp" + +#include +#include + +#include + +#include +#include +#include +#include + +#define TEMP_EDIT_WIDTH 90 + +namespace openstudio { + +// SiteGroundTemperatureListView + +SiteGroundTemperatureListView::SiteGroundTemperatureListView(const model::Model& model, bool addScrollArea, OSItemType headerType, QWidget* parent) + : OSCollapsibleItemList(addScrollArea, parent), m_model(model) { + // Unique object — ensure it exists before the list view queries the model + m_model.getUniqueModelObject(); + + auto* header = new OSCollapsibleItemHeader("Building Surface Ground Temperatures", OSItemId("", "", false), headerType); + auto* listView = new ModelObjectListView(model::SiteGroundTemperatureBuildingSurface::iddObjectType(), model, false, false); + auto* item = new ModelObjectTypeItem(header, listView); + addCollapsibleItem(item); +} + +// SiteGroundTemperatureView + +SiteGroundTemperatureView::SiteGroundTemperatureView(const model::Model& model, QWidget* parent) + : ModelSubTabView(new SiteGroundTemperatureListView(model, true, OSItemType::CollapsibleListHeader, parent), + new SiteGroundTemperatureInspectorView(model, parent), false, parent) {} + +// SiteGroundTemperatureInspectorView + +SiteGroundTemperatureInspectorView::SiteGroundTemperatureInspectorView(const model::Model& model, QWidget* parent) + : ModelObjectInspectorView(model, true, parent), m_hiddenWidgetIndex(0), m_buildingSurfaceWidgetIndex(0) { + createWidgets(); +} + +SiteGroundTemperatureInspectorView::~SiteGroundTemperatureInspectorView() { + detach(); +} + +void SiteGroundTemperatureInspectorView::createWidgets() { + QVBoxLayout* vLayout = nullptr; + QLabel* label = nullptr; + + auto* hiddenWidget = new QWidget(); + m_hiddenWidgetIndex = this->stackedWidget()->insertWidget(0, hiddenWidget); + + auto* bsWidget = new QWidget(); + m_buildingSurfaceWidgetIndex = this->stackedWidget()->addWidget(bsWidget); + + auto* mainLayout = new QVBoxLayout(); + mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); + mainLayout->setContentsMargins(10, 10, 10, 10); + mainLayout->setSpacing(20); + bsWidget->setLayout(mainLayout); + + label = new QLabel("Site:GroundTemperature:BuildingSurface"); + label->setObjectName("H2"); + mainLayout->addWidget(label); + + auto* gridLayout = new QGridLayout(); + gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setSpacing(10); + mainLayout->addLayout(gridLayout); + + // Row 0: January - April + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("January (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_januaryEdit = new OSDoubleEdit2(); + m_januaryEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_januaryEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 0, 0, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("February (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_februaryEdit = new OSDoubleEdit2(); + m_februaryEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_februaryEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 0, 1, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("March (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_marchEdit = new OSDoubleEdit2(); + m_marchEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_marchEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 0, 2, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("April (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_aprilEdit = new OSDoubleEdit2(); + m_aprilEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_aprilEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 0, 3, Qt::AlignLeft); + + // Row 1: May - August + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("May (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_mayEdit = new OSDoubleEdit2(); + m_mayEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_mayEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 1, 0, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("June (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_juneEdit = new OSDoubleEdit2(); + m_juneEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_juneEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 1, 1, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("July (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_julyEdit = new OSDoubleEdit2(); + m_julyEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_julyEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 1, 2, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("August (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_augustEdit = new OSDoubleEdit2(); + m_augustEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_augustEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 1, 3, Qt::AlignLeft); + + // Row 2: September - December + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("September (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_septemberEdit = new OSDoubleEdit2(); + m_septemberEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_septemberEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 2, 0, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("October (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_octoberEdit = new OSDoubleEdit2(); + m_octoberEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_octoberEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 2, 1, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("November (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_novemberEdit = new OSDoubleEdit2(); + m_novemberEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_novemberEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 2, 2, Qt::AlignLeft); + + vLayout = new QVBoxLayout(); + vLayout->setSpacing(5); + label = new QLabel("December (\xc2\xb0""C)"); + label->setObjectName("H2"); + vLayout->addWidget(label); + m_decemberEdit = new OSDoubleEdit2(); + m_decemberEdit->setFixedWidth(TEMP_EDIT_WIDTH); + vLayout->addWidget(m_decemberEdit); + vLayout->addStretch(); + gridLayout->addLayout(vLayout, 2, 3, Qt::AlignLeft); + + gridLayout->setColumnStretch(100, 100); + + mainLayout->addStretch(); +} + +void SiteGroundTemperatureInspectorView::attachBuildingSurface(const model::SiteGroundTemperatureBuildingSurface& obj) { + m_buildingSurface = obj; + + m_januaryEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::januaryGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setJanuaryGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetJanuaryGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isJanuaryGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_februaryEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::februaryGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setFebruaryGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetFebruaryGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isFebruaryGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_marchEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::marchGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setMarchGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetMarchGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isMarchGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_aprilEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::aprilGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setAprilGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetAprilGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isAprilGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_mayEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::mayGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setMayGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetMayGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isMayGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_juneEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::juneGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setJuneGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetJuneGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isJuneGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_julyEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::julyGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setJulyGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetJulyGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isJulyGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_augustEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::augustGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setAugustGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetAugustGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isAugustGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_septemberEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::septemberGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setSeptemberGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetSeptemberGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isSeptemberGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_octoberEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::octoberGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setOctoberGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetOctoberGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isOctoberGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_novemberEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::novemberGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setNovemberGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetNovemberGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isNovemberGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + m_decemberEdit->bind( + *m_buildingSurface, + DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::decemberGroundTemperature, m_buildingSurface.get_ptr())), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::setDecemberGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::resetDecemberGroundTemperature, m_buildingSurface.get_ptr())), + boost::none, boost::none, + boost::optional( + std::bind(&model::SiteGroundTemperatureBuildingSurface::isDecemberGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); + + this->stackedWidget()->setCurrentIndex(m_buildingSurfaceWidgetIndex); +} + +void SiteGroundTemperatureInspectorView::detach() { + this->stackedWidget()->setCurrentIndex(m_hiddenWidgetIndex); + + if (m_januaryEdit) m_januaryEdit->unbind(); + if (m_februaryEdit) m_februaryEdit->unbind(); + if (m_marchEdit) m_marchEdit->unbind(); + if (m_aprilEdit) m_aprilEdit->unbind(); + if (m_mayEdit) m_mayEdit->unbind(); + if (m_juneEdit) m_juneEdit->unbind(); + if (m_julyEdit) m_julyEdit->unbind(); + if (m_augustEdit) m_augustEdit->unbind(); + if (m_septemberEdit) m_septemberEdit->unbind(); + if (m_octoberEdit) m_octoberEdit->unbind(); + if (m_novemberEdit) m_novemberEdit->unbind(); + if (m_decemberEdit) m_decemberEdit->unbind(); + + m_buildingSurface = boost::none; +} + +void SiteGroundTemperatureInspectorView::onSelectItem(OSItem* item) { + auto* modelObjectItem = qobject_cast(item); + OS_ASSERT(modelObjectItem); + onSelectModelObject(modelObjectItem->modelObject()); +} + +void SiteGroundTemperatureInspectorView::onClearSelection() { + ModelObjectInspectorView::onClearSelection(); + detach(); +} + +void SiteGroundTemperatureInspectorView::onSelectModelObject(const openstudio::model::ModelObject& modelObject) { + detach(); + if (auto obj = modelObject.optionalCast()) { + attachBuildingSurface(*obj); + } +} + +void SiteGroundTemperatureInspectorView::onUpdate() { + // nothing to refresh +} + +} // namespace openstudio diff --git a/src/openstudio_lib/SiteGroundTemperatureWidget.hpp b/src/openstudio_lib/SiteGroundTemperatureWidget.hpp new file mode 100644 index 000000000..2d5d91159 --- /dev/null +++ b/src/openstudio_lib/SiteGroundTemperatureWidget.hpp @@ -0,0 +1,86 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#ifndef OPENSTUDIO_SITEGROUNDTEMPERATUREWIDGET_HPP +#define OPENSTUDIO_SITEGROUNDTEMPERATUREWIDGET_HPP + +#include "ModelObjectInspectorView.hpp" +#include "ModelSubTabView.hpp" +#include "OSCollapsibleItemList.hpp" + +#include +#include + +#include + +namespace openstudio { + +class OSDoubleEdit2; + +class SiteGroundTemperatureListView : public OSCollapsibleItemList +{ + Q_OBJECT + + public: + SiteGroundTemperatureListView(const model::Model& model, bool addScrollArea, OSItemType headerType, QWidget* parent = nullptr); + + virtual ~SiteGroundTemperatureListView() = default; + + private: + model::Model m_model; +}; + +class SiteGroundTemperatureView : public ModelSubTabView +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureView(const model::Model& model, QWidget* parent = nullptr); + + virtual ~SiteGroundTemperatureView() = default; +}; + +class SiteGroundTemperatureInspectorView : public ModelObjectInspectorView +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureInspectorView(const model::Model& model, QWidget* parent = nullptr); + + virtual ~SiteGroundTemperatureInspectorView(); + + protected: + virtual void onSelectItem(OSItem* item) override; + virtual void onClearSelection() override; + virtual void onSelectModelObject(const openstudio::model::ModelObject& modelObject) override; + virtual void onUpdate() override; + + private: + void createWidgets(); + void attachBuildingSurface(const model::SiteGroundTemperatureBuildingSurface& obj); + void detach(); + + boost::optional m_buildingSurface; + + int m_hiddenWidgetIndex; + int m_buildingSurfaceWidgetIndex; + + OSDoubleEdit2* m_januaryEdit = nullptr; + OSDoubleEdit2* m_februaryEdit = nullptr; + OSDoubleEdit2* m_marchEdit = nullptr; + OSDoubleEdit2* m_aprilEdit = nullptr; + OSDoubleEdit2* m_mayEdit = nullptr; + OSDoubleEdit2* m_juneEdit = nullptr; + OSDoubleEdit2* m_julyEdit = nullptr; + OSDoubleEdit2* m_augustEdit = nullptr; + OSDoubleEdit2* m_septemberEdit = nullptr; + OSDoubleEdit2* m_octoberEdit = nullptr; + OSDoubleEdit2* m_novemberEdit = nullptr; + OSDoubleEdit2* m_decemberEdit = nullptr; +}; + +} // namespace openstudio + +#endif // OPENSTUDIO_SITEGROUNDTEMPERATUREWIDGET_HPP From 563ed2dfce6f73df0bed261bbbbdda2c9f193e57 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 10:49:26 +0100 Subject: [PATCH 05/90] Add Site:GT:Shallow object too and refactor a bit --- src/openstudio_lib/CMakeLists.txt | 6 +- src/openstudio_lib/LocationTabController.cpp | 5 +- .../SiteGroundTemperatureController.cpp | 10 +- .../SiteGroundTemperatureController.hpp | 9 +- .../SiteGroundTemperatureMonthlyWidget.cpp | 397 +++++++++++++++++ .../SiteGroundTemperatureMonthlyWidget.hpp | 115 +++++ .../SiteGroundTemperatureWidget.cpp | 408 ------------------ .../SiteGroundTemperatureWidget.hpp | 86 ---- 8 files changed, 533 insertions(+), 503 deletions(-) create mode 100644 src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.cpp create mode 100644 src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.hpp delete mode 100644 src/openstudio_lib/SiteGroundTemperatureWidget.cpp delete mode 100644 src/openstudio_lib/SiteGroundTemperatureWidget.hpp diff --git a/src/openstudio_lib/CMakeLists.txt b/src/openstudio_lib/CMakeLists.txt index 0ac15c3de..ef5ec184f 100644 --- a/src/openstudio_lib/CMakeLists.txt +++ b/src/openstudio_lib/CMakeLists.txt @@ -280,8 +280,8 @@ set(${target_name}_SRC SimSettingsTabController.hpp SiteGroundTemperatureController.cpp SiteGroundTemperatureController.hpp - SiteGroundTemperatureWidget.cpp - SiteGroundTemperatureWidget.hpp + SiteGroundTemperatureMonthlyWidget.cpp + SiteGroundTemperatureMonthlyWidget.hpp SimSettingsTabView.cpp SimSettingsTabView.hpp SimSettingsView.cpp @@ -640,7 +640,7 @@ set(${target_name}_moc SimSettingsTabView.hpp SimSettingsView.hpp SiteGroundTemperatureController.hpp - SiteGroundTemperatureWidget.hpp + SiteGroundTemperatureMonthlyWidget.hpp SpaceLoadInstancesWidget.hpp SpacesDaylightingGridView.hpp SpacesInteriorPartitionsGridView.hpp diff --git a/src/openstudio_lib/LocationTabController.cpp b/src/openstudio_lib/LocationTabController.cpp index bdbb944f8..169cfab9f 100644 --- a/src/openstudio_lib/LocationTabController.cpp +++ b/src/openstudio_lib/LocationTabController.cpp @@ -8,7 +8,7 @@ #include "LifeCycleCostsTabView.hpp" #include "LocationTabView.hpp" #include "SiteGroundTemperatureController.hpp" -#include "SiteGroundTemperatureWidget.hpp" +#include "SiteGroundTemperatureMonthlyWidget.hpp" #include "UtilityBillsView.hpp" #include "UtilityBillsController.hpp" @@ -104,7 +104,8 @@ void LocationTabController::setSubTab(int index) { break; } case 3: { - auto* groundTemperaturesController = new SiteGroundTemperatureController(m_model); + auto* groundTemperaturesController = new SiteGroundTemperatureController(m_isIP, m_model); + connect(this, &LocationTabController::toggleUnitsClicked, groundTemperaturesController, &SiteGroundTemperatureController::toggleUnitsClicked); this->mainContentWidget()->setSubTab(groundTemperaturesController->subTabView()); m_currentView = groundTemperaturesController->subTabView(); break; diff --git a/src/openstudio_lib/SiteGroundTemperatureController.cpp b/src/openstudio_lib/SiteGroundTemperatureController.cpp index 9218c2c7a..a9d5df162 100644 --- a/src/openstudio_lib/SiteGroundTemperatureController.cpp +++ b/src/openstudio_lib/SiteGroundTemperatureController.cpp @@ -5,20 +5,22 @@ #include "SiteGroundTemperatureController.hpp" -#include "SiteGroundTemperatureWidget.hpp" +#include "SiteGroundTemperatureMonthlyWidget.hpp" #include "OSItemSelectorButtons.hpp" #include #include #include #include +#include +#include #include namespace openstudio { -SiteGroundTemperatureController::SiteGroundTemperatureController(const model::Model& model) - : ModelSubTabController(new SiteGroundTemperatureView(model), model) { +SiteGroundTemperatureController::SiteGroundTemperatureController(bool isIP, const model::Model& model) + : ModelSubTabController(new SiteGroundTemperatureMonthlyView(isIP, model), model) { subTabView()->itemSelectorButtons()->hideAddButton(); subTabView()->itemSelectorButtons()->hideRemoveButton(); subTabView()->itemSelectorButtons()->hideCopyButton(); @@ -30,6 +32,8 @@ void SiteGroundTemperatureController::onAddObject(const openstudio::IddObjectTyp model::Model model = this->model(); if (iddObjectType == model::SiteGroundTemperatureBuildingSurface::iddObjectType()) { model.getUniqueModelObject(); + } else if (iddObjectType == model::SiteGroundTemperatureShallow::iddObjectType()) { + model.getUniqueModelObject(); } } diff --git a/src/openstudio_lib/SiteGroundTemperatureController.hpp b/src/openstudio_lib/SiteGroundTemperatureController.hpp index 0df947e7d..32884bbc6 100644 --- a/src/openstudio_lib/SiteGroundTemperatureController.hpp +++ b/src/openstudio_lib/SiteGroundTemperatureController.hpp @@ -10,12 +10,16 @@ namespace openstudio { +/** Controller for the "Ground Temperatures" sub-tab of the Location tab. + * Manages all Site:GroundTemperature:* object types and delegates to the + * appropriate sub-tab view (e.g. SiteGroundTemperatureMonthlyView for objects + * with 12 monthly values). */ class SiteGroundTemperatureController : public ModelSubTabController { Q_OBJECT public: - explicit SiteGroundTemperatureController(const model::Model& model); + explicit SiteGroundTemperatureController(bool isIP, const model::Model& model); virtual ~SiteGroundTemperatureController() = default; @@ -27,6 +31,9 @@ class SiteGroundTemperatureController : public ModelSubTabController virtual void onPurgeObjects(const openstudio::IddObjectType& iddObjectType) override; virtual void onDrop(const OSItemId& itemId) override; virtual void onInspectItem(OSItem* item) override; + + private: + bool m_isIP; }; } // namespace openstudio diff --git a/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.cpp b/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.cpp new file mode 100644 index 000000000..9ad14da36 --- /dev/null +++ b/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.cpp @@ -0,0 +1,397 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#include "SiteGroundTemperatureMonthlyWidget.hpp" + +#include "ModelObjectItem.hpp" +#include "OSItemSelectorButtons.hpp" + +#include "YearSettingsWidget.hpp" + +#include "../shared_gui_components/OSQuantityEdit.hpp" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define TEMP_EDIT_WIDTH 90 + +namespace openstudio { + +// SiteGroundTemperatureMonthlyItem + +SiteGroundTemperatureMonthlyItem::SiteGroundTemperatureMonthlyItem(const model::ModelObject& obj, const QString& displayName, QWidget* parent) + : OSItem(modelObjectToItemId(obj, false), OSItemType::ListItem, parent), m_modelObject(obj) { + setText(displayName); +} + +model::ModelObject SiteGroundTemperatureMonthlyItem::modelObject() const { + return m_modelObject; +} + +bool SiteGroundTemperatureMonthlyItem::equal(const OSItem* other) const { + if (const auto* o = qobject_cast(other)) { + return m_modelObject.handle() == o->m_modelObject.handle(); + } + return false; +} + +// SiteGroundTemperatureMonthlyListView + +SiteGroundTemperatureMonthlyListView::SiteGroundTemperatureMonthlyListView(const model::Model& model, QWidget* parent) + : OSItemSelector(parent) { + auto* layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + setLayout(layout); + + // getUniqueModelObject is non-const (creates the object if absent) + model::Model m = model; + auto bs = m.getUniqueModelObject(); + auto sh = m.getUniqueModelObject(); + + m_bsItem = new SiteGroundTemperatureMonthlyItem(bs, tr("Building Surface Ground Temperatures"), this); + auto* shItem = new SiteGroundTemperatureMonthlyItem(sh, tr("Shallow Ground Temperatures"), this); + + connect(m_bsItem, &OSItem::itemClicked, this, &SiteGroundTemperatureMonthlyListView::onItemClicked); + connect(shItem, &OSItem::itemClicked, this, &SiteGroundTemperatureMonthlyListView::onItemClicked); + + layout->addWidget(m_bsItem); + layout->addWidget(shItem); + layout->addStretch(); + + QTimer::singleShot(0, this, &SiteGroundTemperatureMonthlyListView::selectFirst); +} + +OSItem* SiteGroundTemperatureMonthlyListView::selectedItem() const { + return m_selectedItem; +} + +void SiteGroundTemperatureMonthlyListView::onItemClicked(OSItem* item) { + if (m_selectedItem) { + m_selectedItem->setSelected(false); + } + m_selectedItem = item; + if (m_selectedItem) { + m_selectedItem->setSelected(true); + } + emit itemSelected(item); +} + +void SiteGroundTemperatureMonthlyListView::selectFirst() { + if (m_bsItem) { + onItemClicked(m_bsItem); + } +} + +// SiteGroundTemperatureMonthlyView + +SiteGroundTemperatureMonthlyView::SiteGroundTemperatureMonthlyView(bool isIP, const model::Model& model, QWidget* parent) + : ModelSubTabView(new SiteGroundTemperatureMonthlyListView(model, parent), + new SiteGroundTemperatureMonthlyInspectorView(isIP, model, parent), false, parent) {} + +// SiteGroundTemperatureMonthlyInspectorView + +SiteGroundTemperatureMonthlyInspectorView::SiteGroundTemperatureMonthlyInspectorView(bool isIP, const model::Model& model, QWidget* parent) + : ModelObjectInspectorView(model, true, parent), m_isIP(isIP), m_hiddenWidgetIndex(0), m_monthlyWidgetIndex(0) { + createWidgets(); +} + +SiteGroundTemperatureMonthlyInspectorView::~SiteGroundTemperatureMonthlyInspectorView() { + detach(); +} + +void SiteGroundTemperatureMonthlyInspectorView::createWidgets() { + auto* hiddenWidget = new QWidget(); + m_hiddenWidgetIndex = this->stackedWidget()->insertWidget(0, hiddenWidget); + + auto* monthlyWidget = new QWidget(); + m_monthlyWidgetIndex = this->stackedWidget()->addWidget(monthlyWidget); + + auto* mainLayout = new QVBoxLayout(); + mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); + mainLayout->setContentsMargins(10, 10, 10, 10); + mainLayout->setSpacing(20); + monthlyWidget->setLayout(mainLayout); + + m_titleLabel = new QLabel(); + m_titleLabel->setObjectName("H2"); + mainLayout->addWidget(m_titleLabel); + + auto* gridLayout = new QGridLayout(); + gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setSpacing(10); + mainLayout->addLayout(gridLayout); + + // Column headers + auto* monthHeader = new QLabel(tr("Month")); + monthHeader->setObjectName("H2"); + gridLayout->addWidget(monthHeader, 0, 0); + + m_temperatureHeader = new QLabel(tr("Temperature")); + m_temperatureHeader->setObjectName("H2"); + gridLayout->addWidget(m_temperatureHeader, 0, 1); + + // One row per month, built from the translated month list + const QStringList monthNames = YearSettingsWidget::months(); + + for (int i = 0; i < 12; ++i) { + gridLayout->addWidget(new QLabel(monthNames[i]), i + 1, 0); + m_edits[i] = new OSQuantityEdit2("C", "C", "F", m_isIP); + connect(this, &SiteGroundTemperatureMonthlyInspectorView::toggleUnitsClicked, m_edits[i], &OSQuantityEdit2::onUnitSystemChange); + m_edits[i]->setFixedWidth(TEMP_EDIT_WIDTH); + gridLayout->addWidget(m_edits[i], i + 1, 1, Qt::AlignLeft); + } + + mainLayout->addStretch(); +} + +void SiteGroundTemperatureMonthlyInspectorView::attachBuildingSurface(const model::SiteGroundTemperatureBuildingSurface& obj) { + m_buildingSurface = obj; + m_titleLabel->setText("Site:GroundTemperature:BuildingSurface"); + + using BS = model::SiteGroundTemperatureBuildingSurface; + + struct MonthBinding + { + double (BS::*getter)() const; + bool (BS::*setter)(double); + void (BS::*resetter)(); + bool (BS::*defaulted)() const; + }; + + static const std::array month_binders{{ + { + &BS::januaryGroundTemperature, + &BS::setJanuaryGroundTemperature, + &BS::resetJanuaryGroundTemperature, + &BS::isJanuaryGroundTemperatureDefaulted, + }, + { + &BS::februaryGroundTemperature, + &BS::setFebruaryGroundTemperature, + &BS::resetFebruaryGroundTemperature, + &BS::isFebruaryGroundTemperatureDefaulted, + }, + { + &BS::marchGroundTemperature, + &BS::setMarchGroundTemperature, + &BS::resetMarchGroundTemperature, + &BS::isMarchGroundTemperatureDefaulted, + }, + { + &BS::aprilGroundTemperature, + &BS::setAprilGroundTemperature, + &BS::resetAprilGroundTemperature, + &BS::isAprilGroundTemperatureDefaulted, + }, + { + &BS::mayGroundTemperature, + &BS::setMayGroundTemperature, + &BS::resetMayGroundTemperature, + &BS::isMayGroundTemperatureDefaulted, + }, + { + &BS::juneGroundTemperature, + &BS::setJuneGroundTemperature, + &BS::resetJuneGroundTemperature, + &BS::isJuneGroundTemperatureDefaulted, + }, + { + &BS::julyGroundTemperature, + &BS::setJulyGroundTemperature, + &BS::resetJulyGroundTemperature, + &BS::isJulyGroundTemperatureDefaulted, + }, + { + &BS::augustGroundTemperature, + &BS::setAugustGroundTemperature, + &BS::resetAugustGroundTemperature, + &BS::isAugustGroundTemperatureDefaulted, + }, + { + &BS::septemberGroundTemperature, + &BS::setSeptemberGroundTemperature, + &BS::resetSeptemberGroundTemperature, + &BS::isSeptemberGroundTemperatureDefaulted, + }, + { + &BS::octoberGroundTemperature, + &BS::setOctoberGroundTemperature, + &BS::resetOctoberGroundTemperature, + &BS::isOctoberGroundTemperatureDefaulted, + }, + { + &BS::novemberGroundTemperature, + &BS::setNovemberGroundTemperature, + &BS::resetNovemberGroundTemperature, + &BS::isNovemberGroundTemperatureDefaulted, + }, + { + &BS::decemberGroundTemperature, + &BS::setDecemberGroundTemperature, + &BS::resetDecemberGroundTemperature, + &BS::isDecemberGroundTemperatureDefaulted, + }, + }}; + + for (int i = 0; i < 12; ++i) { + const auto& m = month_binders[i]; + m_edits[i]->bind(m_isIP, *m_buildingSurface, DoubleGetter([this, g = m.getter]() { return (m_buildingSurface.get_ptr()->*g)(); }), + boost::optional([this, s = m.setter](double v) { return (m_buildingSurface.get_ptr()->*s)(v); }), + boost::optional([this, r = m.resetter]() { (m_buildingSurface.get_ptr()->*r)(); }), boost::none, boost::none, + boost::optional([this, d = m.defaulted]() { return (m_buildingSurface.get_ptr()->*d)(); })); + } + + this->stackedWidget()->setCurrentIndex(m_monthlyWidgetIndex); +} + +void SiteGroundTemperatureMonthlyInspectorView::attachShallow(const model::SiteGroundTemperatureShallow& obj) { + m_shallow = obj; + m_titleLabel->setText("Site:GroundTemperature:Shallow"); + + using SH = model::SiteGroundTemperatureShallow; + + struct MonthBinding + { + double (SH::*getter)() const; + bool (SH::*setter)(double); + void (SH::*resetter)(); + bool (SH::*defaulted)() const; + }; + + static const std::array month_binders{{ + { + &SH::januarySurfaceGroundTemperature, + &SH::setJanuarySurfaceGroundTemperature, + &SH::resetJanuarySurfaceGroundTemperature, + &SH::isJanuarySurfaceGroundTemperatureDefaulted, + }, + { + &SH::februarySurfaceGroundTemperature, + &SH::setFebruarySurfaceGroundTemperature, + &SH::resetFebruarySurfaceGroundTemperature, + &SH::isFebruarySurfaceGroundTemperatureDefaulted, + }, + { + &SH::marchSurfaceGroundTemperature, + &SH::setMarchSurfaceGroundTemperature, + &SH::resetMarchSurfaceGroundTemperature, + &SH::isMarchSurfaceGroundTemperatureDefaulted, + }, + { + &SH::aprilSurfaceGroundTemperature, + &SH::setAprilSurfaceGroundTemperature, + &SH::resetAprilSurfaceGroundTemperature, + &SH::isAprilSurfaceGroundTemperatureDefaulted, + }, + { + &SH::maySurfaceGroundTemperature, + &SH::setMaySurfaceGroundTemperature, + &SH::resetMaySurfaceGroundTemperature, + &SH::isMaySurfaceGroundTemperatureDefaulted, + }, + { + &SH::juneSurfaceGroundTemperature, + &SH::setJuneSurfaceGroundTemperature, + &SH::resetJuneSurfaceGroundTemperature, + &SH::isJuneSurfaceGroundTemperatureDefaulted, + }, + { + &SH::julySurfaceGroundTemperature, + &SH::setJulySurfaceGroundTemperature, + &SH::resetJulySurfaceGroundTemperature, + &SH::isJulySurfaceGroundTemperatureDefaulted, + }, + { + &SH::augustSurfaceGroundTemperature, + &SH::setAugustSurfaceGroundTemperature, + &SH::resetAugustSurfaceGroundTemperature, + &SH::isAugustSurfaceGroundTemperatureDefaulted, + }, + { + &SH::septemberSurfaceGroundTemperature, + &SH::setSeptemberSurfaceGroundTemperature, + &SH::resetSeptemberSurfaceGroundTemperature, + &SH::isSeptemberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::octoberSurfaceGroundTemperature, + &SH::setOctoberSurfaceGroundTemperature, + &SH::resetOctoberSurfaceGroundTemperature, + &SH::isOctoberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::novemberSurfaceGroundTemperature, + &SH::setNovemberSurfaceGroundTemperature, + &SH::resetNovemberSurfaceGroundTemperature, + &SH::isNovemberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::decemberSurfaceGroundTemperature, + &SH::setDecemberSurfaceGroundTemperature, + &SH::resetDecemberSurfaceGroundTemperature, + &SH::isDecemberSurfaceGroundTemperatureDefaulted, + }, + }}; + + for (int i = 0; i < 12; ++i) { + const auto& m = month_binders[i]; + m_edits[i]->bind(m_isIP, *m_shallow, DoubleGetter([this, g = m.getter]() { return (m_shallow.get_ptr()->*g)(); }), + boost::optional([this, s = m.setter](double v) { return (m_shallow.get_ptr()->*s)(v); }), + boost::optional([this, r = m.resetter]() { (m_shallow.get_ptr()->*r)(); }), boost::none, boost::none, + boost::optional([this, d = m.defaulted]() { return (m_shallow.get_ptr()->*d)(); })); + } + + this->stackedWidget()->setCurrentIndex(m_monthlyWidgetIndex); +} + +void SiteGroundTemperatureMonthlyInspectorView::detach() { + this->stackedWidget()->setCurrentIndex(m_hiddenWidgetIndex); + + for (auto* edit : m_edits) { + if (edit) { + edit->unbind(); + } + } + + m_buildingSurface = boost::none; + m_shallow = boost::none; +} + +void SiteGroundTemperatureMonthlyInspectorView::onSelectItem(OSItem* item) { + auto* monthlyItem = qobject_cast(item); + OS_ASSERT(monthlyItem); + onSelectModelObject(monthlyItem->modelObject()); +} + +void SiteGroundTemperatureMonthlyInspectorView::onClearSelection() { + ModelObjectInspectorView::onClearSelection(); + detach(); +} + +void SiteGroundTemperatureMonthlyInspectorView::onSelectModelObject(const openstudio::model::ModelObject& modelObject) { + detach(); + if (auto obj = modelObject.optionalCast()) { + attachBuildingSurface(*obj); + } else if (auto obj = modelObject.optionalCast()) { + attachShallow(*obj); + } +} + +void SiteGroundTemperatureMonthlyInspectorView::onUpdate() { + // nothing to refresh +} + +} // namespace openstudio diff --git a/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.hpp b/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.hpp new file mode 100644 index 000000000..a648f03ac --- /dev/null +++ b/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.hpp @@ -0,0 +1,115 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#ifndef OPENSTUDIO_SITEGROUNDTEMPERATUREMONTHLYWIDGET_HPP +#define OPENSTUDIO_SITEGROUNDTEMPERATUREMONTHLYWIDGET_HPP + +#include "ModelObjectInspectorView.hpp" +#include "ModelSubTabView.hpp" +#include "OSItem.hpp" +#include "OSItemSelector.hpp" + +#include +#include +#include +#include + +#include + +#include + +namespace openstudio { + +class OSQuantityEdit2; + +/** A single entry in the left-hand list for one Site:GroundTemperature:* unique object. + * Uses a caller-supplied display name instead of name().get() (which would crash for + * objects that have no Name IDD field). */ +class SiteGroundTemperatureMonthlyItem : public OSItem +{ + Q_OBJECT + + public: + SiteGroundTemperatureMonthlyItem(const model::ModelObject& obj, const QString& displayName, QWidget* parent = nullptr); + virtual ~SiteGroundTemperatureMonthlyItem() = default; + + model::ModelObject modelObject() const; + + bool equal(const OSItem* other) const override; + + private: + model::ModelObject m_modelObject; +}; + +/** Left-hand picker list for Site:GroundTemperature objects that carry 12 monthly values. + * Directly creates SiteGroundTemperatureMonthlyItem entries — bypasses ModelObjectItem / + * makeItem so that objects without a Name IDD field do not crash. */ +class SiteGroundTemperatureMonthlyListView : public OSItemSelector +{ + Q_OBJECT + + public: + SiteGroundTemperatureMonthlyListView(const model::Model& model, QWidget* parent = nullptr); + virtual ~SiteGroundTemperatureMonthlyListView() = default; + + OSItem* selectedItem() const override; + + private slots: + void onItemClicked(OSItem* item); + void selectFirst(); + + private: + OSItem* m_selectedItem = nullptr; + SiteGroundTemperatureMonthlyItem* m_bsItem = nullptr; +}; + +class SiteGroundTemperatureMonthlyView : public ModelSubTabView +{ + Q_OBJECT + + public: + SiteGroundTemperatureMonthlyView(bool isIP, const model::Model& model, QWidget* parent = nullptr); + + virtual ~SiteGroundTemperatureMonthlyView() = default; +}; + +class SiteGroundTemperatureMonthlyInspectorView : public ModelObjectInspectorView +{ + Q_OBJECT + + public: + SiteGroundTemperatureMonthlyInspectorView(bool isIP, const model::Model& model, QWidget* parent = nullptr); + + virtual ~SiteGroundTemperatureMonthlyInspectorView(); + + protected: + virtual void onSelectItem(OSItem* item) override; + virtual void onClearSelection() override; + virtual void onSelectModelObject(const openstudio::model::ModelObject& modelObject) override; + virtual void onUpdate() override; + + private: + void createWidgets(); + void attachBuildingSurface(const model::SiteGroundTemperatureBuildingSurface& obj); + void attachShallow(const model::SiteGroundTemperatureShallow& obj); + void detach(); + + bool m_isIP; + + boost::optional m_buildingSurface; + boost::optional m_shallow; + + int m_hiddenWidgetIndex; + int m_monthlyWidgetIndex; + + QLabel* m_titleLabel = nullptr; + QLabel* m_temperatureHeader = nullptr; + + std::array m_edits{}; +}; + +} // namespace openstudio + +#endif // OPENSTUDIO_SITEGROUNDTEMPERATUREMONTHLYWIDGET_HPP diff --git a/src/openstudio_lib/SiteGroundTemperatureWidget.cpp b/src/openstudio_lib/SiteGroundTemperatureWidget.cpp deleted file mode 100644 index 4a3274e06..000000000 --- a/src/openstudio_lib/SiteGroundTemperatureWidget.cpp +++ /dev/null @@ -1,408 +0,0 @@ -/*********************************************************************************************************************** -* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. -* See also https://openstudiocoalition.org/about/software_license/ -***********************************************************************************************************************/ - -#include "SiteGroundTemperatureWidget.hpp" - -#include "ModelObjectItem.hpp" -#include "ModelObjectListView.hpp" -#include "ModelObjectTypeItem.hpp" -#include "OSCollapsibleItemHeader.hpp" -#include "OSItem.hpp" -#include "OSItemSelectorButtons.hpp" - -#include "../shared_gui_components/OSDoubleEdit.hpp" - -#include -#include - -#include - -#include -#include -#include -#include - -#define TEMP_EDIT_WIDTH 90 - -namespace openstudio { - -// SiteGroundTemperatureListView - -SiteGroundTemperatureListView::SiteGroundTemperatureListView(const model::Model& model, bool addScrollArea, OSItemType headerType, QWidget* parent) - : OSCollapsibleItemList(addScrollArea, parent), m_model(model) { - // Unique object — ensure it exists before the list view queries the model - m_model.getUniqueModelObject(); - - auto* header = new OSCollapsibleItemHeader("Building Surface Ground Temperatures", OSItemId("", "", false), headerType); - auto* listView = new ModelObjectListView(model::SiteGroundTemperatureBuildingSurface::iddObjectType(), model, false, false); - auto* item = new ModelObjectTypeItem(header, listView); - addCollapsibleItem(item); -} - -// SiteGroundTemperatureView - -SiteGroundTemperatureView::SiteGroundTemperatureView(const model::Model& model, QWidget* parent) - : ModelSubTabView(new SiteGroundTemperatureListView(model, true, OSItemType::CollapsibleListHeader, parent), - new SiteGroundTemperatureInspectorView(model, parent), false, parent) {} - -// SiteGroundTemperatureInspectorView - -SiteGroundTemperatureInspectorView::SiteGroundTemperatureInspectorView(const model::Model& model, QWidget* parent) - : ModelObjectInspectorView(model, true, parent), m_hiddenWidgetIndex(0), m_buildingSurfaceWidgetIndex(0) { - createWidgets(); -} - -SiteGroundTemperatureInspectorView::~SiteGroundTemperatureInspectorView() { - detach(); -} - -void SiteGroundTemperatureInspectorView::createWidgets() { - QVBoxLayout* vLayout = nullptr; - QLabel* label = nullptr; - - auto* hiddenWidget = new QWidget(); - m_hiddenWidgetIndex = this->stackedWidget()->insertWidget(0, hiddenWidget); - - auto* bsWidget = new QWidget(); - m_buildingSurfaceWidgetIndex = this->stackedWidget()->addWidget(bsWidget); - - auto* mainLayout = new QVBoxLayout(); - mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); - mainLayout->setContentsMargins(10, 10, 10, 10); - mainLayout->setSpacing(20); - bsWidget->setLayout(mainLayout); - - label = new QLabel("Site:GroundTemperature:BuildingSurface"); - label->setObjectName("H2"); - mainLayout->addWidget(label); - - auto* gridLayout = new QGridLayout(); - gridLayout->setContentsMargins(0, 0, 0, 0); - gridLayout->setSpacing(10); - mainLayout->addLayout(gridLayout); - - // Row 0: January - April - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("January (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_januaryEdit = new OSDoubleEdit2(); - m_januaryEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_januaryEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 0, 0, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("February (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_februaryEdit = new OSDoubleEdit2(); - m_februaryEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_februaryEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 0, 1, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("March (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_marchEdit = new OSDoubleEdit2(); - m_marchEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_marchEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 0, 2, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("April (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_aprilEdit = new OSDoubleEdit2(); - m_aprilEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_aprilEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 0, 3, Qt::AlignLeft); - - // Row 1: May - August - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("May (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_mayEdit = new OSDoubleEdit2(); - m_mayEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_mayEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 1, 0, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("June (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_juneEdit = new OSDoubleEdit2(); - m_juneEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_juneEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 1, 1, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("July (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_julyEdit = new OSDoubleEdit2(); - m_julyEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_julyEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 1, 2, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("August (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_augustEdit = new OSDoubleEdit2(); - m_augustEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_augustEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 1, 3, Qt::AlignLeft); - - // Row 2: September - December - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("September (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_septemberEdit = new OSDoubleEdit2(); - m_septemberEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_septemberEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 2, 0, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("October (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_octoberEdit = new OSDoubleEdit2(); - m_octoberEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_octoberEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 2, 1, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("November (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_novemberEdit = new OSDoubleEdit2(); - m_novemberEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_novemberEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 2, 2, Qt::AlignLeft); - - vLayout = new QVBoxLayout(); - vLayout->setSpacing(5); - label = new QLabel("December (\xc2\xb0""C)"); - label->setObjectName("H2"); - vLayout->addWidget(label); - m_decemberEdit = new OSDoubleEdit2(); - m_decemberEdit->setFixedWidth(TEMP_EDIT_WIDTH); - vLayout->addWidget(m_decemberEdit); - vLayout->addStretch(); - gridLayout->addLayout(vLayout, 2, 3, Qt::AlignLeft); - - gridLayout->setColumnStretch(100, 100); - - mainLayout->addStretch(); -} - -void SiteGroundTemperatureInspectorView::attachBuildingSurface(const model::SiteGroundTemperatureBuildingSurface& obj) { - m_buildingSurface = obj; - - m_januaryEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::januaryGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setJanuaryGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetJanuaryGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isJanuaryGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_februaryEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::februaryGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setFebruaryGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetFebruaryGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isFebruaryGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_marchEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::marchGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setMarchGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetMarchGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isMarchGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_aprilEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::aprilGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setAprilGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetAprilGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isAprilGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_mayEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::mayGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setMayGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetMayGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isMayGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_juneEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::juneGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setJuneGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetJuneGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isJuneGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_julyEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::julyGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setJulyGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetJulyGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isJulyGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_augustEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::augustGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setAugustGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetAugustGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isAugustGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_septemberEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::septemberGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setSeptemberGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetSeptemberGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isSeptemberGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_octoberEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::octoberGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setOctoberGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetOctoberGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isOctoberGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_novemberEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::novemberGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setNovemberGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetNovemberGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isNovemberGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - m_decemberEdit->bind( - *m_buildingSurface, - DoubleGetter(std::bind(&model::SiteGroundTemperatureBuildingSurface::decemberGroundTemperature, m_buildingSurface.get_ptr())), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::setDecemberGroundTemperature, m_buildingSurface.get_ptr(), std::placeholders::_1)), - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::resetDecemberGroundTemperature, m_buildingSurface.get_ptr())), - boost::none, boost::none, - boost::optional( - std::bind(&model::SiteGroundTemperatureBuildingSurface::isDecemberGroundTemperatureDefaulted, m_buildingSurface.get_ptr()))); - - this->stackedWidget()->setCurrentIndex(m_buildingSurfaceWidgetIndex); -} - -void SiteGroundTemperatureInspectorView::detach() { - this->stackedWidget()->setCurrentIndex(m_hiddenWidgetIndex); - - if (m_januaryEdit) m_januaryEdit->unbind(); - if (m_februaryEdit) m_februaryEdit->unbind(); - if (m_marchEdit) m_marchEdit->unbind(); - if (m_aprilEdit) m_aprilEdit->unbind(); - if (m_mayEdit) m_mayEdit->unbind(); - if (m_juneEdit) m_juneEdit->unbind(); - if (m_julyEdit) m_julyEdit->unbind(); - if (m_augustEdit) m_augustEdit->unbind(); - if (m_septemberEdit) m_septemberEdit->unbind(); - if (m_octoberEdit) m_octoberEdit->unbind(); - if (m_novemberEdit) m_novemberEdit->unbind(); - if (m_decemberEdit) m_decemberEdit->unbind(); - - m_buildingSurface = boost::none; -} - -void SiteGroundTemperatureInspectorView::onSelectItem(OSItem* item) { - auto* modelObjectItem = qobject_cast(item); - OS_ASSERT(modelObjectItem); - onSelectModelObject(modelObjectItem->modelObject()); -} - -void SiteGroundTemperatureInspectorView::onClearSelection() { - ModelObjectInspectorView::onClearSelection(); - detach(); -} - -void SiteGroundTemperatureInspectorView::onSelectModelObject(const openstudio::model::ModelObject& modelObject) { - detach(); - if (auto obj = modelObject.optionalCast()) { - attachBuildingSurface(*obj); - } -} - -void SiteGroundTemperatureInspectorView::onUpdate() { - // nothing to refresh -} - -} // namespace openstudio diff --git a/src/openstudio_lib/SiteGroundTemperatureWidget.hpp b/src/openstudio_lib/SiteGroundTemperatureWidget.hpp deleted file mode 100644 index 2d5d91159..000000000 --- a/src/openstudio_lib/SiteGroundTemperatureWidget.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/*********************************************************************************************************************** -* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. -* See also https://openstudiocoalition.org/about/software_license/ -***********************************************************************************************************************/ - -#ifndef OPENSTUDIO_SITEGROUNDTEMPERATUREWIDGET_HPP -#define OPENSTUDIO_SITEGROUNDTEMPERATUREWIDGET_HPP - -#include "ModelObjectInspectorView.hpp" -#include "ModelSubTabView.hpp" -#include "OSCollapsibleItemList.hpp" - -#include -#include - -#include - -namespace openstudio { - -class OSDoubleEdit2; - -class SiteGroundTemperatureListView : public OSCollapsibleItemList -{ - Q_OBJECT - - public: - SiteGroundTemperatureListView(const model::Model& model, bool addScrollArea, OSItemType headerType, QWidget* parent = nullptr); - - virtual ~SiteGroundTemperatureListView() = default; - - private: - model::Model m_model; -}; - -class SiteGroundTemperatureView : public ModelSubTabView -{ - Q_OBJECT - - public: - explicit SiteGroundTemperatureView(const model::Model& model, QWidget* parent = nullptr); - - virtual ~SiteGroundTemperatureView() = default; -}; - -class SiteGroundTemperatureInspectorView : public ModelObjectInspectorView -{ - Q_OBJECT - - public: - explicit SiteGroundTemperatureInspectorView(const model::Model& model, QWidget* parent = nullptr); - - virtual ~SiteGroundTemperatureInspectorView(); - - protected: - virtual void onSelectItem(OSItem* item) override; - virtual void onClearSelection() override; - virtual void onSelectModelObject(const openstudio::model::ModelObject& modelObject) override; - virtual void onUpdate() override; - - private: - void createWidgets(); - void attachBuildingSurface(const model::SiteGroundTemperatureBuildingSurface& obj); - void detach(); - - boost::optional m_buildingSurface; - - int m_hiddenWidgetIndex; - int m_buildingSurfaceWidgetIndex; - - OSDoubleEdit2* m_januaryEdit = nullptr; - OSDoubleEdit2* m_februaryEdit = nullptr; - OSDoubleEdit2* m_marchEdit = nullptr; - OSDoubleEdit2* m_aprilEdit = nullptr; - OSDoubleEdit2* m_mayEdit = nullptr; - OSDoubleEdit2* m_juneEdit = nullptr; - OSDoubleEdit2* m_julyEdit = nullptr; - OSDoubleEdit2* m_augustEdit = nullptr; - OSDoubleEdit2* m_septemberEdit = nullptr; - OSDoubleEdit2* m_octoberEdit = nullptr; - OSDoubleEdit2* m_novemberEdit = nullptr; - OSDoubleEdit2* m_decemberEdit = nullptr; -}; - -} // namespace openstudio - -#endif // OPENSTUDIO_SITEGROUNDTEMPERATUREWIDGET_HPP From 4e1a3f74706fdf87c425fa8cf407396067ee9d3a Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 11:22:24 +0100 Subject: [PATCH 06/90] Revamp into a custom page, so I don't have to use OSItem here Replace OSItem-based left panel with ScheduleTabDefault-style static entries The current SiteGroundTemperatureMonthlyListView uses OSItemSelector + custom OSItem subclasses. This results in a "NO ICON" placeholder next to each entry because OSItem always reserves space for an icon. I aim to have the visual style of the Schedules tab's ScheduleTabDefault: plain clickable text labels, no icon, with a "not present" fallback view (like NewProfileView) that offers an Add button if the unique object doesn't exist yet. --- src/openstudio_lib/CMakeLists.txt | 9 +- .../GroundTemperatureWidget.cpp | 402 ++++++++++++++++++ .../GroundTemperatureWidget.hpp | 177 ++++++++ src/openstudio_lib/LocationTabController.cpp | 11 +- .../SiteGroundTemperatureController.cpp | 64 --- .../SiteGroundTemperatureController.hpp | 41 -- .../SiteGroundTemperatureMonthlyWidget.cpp | 397 ----------------- .../SiteGroundTemperatureMonthlyWidget.hpp | 115 ----- 8 files changed, 587 insertions(+), 629 deletions(-) create mode 100644 src/openstudio_lib/GroundTemperatureWidget.cpp create mode 100644 src/openstudio_lib/GroundTemperatureWidget.hpp delete mode 100644 src/openstudio_lib/SiteGroundTemperatureController.cpp delete mode 100644 src/openstudio_lib/SiteGroundTemperatureController.hpp delete mode 100644 src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.cpp delete mode 100644 src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.hpp diff --git a/src/openstudio_lib/CMakeLists.txt b/src/openstudio_lib/CMakeLists.txt index ef5ec184f..61b69c197 100644 --- a/src/openstudio_lib/CMakeLists.txt +++ b/src/openstudio_lib/CMakeLists.txt @@ -278,10 +278,8 @@ set(${target_name}_SRC ServiceWaterScene.hpp SimSettingsTabController.cpp SimSettingsTabController.hpp - SiteGroundTemperatureController.cpp - SiteGroundTemperatureController.hpp - SiteGroundTemperatureMonthlyWidget.cpp - SiteGroundTemperatureMonthlyWidget.hpp + GroundTemperatureWidget.cpp + GroundTemperatureWidget.hpp SimSettingsTabView.cpp SimSettingsTabView.hpp SimSettingsView.cpp @@ -639,8 +637,7 @@ set(${target_name}_moc SimSettingsTabController.hpp SimSettingsTabView.hpp SimSettingsView.hpp - SiteGroundTemperatureController.hpp - SiteGroundTemperatureMonthlyWidget.hpp + GroundTemperatureWidget.hpp SpaceLoadInstancesWidget.hpp SpacesDaylightingGridView.hpp SpacesInteriorPartitionsGridView.hpp diff --git a/src/openstudio_lib/GroundTemperatureWidget.cpp b/src/openstudio_lib/GroundTemperatureWidget.cpp new file mode 100644 index 000000000..33dd5d35c --- /dev/null +++ b/src/openstudio_lib/GroundTemperatureWidget.cpp @@ -0,0 +1,402 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#include "GroundTemperatureWidget.hpp" + +#include "YearSettingsWidget.hpp" + +#include "../shared_gui_components/OSQuantityEdit.hpp" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEMP_EDIT_WIDTH 90 + +namespace openstudio { + +// ───────────────────────────────────────────────────────── +// SiteGroundTemperatureEntry +// ───────────────────────────────────────────────────────── + +SiteGroundTemperatureEntry::SiteGroundTemperatureEntry(const QString& label, QWidget* parent) : QWidget(parent) { + setFixedHeight(25); + setMouseTracking(true); + + auto* layout = new QHBoxLayout(); + layout->setContentsMargins(10, 0, 0, 0); + setLayout(layout); + + m_label = new QLabel(label); + m_label->setMouseTracking(true); + layout->addWidget(m_label); +} + +void SiteGroundTemperatureEntry::setSelected(bool selected) { + m_selected = selected; + update(); +} + +void SiteGroundTemperatureEntry::paintEvent(QPaintEvent* /*event*/) { + QStyleOption opt; + opt.initFrom(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + if (m_hovering || m_selected) { + p.setBrush(QBrush(QColor(207, 207, 207))); + p.setPen(Qt::NoPen); + p.drawRect(0, 0, size().width() - 1, size().height() - 1); + } + + p.setPen(Qt::SolidLine); + p.setBrush(QBrush(QColor(Qt::black))); + p.drawLine(0, 0, size().width(), 0); + p.drawLine(0, size().height() - 1, size().width(), size().height() - 1); +} + +void SiteGroundTemperatureEntry::mousePressEvent(QMouseEvent* event) { + m_mouseDown = true; + event->accept(); +} + +void SiteGroundTemperatureEntry::mouseReleaseEvent(QMouseEvent* event) { + if (m_mouseDown) { + emit clicked(); + } + m_mouseDown = false; + event->accept(); +} + +void SiteGroundTemperatureEntry::mouseMoveEvent(QMouseEvent* event) { + m_hovering = true; + update(); + event->accept(); +} + +void SiteGroundTemperatureEntry::leaveEvent(QEvent* event) { + m_mouseDown = false; + m_hovering = false; + update(); + event->accept(); +} + +// ───────────────────────────────────────────────────────── +// GroundTemperatureListView +// ───────────────────────────────────────────────────────── + +GroundTemperatureListView::GroundTemperatureListView(QWidget* parent) : QWidget(parent) { + auto* layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + setLayout(layout); + + m_bsEntry = new SiteGroundTemperatureEntry(tr("Building Surface Ground Temperatures"), this); + m_shEntry = new SiteGroundTemperatureEntry(tr("Shallow Ground Temperatures"), this); + + connect(m_bsEntry, &SiteGroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onBuildingSurfaceClicked); + connect(m_shEntry, &SiteGroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onShallowClicked); + + layout->addWidget(m_bsEntry); + layout->addWidget(m_shEntry); + layout->addStretch(); +} + +void GroundTemperatureListView::selectFirst() { + onBuildingSurfaceClicked(); +} + +void GroundTemperatureListView::onBuildingSurfaceClicked() { + m_bsEntry->setSelected(true); + m_shEntry->setSelected(false); + emit typeSelected(GroundTempType::BuildingSurface); +} + +void GroundTemperatureListView::onShallowClicked() { + m_bsEntry->setSelected(false); + m_shEntry->setSelected(true); + emit typeSelected(GroundTempType::Shallow); +} + +// ───────────────────────────────────────────────────────── +// GroundTemperatureNotPresentView +// ───────────────────────────────────────────────────────── + +GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent) : QWidget(parent) { + auto* layout = new QVBoxLayout(); + layout->setContentsMargins(10, 10, 10, 10); + layout->setSpacing(10); + setLayout(layout); + + m_label = new QLabel(); + m_label->setWordWrap(true); + layout->addWidget(m_label); + + m_addButton = new QPushButton(tr("Add")); + m_addButton->setObjectName("StandardBlueButton"); + layout->addWidget(m_addButton, 0, Qt::AlignLeft); + + layout->addStretch(); + + connect(m_addButton, &QPushButton::clicked, this, [this]() { emit addClicked(m_type); }); +} + +void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString& typeName, model::Model model) { + m_type = type; + m_model = model; + m_label->setText(tr("The %1 object is not present in this model. Click Add to create it.").arg(typeName)); +} + +// ───────────────────────────────────────────────────────── +// SiteGroundTemperatureMonthlyWidget (abstract base) +// ───────────────────────────────────────────────────────── + +SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP, QWidget* parent) : QWidget(parent), m_isIP(isIP) { + auto* mainLayout = new QVBoxLayout(); + mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); + mainLayout->setContentsMargins(10, 10, 10, 10); + mainLayout->setSpacing(20); + setLayout(mainLayout); + + m_titleLabel = new QLabel(); + m_titleLabel->setObjectName("H2"); + mainLayout->addWidget(m_titleLabel); + + auto* gridLayout = new QGridLayout(); + gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setSpacing(10); + mainLayout->addLayout(gridLayout); + + auto* monthHeader = new QLabel(tr("Month")); + monthHeader->setObjectName("H2"); + gridLayout->addWidget(monthHeader, 0, 0); + + auto* tempHeader = new QLabel(tr("Temperature")); + tempHeader->setObjectName("H2"); + gridLayout->addWidget(tempHeader, 0, 1); + + const QStringList monthNames = YearSettingsWidget::months(); + for (int i = 0; i < 12; ++i) { + gridLayout->addWidget(new QLabel(monthNames[i]), i + 1, 0); + m_edits[i] = new OSQuantityEdit2("C", "C", "F", m_isIP); + connect(this, &SiteGroundTemperatureMonthlyWidget::toggleUnitsClicked, m_edits[i], &OSQuantityEdit2::onUnitSystemChange); + m_edits[i]->setFixedWidth(TEMP_EDIT_WIDTH); + gridLayout->addWidget(m_edits[i], i + 1, 1, Qt::AlignLeft); + } + + mainLayout->addStretch(); +} + +void SiteGroundTemperatureMonthlyWidget::detach() { + for (auto* edit : m_edits) { + if (edit) { + edit->unbind(); + } + } +} + +// ───────────────────────────────────────────────────────── +// SiteGroundTemperatureBuildingSurfaceWidget +// ───────────────────────────────────────────────────────── + +SiteGroundTemperatureBuildingSurfaceWidget::SiteGroundTemperatureBuildingSurfaceWidget(bool isIP, QWidget* parent) + : SiteGroundTemperatureMonthlyWidget(isIP, parent) {} + +void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject& obj) { + detach(); + m_obj = obj.cast(); + m_titleLabel->setText("Site:GroundTemperature:BuildingSurface"); + + using BS = model::SiteGroundTemperatureBuildingSurface; + + struct MonthBinding + { + double (BS::*getter)() const; + bool (BS::*setter)(double); + void (BS::*resetter)(); + bool (BS::*defaulted)() const; + }; + + static const std::array month_binders{{ + {&BS::januaryGroundTemperature, &BS::setJanuaryGroundTemperature, &BS::resetJanuaryGroundTemperature, &BS::isJanuaryGroundTemperatureDefaulted}, + {&BS::februaryGroundTemperature, &BS::setFebruaryGroundTemperature, &BS::resetFebruaryGroundTemperature, &BS::isFebruaryGroundTemperatureDefaulted}, + {&BS::marchGroundTemperature, &BS::setMarchGroundTemperature, &BS::resetMarchGroundTemperature, &BS::isMarchGroundTemperatureDefaulted}, + {&BS::aprilGroundTemperature, &BS::setAprilGroundTemperature, &BS::resetAprilGroundTemperature, &BS::isAprilGroundTemperatureDefaulted}, + {&BS::mayGroundTemperature, &BS::setMayGroundTemperature, &BS::resetMayGroundTemperature, &BS::isMayGroundTemperatureDefaulted}, + {&BS::juneGroundTemperature, &BS::setJuneGroundTemperature, &BS::resetJuneGroundTemperature, &BS::isJuneGroundTemperatureDefaulted}, + {&BS::julyGroundTemperature, &BS::setJulyGroundTemperature, &BS::resetJulyGroundTemperature, &BS::isJulyGroundTemperatureDefaulted}, + {&BS::augustGroundTemperature, &BS::setAugustGroundTemperature, &BS::resetAugustGroundTemperature, &BS::isAugustGroundTemperatureDefaulted}, + {&BS::septemberGroundTemperature, &BS::setSeptemberGroundTemperature, &BS::resetSeptemberGroundTemperature, &BS::isSeptemberGroundTemperatureDefaulted}, + {&BS::octoberGroundTemperature, &BS::setOctoberGroundTemperature, &BS::resetOctoberGroundTemperature, &BS::isOctoberGroundTemperatureDefaulted}, + {&BS::novemberGroundTemperature, &BS::setNovemberGroundTemperature, &BS::resetNovemberGroundTemperature, &BS::isNovemberGroundTemperatureDefaulted}, + {&BS::decemberGroundTemperature, &BS::setDecemberGroundTemperature, &BS::resetDecemberGroundTemperature, &BS::isDecemberGroundTemperatureDefaulted}, + }}; + + for (int i = 0; i < 12; ++i) { + const auto& mb = month_binders[i]; + m_edits[i]->bind(m_isIP, *m_obj, DoubleGetter([this, g = mb.getter]() { return (m_obj.get_ptr()->*g)(); }), + boost::optional([this, s = mb.setter](double v) { return (m_obj.get_ptr()->*s)(v); }), + boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, + boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); + } +} + +// ───────────────────────────────────────────────────────── +// SiteGroundTemperatureShallowWidget +// ───────────────────────────────────────────────────────── + +SiteGroundTemperatureShallowWidget::SiteGroundTemperatureShallowWidget(bool isIP, QWidget* parent) + : SiteGroundTemperatureMonthlyWidget(isIP, parent) {} + +void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { + detach(); + m_obj = obj.cast(); + m_titleLabel->setText("Site:GroundTemperature:Shallow"); + + using SH = model::SiteGroundTemperatureShallow; + + struct MonthBinding + { + double (SH::*getter)() const; + bool (SH::*setter)(double); + void (SH::*resetter)(); + bool (SH::*defaulted)() const; + }; + + static const std::array month_binders{{ + {&SH::januarySurfaceGroundTemperature, &SH::setJanuarySurfaceGroundTemperature, &SH::resetJanuarySurfaceGroundTemperature, + &SH::isJanuarySurfaceGroundTemperatureDefaulted}, + {&SH::februarySurfaceGroundTemperature, &SH::setFebruarySurfaceGroundTemperature, &SH::resetFebruarySurfaceGroundTemperature, + &SH::isFebruarySurfaceGroundTemperatureDefaulted}, + {&SH::marchSurfaceGroundTemperature, &SH::setMarchSurfaceGroundTemperature, &SH::resetMarchSurfaceGroundTemperature, + &SH::isMarchSurfaceGroundTemperatureDefaulted}, + {&SH::aprilSurfaceGroundTemperature, &SH::setAprilSurfaceGroundTemperature, &SH::resetAprilSurfaceGroundTemperature, + &SH::isAprilSurfaceGroundTemperatureDefaulted}, + {&SH::maySurfaceGroundTemperature, &SH::setMaySurfaceGroundTemperature, &SH::resetMaySurfaceGroundTemperature, + &SH::isMaySurfaceGroundTemperatureDefaulted}, + {&SH::juneSurfaceGroundTemperature, &SH::setJuneSurfaceGroundTemperature, &SH::resetJuneSurfaceGroundTemperature, + &SH::isJuneSurfaceGroundTemperatureDefaulted}, + {&SH::julySurfaceGroundTemperature, &SH::setJulySurfaceGroundTemperature, &SH::resetJulySurfaceGroundTemperature, + &SH::isJulySurfaceGroundTemperatureDefaulted}, + {&SH::augustSurfaceGroundTemperature, &SH::setAugustSurfaceGroundTemperature, &SH::resetAugustSurfaceGroundTemperature, + &SH::isAugustSurfaceGroundTemperatureDefaulted}, + {&SH::septemberSurfaceGroundTemperature, &SH::setSeptemberSurfaceGroundTemperature, &SH::resetSeptemberSurfaceGroundTemperature, + &SH::isSeptemberSurfaceGroundTemperatureDefaulted}, + {&SH::octoberSurfaceGroundTemperature, &SH::setOctoberSurfaceGroundTemperature, &SH::resetOctoberSurfaceGroundTemperature, + &SH::isOctoberSurfaceGroundTemperatureDefaulted}, + {&SH::novemberSurfaceGroundTemperature, &SH::setNovemberSurfaceGroundTemperature, &SH::resetNovemberSurfaceGroundTemperature, + &SH::isNovemberSurfaceGroundTemperatureDefaulted}, + {&SH::decemberSurfaceGroundTemperature, &SH::setDecemberSurfaceGroundTemperature, &SH::resetDecemberSurfaceGroundTemperature, + &SH::isDecemberSurfaceGroundTemperatureDefaulted}, + }}; + + for (int i = 0; i < 12; ++i) { + const auto& mb = month_binders[i]; + m_edits[i]->bind(m_isIP, *m_obj, DoubleGetter([this, g = mb.getter]() { return (m_obj.get_ptr()->*g)(); }), + boost::optional([this, s = mb.setter](double v) { return (m_obj.get_ptr()->*s)(v); }), + boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, + boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); + } +} + +// ───────────────────────────────────────────────────────── +// GroundTemperatureView +// ───────────────────────────────────────────────────────── + +GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& model, QWidget* parent) + : QWidget(parent), m_model(model), m_isIP(isIP) { + auto* mainLayout = new QHBoxLayout(); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + setLayout(mainLayout); + + // Left pane (fixed width) + auto* leftPane = new QWidget(); + leftPane->setFixedWidth(200); + auto* leftLayout = new QVBoxLayout(); + leftLayout->setContentsMargins(0, 0, 0, 0); + leftLayout->setSpacing(0); + leftPane->setLayout(leftLayout); + + m_listView = new GroundTemperatureListView(leftPane); + leftLayout->addWidget(m_listView); + leftLayout->addStretch(); + + mainLayout->addWidget(leftPane); + + // Right pane: stacked widget + m_rightStack = new QStackedWidget(); + mainLayout->addWidget(m_rightStack, 1); + + m_notPresentView = new GroundTemperatureNotPresentView(); + m_rightStack->addWidget(m_notPresentView); // index 0 + + m_bsView = new SiteGroundTemperatureBuildingSurfaceWidget(isIP); + m_rightStack->addWidget(m_bsView); // index 1 + + m_shView = new SiteGroundTemperatureShallowWidget(isIP); + m_rightStack->addWidget(m_shView); // index 2 + + connect(m_listView, &GroundTemperatureListView::typeSelected, this, &GroundTemperatureView::onTypeSelected); + connect(m_notPresentView, &GroundTemperatureNotPresentView::addClicked, this, &GroundTemperatureView::onObjectCreated); + + // Forward unit toggle to sub-views (signal-to-signal) + connect(this, &GroundTemperatureView::toggleUnitsClicked, m_bsView, &SiteGroundTemperatureBuildingSurfaceWidget::toggleUnitsClicked); + connect(this, &GroundTemperatureView::toggleUnitsClicked, m_shView, &SiteGroundTemperatureShallowWidget::toggleUnitsClicked); + + // Auto-select first entry after the event loop starts (triggers both visual selection and right-pane update) + QTimer::singleShot(0, this, [this]() { m_listView->selectFirst(); }); +} + +void GroundTemperatureView::onTypeSelected(GroundTempType type) { + if (type == GroundTempType::BuildingSurface) { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_bsView->attach(*opt); + m_rightStack->setCurrentIndex(1); + } else { + m_notPresentView->setType(type, tr("Site:GroundTemperature:BuildingSurface"), m_model); + m_rightStack->setCurrentIndex(0); + } + } else { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_shView->attach(*opt); + m_rightStack->setCurrentIndex(2); + } else { + m_notPresentView->setType(type, tr("Site:GroundTemperature:Shallow"), m_model); + m_rightStack->setCurrentIndex(0); + } + } +} + +void GroundTemperatureView::onObjectCreated(GroundTempType type) { + if (type == GroundTempType::BuildingSurface) { + auto obj = m_model.getUniqueModelObject(); + m_bsView->attach(obj); + m_rightStack->setCurrentIndex(1); + } else { + auto obj = m_model.getUniqueModelObject(); + m_shView->attach(obj); + m_rightStack->setCurrentIndex(2); + } +} + +} // namespace openstudio diff --git a/src/openstudio_lib/GroundTemperatureWidget.hpp b/src/openstudio_lib/GroundTemperatureWidget.hpp new file mode 100644 index 000000000..61d144533 --- /dev/null +++ b/src/openstudio_lib/GroundTemperatureWidget.hpp @@ -0,0 +1,177 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#ifndef OPENSTUDIO_GROUNDTEMPERATUREWIDGET_HPP +#define OPENSTUDIO_GROUNDTEMPERATUREWIDGET_HPP + +#include +#include +#include +#include + +#include + +#include + +class QLabel; +class QPushButton; +class QStackedWidget; + +namespace openstudio { + +class OSQuantityEdit2; + +enum class GroundTempType +{ + BuildingSurface, + Shallow +}; + +/** A single clickable entry in the left-hand list (ScheduleTabDefault style). */ +class SiteGroundTemperatureEntry : public QWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureEntry(const QString& label, QWidget* parent = nullptr); + + void setSelected(bool selected); + + signals: + void clicked(); + + protected: + void paintEvent(QPaintEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void leaveEvent(QEvent* event) override; + + private: + bool m_mouseDown = false; + bool m_hovering = false; + bool m_selected = false; + QLabel* m_label = nullptr; +}; + +/** Left-hand list: two SiteGroundTemperatureEntry items. */ +class GroundTemperatureListView : public QWidget +{ + Q_OBJECT + + public: + explicit GroundTemperatureListView(QWidget* parent = nullptr); + + void selectFirst(); + + signals: + void typeSelected(openstudio::GroundTempType type); + + private slots: + void onBuildingSurfaceClicked(); + void onShallowClicked(); + + private: + SiteGroundTemperatureEntry* m_bsEntry = nullptr; + SiteGroundTemperatureEntry* m_shEntry = nullptr; +}; + +/** "Not present" right pane: message label + Add button. */ +class GroundTemperatureNotPresentView : public QWidget +{ + Q_OBJECT + + public: + explicit GroundTemperatureNotPresentView(QWidget* parent = nullptr); + + void setType(GroundTempType type, const QString& typeName, model::Model model); + + signals: + void addClicked(openstudio::GroundTempType type); + + private: + QLabel* m_label = nullptr; + QPushButton* m_addButton = nullptr; + GroundTempType m_type = GroundTempType::BuildingSurface; + model::Model m_model; +}; + +/** Abstract base for widgets showing 12 monthly temperature fields. */ +class SiteGroundTemperatureMonthlyWidget : public QWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureMonthlyWidget(bool isIP, QWidget* parent = nullptr); + virtual ~SiteGroundTemperatureMonthlyWidget() = default; + + virtual void attach(const model::ModelObject& obj) = 0; + void detach(); + + signals: + void toggleUnitsClicked(bool displayIP); + + protected: + bool m_isIP; + QLabel* m_titleLabel = nullptr; + std::array m_edits{}; +}; + +/** Concrete: Site:GroundTemperature:BuildingSurface */ +class SiteGroundTemperatureBuildingSurfaceWidget : public SiteGroundTemperatureMonthlyWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureBuildingSurfaceWidget(bool isIP, QWidget* parent = nullptr); + + void attach(const model::ModelObject& obj) override; + + private: + boost::optional m_obj; +}; + +/** Concrete: Site:GroundTemperature:Shallow */ +class SiteGroundTemperatureShallowWidget : public SiteGroundTemperatureMonthlyWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureShallowWidget(bool isIP, QWidget* parent = nullptr); + + void attach(const model::ModelObject& obj) override; + + private: + boost::optional m_obj; +}; + +/** Top-level widget for the Ground Temperatures sub-tab. */ +class GroundTemperatureView : public QWidget +{ + Q_OBJECT + + public: + explicit GroundTemperatureView(bool isIP, const model::Model& model, QWidget* parent = nullptr); + + signals: + void toggleUnitsClicked(bool displayIP); + + private slots: + void onTypeSelected(openstudio::GroundTempType type); + void onObjectCreated(openstudio::GroundTempType type); + + private: + model::Model m_model; + bool m_isIP; + GroundTemperatureListView* m_listView = nullptr; + GroundTemperatureNotPresentView* m_notPresentView = nullptr; + SiteGroundTemperatureBuildingSurfaceWidget* m_bsView = nullptr; + SiteGroundTemperatureShallowWidget* m_shView = nullptr; + QStackedWidget* m_rightStack = nullptr; +}; + +} // namespace openstudio + +#endif // OPENSTUDIO_GROUNDTEMPERATUREWIDGET_HPP diff --git a/src/openstudio_lib/LocationTabController.cpp b/src/openstudio_lib/LocationTabController.cpp index 169cfab9f..b157aa289 100644 --- a/src/openstudio_lib/LocationTabController.cpp +++ b/src/openstudio_lib/LocationTabController.cpp @@ -7,8 +7,7 @@ #include "LifeCycleCostsTabView.hpp" #include "LocationTabView.hpp" -#include "SiteGroundTemperatureController.hpp" -#include "SiteGroundTemperatureMonthlyWidget.hpp" +#include "GroundTemperatureWidget.hpp" #include "UtilityBillsView.hpp" #include "UtilityBillsController.hpp" @@ -104,10 +103,10 @@ void LocationTabController::setSubTab(int index) { break; } case 3: { - auto* groundTemperaturesController = new SiteGroundTemperatureController(m_isIP, m_model); - connect(this, &LocationTabController::toggleUnitsClicked, groundTemperaturesController, &SiteGroundTemperatureController::toggleUnitsClicked); - this->mainContentWidget()->setSubTab(groundTemperaturesController->subTabView()); - m_currentView = groundTemperaturesController->subTabView(); + auto* view = new GroundTemperatureView(m_isIP, m_model); + connect(this, &LocationTabController::toggleUnitsClicked, view, &GroundTemperatureView::toggleUnitsClicked); + this->mainContentWidget()->setSubTab(view); + m_currentView = view; break; } default: diff --git a/src/openstudio_lib/SiteGroundTemperatureController.cpp b/src/openstudio_lib/SiteGroundTemperatureController.cpp deleted file mode 100644 index a9d5df162..000000000 --- a/src/openstudio_lib/SiteGroundTemperatureController.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/*********************************************************************************************************************** -* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. -* See also https://openstudiocoalition.org/about/software_license/ -***********************************************************************************************************************/ - -#include "SiteGroundTemperatureController.hpp" - -#include "SiteGroundTemperatureMonthlyWidget.hpp" -#include "OSItemSelectorButtons.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -namespace openstudio { - -SiteGroundTemperatureController::SiteGroundTemperatureController(bool isIP, const model::Model& model) - : ModelSubTabController(new SiteGroundTemperatureMonthlyView(isIP, model), model) { - subTabView()->itemSelectorButtons()->hideAddButton(); - subTabView()->itemSelectorButtons()->hideRemoveButton(); - subTabView()->itemSelectorButtons()->hideCopyButton(); - subTabView()->itemSelectorButtons()->hidePurgeButton(); - subTabView()->itemSelectorButtons()->hideDropZone(); -} - -void SiteGroundTemperatureController::onAddObject(const openstudio::IddObjectType& iddObjectType) { - model::Model model = this->model(); - if (iddObjectType == model::SiteGroundTemperatureBuildingSurface::iddObjectType()) { - model.getUniqueModelObject(); - } else if (iddObjectType == model::SiteGroundTemperatureShallow::iddObjectType()) { - model.getUniqueModelObject(); - } -} - -void SiteGroundTemperatureController::onCopyObject(const openstudio::model::ModelObject& /*modelObject*/) { - // not applicable for unique objects -} - -void SiteGroundTemperatureController::onRemoveObject(openstudio::model::ModelObject modelObject) { - modelObject.remove(); -} - -void SiteGroundTemperatureController::onReplaceObject(openstudio::model::ModelObject /*modelObject*/, const OSItemId& /*replacementItemId*/) { - // not yet implemented -} - -void SiteGroundTemperatureController::onPurgeObjects(const openstudio::IddObjectType& /*iddObjectType*/) { - // purge button is disabled -} - -void SiteGroundTemperatureController::onDrop(const OSItemId& /*itemId*/) { - // drop zone is hidden -} - -void SiteGroundTemperatureController::onInspectItem(OSItem* item) { - subTabView()->inspectorView()->selectItem(item); -} - -} // namespace openstudio diff --git a/src/openstudio_lib/SiteGroundTemperatureController.hpp b/src/openstudio_lib/SiteGroundTemperatureController.hpp deleted file mode 100644 index 32884bbc6..000000000 --- a/src/openstudio_lib/SiteGroundTemperatureController.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/*********************************************************************************************************************** -* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. -* See also https://openstudiocoalition.org/about/software_license/ -***********************************************************************************************************************/ - -#ifndef OPENSTUDIO_SITEGROUNDTEMPERATURECONTROLLER_HPP -#define OPENSTUDIO_SITEGROUNDTEMPERATURECONTROLLER_HPP - -#include "ModelSubTabController.hpp" - -namespace openstudio { - -/** Controller for the "Ground Temperatures" sub-tab of the Location tab. - * Manages all Site:GroundTemperature:* object types and delegates to the - * appropriate sub-tab view (e.g. SiteGroundTemperatureMonthlyView for objects - * with 12 monthly values). */ -class SiteGroundTemperatureController : public ModelSubTabController -{ - Q_OBJECT - - public: - explicit SiteGroundTemperatureController(bool isIP, const model::Model& model); - - virtual ~SiteGroundTemperatureController() = default; - - protected: - virtual void onAddObject(const openstudio::IddObjectType& iddObjectType) override; - virtual void onCopyObject(const openstudio::model::ModelObject& modelObject) override; - virtual void onRemoveObject(openstudio::model::ModelObject modelObject) override; - virtual void onReplaceObject(openstudio::model::ModelObject modelObject, const OSItemId& replacementItemId) override; - virtual void onPurgeObjects(const openstudio::IddObjectType& iddObjectType) override; - virtual void onDrop(const OSItemId& itemId) override; - virtual void onInspectItem(OSItem* item) override; - - private: - bool m_isIP; -}; - -} // namespace openstudio - -#endif // OPENSTUDIO_SITEGROUNDTEMPERATURECONTROLLER_HPP diff --git a/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.cpp b/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.cpp deleted file mode 100644 index 9ad14da36..000000000 --- a/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/*********************************************************************************************************************** -* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. -* See also https://openstudiocoalition.org/about/software_license/ -***********************************************************************************************************************/ - -#include "SiteGroundTemperatureMonthlyWidget.hpp" - -#include "ModelObjectItem.hpp" -#include "OSItemSelectorButtons.hpp" - -#include "YearSettingsWidget.hpp" - -#include "../shared_gui_components/OSQuantityEdit.hpp" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#define TEMP_EDIT_WIDTH 90 - -namespace openstudio { - -// SiteGroundTemperatureMonthlyItem - -SiteGroundTemperatureMonthlyItem::SiteGroundTemperatureMonthlyItem(const model::ModelObject& obj, const QString& displayName, QWidget* parent) - : OSItem(modelObjectToItemId(obj, false), OSItemType::ListItem, parent), m_modelObject(obj) { - setText(displayName); -} - -model::ModelObject SiteGroundTemperatureMonthlyItem::modelObject() const { - return m_modelObject; -} - -bool SiteGroundTemperatureMonthlyItem::equal(const OSItem* other) const { - if (const auto* o = qobject_cast(other)) { - return m_modelObject.handle() == o->m_modelObject.handle(); - } - return false; -} - -// SiteGroundTemperatureMonthlyListView - -SiteGroundTemperatureMonthlyListView::SiteGroundTemperatureMonthlyListView(const model::Model& model, QWidget* parent) - : OSItemSelector(parent) { - auto* layout = new QVBoxLayout(); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - setLayout(layout); - - // getUniqueModelObject is non-const (creates the object if absent) - model::Model m = model; - auto bs = m.getUniqueModelObject(); - auto sh = m.getUniqueModelObject(); - - m_bsItem = new SiteGroundTemperatureMonthlyItem(bs, tr("Building Surface Ground Temperatures"), this); - auto* shItem = new SiteGroundTemperatureMonthlyItem(sh, tr("Shallow Ground Temperatures"), this); - - connect(m_bsItem, &OSItem::itemClicked, this, &SiteGroundTemperatureMonthlyListView::onItemClicked); - connect(shItem, &OSItem::itemClicked, this, &SiteGroundTemperatureMonthlyListView::onItemClicked); - - layout->addWidget(m_bsItem); - layout->addWidget(shItem); - layout->addStretch(); - - QTimer::singleShot(0, this, &SiteGroundTemperatureMonthlyListView::selectFirst); -} - -OSItem* SiteGroundTemperatureMonthlyListView::selectedItem() const { - return m_selectedItem; -} - -void SiteGroundTemperatureMonthlyListView::onItemClicked(OSItem* item) { - if (m_selectedItem) { - m_selectedItem->setSelected(false); - } - m_selectedItem = item; - if (m_selectedItem) { - m_selectedItem->setSelected(true); - } - emit itemSelected(item); -} - -void SiteGroundTemperatureMonthlyListView::selectFirst() { - if (m_bsItem) { - onItemClicked(m_bsItem); - } -} - -// SiteGroundTemperatureMonthlyView - -SiteGroundTemperatureMonthlyView::SiteGroundTemperatureMonthlyView(bool isIP, const model::Model& model, QWidget* parent) - : ModelSubTabView(new SiteGroundTemperatureMonthlyListView(model, parent), - new SiteGroundTemperatureMonthlyInspectorView(isIP, model, parent), false, parent) {} - -// SiteGroundTemperatureMonthlyInspectorView - -SiteGroundTemperatureMonthlyInspectorView::SiteGroundTemperatureMonthlyInspectorView(bool isIP, const model::Model& model, QWidget* parent) - : ModelObjectInspectorView(model, true, parent), m_isIP(isIP), m_hiddenWidgetIndex(0), m_monthlyWidgetIndex(0) { - createWidgets(); -} - -SiteGroundTemperatureMonthlyInspectorView::~SiteGroundTemperatureMonthlyInspectorView() { - detach(); -} - -void SiteGroundTemperatureMonthlyInspectorView::createWidgets() { - auto* hiddenWidget = new QWidget(); - m_hiddenWidgetIndex = this->stackedWidget()->insertWidget(0, hiddenWidget); - - auto* monthlyWidget = new QWidget(); - m_monthlyWidgetIndex = this->stackedWidget()->addWidget(monthlyWidget); - - auto* mainLayout = new QVBoxLayout(); - mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); - mainLayout->setContentsMargins(10, 10, 10, 10); - mainLayout->setSpacing(20); - monthlyWidget->setLayout(mainLayout); - - m_titleLabel = new QLabel(); - m_titleLabel->setObjectName("H2"); - mainLayout->addWidget(m_titleLabel); - - auto* gridLayout = new QGridLayout(); - gridLayout->setContentsMargins(0, 0, 0, 0); - gridLayout->setSpacing(10); - mainLayout->addLayout(gridLayout); - - // Column headers - auto* monthHeader = new QLabel(tr("Month")); - monthHeader->setObjectName("H2"); - gridLayout->addWidget(monthHeader, 0, 0); - - m_temperatureHeader = new QLabel(tr("Temperature")); - m_temperatureHeader->setObjectName("H2"); - gridLayout->addWidget(m_temperatureHeader, 0, 1); - - // One row per month, built from the translated month list - const QStringList monthNames = YearSettingsWidget::months(); - - for (int i = 0; i < 12; ++i) { - gridLayout->addWidget(new QLabel(monthNames[i]), i + 1, 0); - m_edits[i] = new OSQuantityEdit2("C", "C", "F", m_isIP); - connect(this, &SiteGroundTemperatureMonthlyInspectorView::toggleUnitsClicked, m_edits[i], &OSQuantityEdit2::onUnitSystemChange); - m_edits[i]->setFixedWidth(TEMP_EDIT_WIDTH); - gridLayout->addWidget(m_edits[i], i + 1, 1, Qt::AlignLeft); - } - - mainLayout->addStretch(); -} - -void SiteGroundTemperatureMonthlyInspectorView::attachBuildingSurface(const model::SiteGroundTemperatureBuildingSurface& obj) { - m_buildingSurface = obj; - m_titleLabel->setText("Site:GroundTemperature:BuildingSurface"); - - using BS = model::SiteGroundTemperatureBuildingSurface; - - struct MonthBinding - { - double (BS::*getter)() const; - bool (BS::*setter)(double); - void (BS::*resetter)(); - bool (BS::*defaulted)() const; - }; - - static const std::array month_binders{{ - { - &BS::januaryGroundTemperature, - &BS::setJanuaryGroundTemperature, - &BS::resetJanuaryGroundTemperature, - &BS::isJanuaryGroundTemperatureDefaulted, - }, - { - &BS::februaryGroundTemperature, - &BS::setFebruaryGroundTemperature, - &BS::resetFebruaryGroundTemperature, - &BS::isFebruaryGroundTemperatureDefaulted, - }, - { - &BS::marchGroundTemperature, - &BS::setMarchGroundTemperature, - &BS::resetMarchGroundTemperature, - &BS::isMarchGroundTemperatureDefaulted, - }, - { - &BS::aprilGroundTemperature, - &BS::setAprilGroundTemperature, - &BS::resetAprilGroundTemperature, - &BS::isAprilGroundTemperatureDefaulted, - }, - { - &BS::mayGroundTemperature, - &BS::setMayGroundTemperature, - &BS::resetMayGroundTemperature, - &BS::isMayGroundTemperatureDefaulted, - }, - { - &BS::juneGroundTemperature, - &BS::setJuneGroundTemperature, - &BS::resetJuneGroundTemperature, - &BS::isJuneGroundTemperatureDefaulted, - }, - { - &BS::julyGroundTemperature, - &BS::setJulyGroundTemperature, - &BS::resetJulyGroundTemperature, - &BS::isJulyGroundTemperatureDefaulted, - }, - { - &BS::augustGroundTemperature, - &BS::setAugustGroundTemperature, - &BS::resetAugustGroundTemperature, - &BS::isAugustGroundTemperatureDefaulted, - }, - { - &BS::septemberGroundTemperature, - &BS::setSeptemberGroundTemperature, - &BS::resetSeptemberGroundTemperature, - &BS::isSeptemberGroundTemperatureDefaulted, - }, - { - &BS::octoberGroundTemperature, - &BS::setOctoberGroundTemperature, - &BS::resetOctoberGroundTemperature, - &BS::isOctoberGroundTemperatureDefaulted, - }, - { - &BS::novemberGroundTemperature, - &BS::setNovemberGroundTemperature, - &BS::resetNovemberGroundTemperature, - &BS::isNovemberGroundTemperatureDefaulted, - }, - { - &BS::decemberGroundTemperature, - &BS::setDecemberGroundTemperature, - &BS::resetDecemberGroundTemperature, - &BS::isDecemberGroundTemperatureDefaulted, - }, - }}; - - for (int i = 0; i < 12; ++i) { - const auto& m = month_binders[i]; - m_edits[i]->bind(m_isIP, *m_buildingSurface, DoubleGetter([this, g = m.getter]() { return (m_buildingSurface.get_ptr()->*g)(); }), - boost::optional([this, s = m.setter](double v) { return (m_buildingSurface.get_ptr()->*s)(v); }), - boost::optional([this, r = m.resetter]() { (m_buildingSurface.get_ptr()->*r)(); }), boost::none, boost::none, - boost::optional([this, d = m.defaulted]() { return (m_buildingSurface.get_ptr()->*d)(); })); - } - - this->stackedWidget()->setCurrentIndex(m_monthlyWidgetIndex); -} - -void SiteGroundTemperatureMonthlyInspectorView::attachShallow(const model::SiteGroundTemperatureShallow& obj) { - m_shallow = obj; - m_titleLabel->setText("Site:GroundTemperature:Shallow"); - - using SH = model::SiteGroundTemperatureShallow; - - struct MonthBinding - { - double (SH::*getter)() const; - bool (SH::*setter)(double); - void (SH::*resetter)(); - bool (SH::*defaulted)() const; - }; - - static const std::array month_binders{{ - { - &SH::januarySurfaceGroundTemperature, - &SH::setJanuarySurfaceGroundTemperature, - &SH::resetJanuarySurfaceGroundTemperature, - &SH::isJanuarySurfaceGroundTemperatureDefaulted, - }, - { - &SH::februarySurfaceGroundTemperature, - &SH::setFebruarySurfaceGroundTemperature, - &SH::resetFebruarySurfaceGroundTemperature, - &SH::isFebruarySurfaceGroundTemperatureDefaulted, - }, - { - &SH::marchSurfaceGroundTemperature, - &SH::setMarchSurfaceGroundTemperature, - &SH::resetMarchSurfaceGroundTemperature, - &SH::isMarchSurfaceGroundTemperatureDefaulted, - }, - { - &SH::aprilSurfaceGroundTemperature, - &SH::setAprilSurfaceGroundTemperature, - &SH::resetAprilSurfaceGroundTemperature, - &SH::isAprilSurfaceGroundTemperatureDefaulted, - }, - { - &SH::maySurfaceGroundTemperature, - &SH::setMaySurfaceGroundTemperature, - &SH::resetMaySurfaceGroundTemperature, - &SH::isMaySurfaceGroundTemperatureDefaulted, - }, - { - &SH::juneSurfaceGroundTemperature, - &SH::setJuneSurfaceGroundTemperature, - &SH::resetJuneSurfaceGroundTemperature, - &SH::isJuneSurfaceGroundTemperatureDefaulted, - }, - { - &SH::julySurfaceGroundTemperature, - &SH::setJulySurfaceGroundTemperature, - &SH::resetJulySurfaceGroundTemperature, - &SH::isJulySurfaceGroundTemperatureDefaulted, - }, - { - &SH::augustSurfaceGroundTemperature, - &SH::setAugustSurfaceGroundTemperature, - &SH::resetAugustSurfaceGroundTemperature, - &SH::isAugustSurfaceGroundTemperatureDefaulted, - }, - { - &SH::septemberSurfaceGroundTemperature, - &SH::setSeptemberSurfaceGroundTemperature, - &SH::resetSeptemberSurfaceGroundTemperature, - &SH::isSeptemberSurfaceGroundTemperatureDefaulted, - }, - { - &SH::octoberSurfaceGroundTemperature, - &SH::setOctoberSurfaceGroundTemperature, - &SH::resetOctoberSurfaceGroundTemperature, - &SH::isOctoberSurfaceGroundTemperatureDefaulted, - }, - { - &SH::novemberSurfaceGroundTemperature, - &SH::setNovemberSurfaceGroundTemperature, - &SH::resetNovemberSurfaceGroundTemperature, - &SH::isNovemberSurfaceGroundTemperatureDefaulted, - }, - { - &SH::decemberSurfaceGroundTemperature, - &SH::setDecemberSurfaceGroundTemperature, - &SH::resetDecemberSurfaceGroundTemperature, - &SH::isDecemberSurfaceGroundTemperatureDefaulted, - }, - }}; - - for (int i = 0; i < 12; ++i) { - const auto& m = month_binders[i]; - m_edits[i]->bind(m_isIP, *m_shallow, DoubleGetter([this, g = m.getter]() { return (m_shallow.get_ptr()->*g)(); }), - boost::optional([this, s = m.setter](double v) { return (m_shallow.get_ptr()->*s)(v); }), - boost::optional([this, r = m.resetter]() { (m_shallow.get_ptr()->*r)(); }), boost::none, boost::none, - boost::optional([this, d = m.defaulted]() { return (m_shallow.get_ptr()->*d)(); })); - } - - this->stackedWidget()->setCurrentIndex(m_monthlyWidgetIndex); -} - -void SiteGroundTemperatureMonthlyInspectorView::detach() { - this->stackedWidget()->setCurrentIndex(m_hiddenWidgetIndex); - - for (auto* edit : m_edits) { - if (edit) { - edit->unbind(); - } - } - - m_buildingSurface = boost::none; - m_shallow = boost::none; -} - -void SiteGroundTemperatureMonthlyInspectorView::onSelectItem(OSItem* item) { - auto* monthlyItem = qobject_cast(item); - OS_ASSERT(monthlyItem); - onSelectModelObject(monthlyItem->modelObject()); -} - -void SiteGroundTemperatureMonthlyInspectorView::onClearSelection() { - ModelObjectInspectorView::onClearSelection(); - detach(); -} - -void SiteGroundTemperatureMonthlyInspectorView::onSelectModelObject(const openstudio::model::ModelObject& modelObject) { - detach(); - if (auto obj = modelObject.optionalCast()) { - attachBuildingSurface(*obj); - } else if (auto obj = modelObject.optionalCast()) { - attachShallow(*obj); - } -} - -void SiteGroundTemperatureMonthlyInspectorView::onUpdate() { - // nothing to refresh -} - -} // namespace openstudio diff --git a/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.hpp b/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.hpp deleted file mode 100644 index a648f03ac..000000000 --- a/src/openstudio_lib/SiteGroundTemperatureMonthlyWidget.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/*********************************************************************************************************************** -* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. -* See also https://openstudiocoalition.org/about/software_license/ -***********************************************************************************************************************/ - -#ifndef OPENSTUDIO_SITEGROUNDTEMPERATUREMONTHLYWIDGET_HPP -#define OPENSTUDIO_SITEGROUNDTEMPERATUREMONTHLYWIDGET_HPP - -#include "ModelObjectInspectorView.hpp" -#include "ModelSubTabView.hpp" -#include "OSItem.hpp" -#include "OSItemSelector.hpp" - -#include -#include -#include -#include - -#include - -#include - -namespace openstudio { - -class OSQuantityEdit2; - -/** A single entry in the left-hand list for one Site:GroundTemperature:* unique object. - * Uses a caller-supplied display name instead of name().get() (which would crash for - * objects that have no Name IDD field). */ -class SiteGroundTemperatureMonthlyItem : public OSItem -{ - Q_OBJECT - - public: - SiteGroundTemperatureMonthlyItem(const model::ModelObject& obj, const QString& displayName, QWidget* parent = nullptr); - virtual ~SiteGroundTemperatureMonthlyItem() = default; - - model::ModelObject modelObject() const; - - bool equal(const OSItem* other) const override; - - private: - model::ModelObject m_modelObject; -}; - -/** Left-hand picker list for Site:GroundTemperature objects that carry 12 monthly values. - * Directly creates SiteGroundTemperatureMonthlyItem entries — bypasses ModelObjectItem / - * makeItem so that objects without a Name IDD field do not crash. */ -class SiteGroundTemperatureMonthlyListView : public OSItemSelector -{ - Q_OBJECT - - public: - SiteGroundTemperatureMonthlyListView(const model::Model& model, QWidget* parent = nullptr); - virtual ~SiteGroundTemperatureMonthlyListView() = default; - - OSItem* selectedItem() const override; - - private slots: - void onItemClicked(OSItem* item); - void selectFirst(); - - private: - OSItem* m_selectedItem = nullptr; - SiteGroundTemperatureMonthlyItem* m_bsItem = nullptr; -}; - -class SiteGroundTemperatureMonthlyView : public ModelSubTabView -{ - Q_OBJECT - - public: - SiteGroundTemperatureMonthlyView(bool isIP, const model::Model& model, QWidget* parent = nullptr); - - virtual ~SiteGroundTemperatureMonthlyView() = default; -}; - -class SiteGroundTemperatureMonthlyInspectorView : public ModelObjectInspectorView -{ - Q_OBJECT - - public: - SiteGroundTemperatureMonthlyInspectorView(bool isIP, const model::Model& model, QWidget* parent = nullptr); - - virtual ~SiteGroundTemperatureMonthlyInspectorView(); - - protected: - virtual void onSelectItem(OSItem* item) override; - virtual void onClearSelection() override; - virtual void onSelectModelObject(const openstudio::model::ModelObject& modelObject) override; - virtual void onUpdate() override; - - private: - void createWidgets(); - void attachBuildingSurface(const model::SiteGroundTemperatureBuildingSurface& obj); - void attachShallow(const model::SiteGroundTemperatureShallow& obj); - void detach(); - - bool m_isIP; - - boost::optional m_buildingSurface; - boost::optional m_shallow; - - int m_hiddenWidgetIndex; - int m_monthlyWidgetIndex; - - QLabel* m_titleLabel = nullptr; - QLabel* m_temperatureHeader = nullptr; - - std::array m_edits{}; -}; - -} // namespace openstudio - -#endif // OPENSTUDIO_SITEGROUNDTEMPERATUREMONTHLYWIDGET_HPP From 40d40117ea1811dc93c2203db990e5c7c7b63ab9 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 11:34:07 +0100 Subject: [PATCH 07/90] Break into sevreral files and rename classes for clarity. --- src/openstudio_lib/CMakeLists.txt | 9 +- ...GroundTemperatureMonthlyInspectorView.cpp} | 240 +----------------- .../GroundTemperatureMonthlyInspectorView.hpp | 74 ++++++ src/openstudio_lib/GroundTemperatureView.cpp | 238 +++++++++++++++++ ...reWidget.hpp => GroundTemperatureView.hpp} | 76 +----- src/openstudio_lib/LocationTabController.cpp | 2 +- 6 files changed, 339 insertions(+), 300 deletions(-) rename src/openstudio_lib/{GroundTemperatureWidget.cpp => GroundTemperatureMonthlyInspectorView.cpp} (51%) create mode 100644 src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp create mode 100644 src/openstudio_lib/GroundTemperatureView.cpp rename src/openstudio_lib/{GroundTemperatureWidget.hpp => GroundTemperatureView.hpp} (56%) diff --git a/src/openstudio_lib/CMakeLists.txt b/src/openstudio_lib/CMakeLists.txt index 61b69c197..65a9bb9b6 100644 --- a/src/openstudio_lib/CMakeLists.txt +++ b/src/openstudio_lib/CMakeLists.txt @@ -278,8 +278,10 @@ set(${target_name}_SRC ServiceWaterScene.hpp SimSettingsTabController.cpp SimSettingsTabController.hpp - GroundTemperatureWidget.cpp - GroundTemperatureWidget.hpp + GroundTemperatureMonthlyInspectorView.cpp + GroundTemperatureMonthlyInspectorView.hpp + GroundTemperatureView.cpp + GroundTemperatureView.hpp SimSettingsTabView.cpp SimSettingsTabView.hpp SimSettingsView.cpp @@ -637,7 +639,8 @@ set(${target_name}_moc SimSettingsTabController.hpp SimSettingsTabView.hpp SimSettingsView.hpp - GroundTemperatureWidget.hpp + GroundTemperatureMonthlyInspectorView.hpp + GroundTemperatureView.hpp SpaceLoadInstancesWidget.hpp SpacesDaylightingGridView.hpp SpacesInteriorPartitionsGridView.hpp diff --git a/src/openstudio_lib/GroundTemperatureWidget.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp similarity index 51% rename from src/openstudio_lib/GroundTemperatureWidget.cpp rename to src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 33dd5d35c..ba1543393 100644 --- a/src/openstudio_lib/GroundTemperatureWidget.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -3,167 +3,24 @@ * See also https://openstudiocoalition.org/about/software_license/ ***********************************************************************************************************************/ -#include "GroundTemperatureWidget.hpp" +#include "GroundTemperatureMonthlyInspectorView.hpp" #include "YearSettingsWidget.hpp" #include "../shared_gui_components/OSQuantityEdit.hpp" -#include #include #include -#include - -#include #include -#include #include -#include -#include -#include -#include #include -#include -#include #include #define TEMP_EDIT_WIDTH 90 namespace openstudio { -// ───────────────────────────────────────────────────────── -// SiteGroundTemperatureEntry -// ───────────────────────────────────────────────────────── - -SiteGroundTemperatureEntry::SiteGroundTemperatureEntry(const QString& label, QWidget* parent) : QWidget(parent) { - setFixedHeight(25); - setMouseTracking(true); - - auto* layout = new QHBoxLayout(); - layout->setContentsMargins(10, 0, 0, 0); - setLayout(layout); - - m_label = new QLabel(label); - m_label->setMouseTracking(true); - layout->addWidget(m_label); -} - -void SiteGroundTemperatureEntry::setSelected(bool selected) { - m_selected = selected; - update(); -} - -void SiteGroundTemperatureEntry::paintEvent(QPaintEvent* /*event*/) { - QStyleOption opt; - opt.initFrom(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - - if (m_hovering || m_selected) { - p.setBrush(QBrush(QColor(207, 207, 207))); - p.setPen(Qt::NoPen); - p.drawRect(0, 0, size().width() - 1, size().height() - 1); - } - - p.setPen(Qt::SolidLine); - p.setBrush(QBrush(QColor(Qt::black))); - p.drawLine(0, 0, size().width(), 0); - p.drawLine(0, size().height() - 1, size().width(), size().height() - 1); -} - -void SiteGroundTemperatureEntry::mousePressEvent(QMouseEvent* event) { - m_mouseDown = true; - event->accept(); -} - -void SiteGroundTemperatureEntry::mouseReleaseEvent(QMouseEvent* event) { - if (m_mouseDown) { - emit clicked(); - } - m_mouseDown = false; - event->accept(); -} - -void SiteGroundTemperatureEntry::mouseMoveEvent(QMouseEvent* event) { - m_hovering = true; - update(); - event->accept(); -} - -void SiteGroundTemperatureEntry::leaveEvent(QEvent* event) { - m_mouseDown = false; - m_hovering = false; - update(); - event->accept(); -} - -// ───────────────────────────────────────────────────────── -// GroundTemperatureListView -// ───────────────────────────────────────────────────────── - -GroundTemperatureListView::GroundTemperatureListView(QWidget* parent) : QWidget(parent) { - auto* layout = new QVBoxLayout(); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - setLayout(layout); - - m_bsEntry = new SiteGroundTemperatureEntry(tr("Building Surface Ground Temperatures"), this); - m_shEntry = new SiteGroundTemperatureEntry(tr("Shallow Ground Temperatures"), this); - - connect(m_bsEntry, &SiteGroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onBuildingSurfaceClicked); - connect(m_shEntry, &SiteGroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onShallowClicked); - - layout->addWidget(m_bsEntry); - layout->addWidget(m_shEntry); - layout->addStretch(); -} - -void GroundTemperatureListView::selectFirst() { - onBuildingSurfaceClicked(); -} - -void GroundTemperatureListView::onBuildingSurfaceClicked() { - m_bsEntry->setSelected(true); - m_shEntry->setSelected(false); - emit typeSelected(GroundTempType::BuildingSurface); -} - -void GroundTemperatureListView::onShallowClicked() { - m_bsEntry->setSelected(false); - m_shEntry->setSelected(true); - emit typeSelected(GroundTempType::Shallow); -} - -// ───────────────────────────────────────────────────────── -// GroundTemperatureNotPresentView -// ───────────────────────────────────────────────────────── - -GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent) : QWidget(parent) { - auto* layout = new QVBoxLayout(); - layout->setContentsMargins(10, 10, 10, 10); - layout->setSpacing(10); - setLayout(layout); - - m_label = new QLabel(); - m_label->setWordWrap(true); - layout->addWidget(m_label); - - m_addButton = new QPushButton(tr("Add")); - m_addButton->setObjectName("StandardBlueButton"); - layout->addWidget(m_addButton, 0, Qt::AlignLeft); - - layout->addStretch(); - - connect(m_addButton, &QPushButton::clicked, this, [this]() { emit addClicked(m_type); }); -} - -void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString& typeName, model::Model model) { - m_type = type; - m_model = model; - m_label->setText(tr("The %1 object is not present in this model. Click Add to create it.").arg(typeName)); -} - // ───────────────────────────────────────────────────────── // SiteGroundTemperatureMonthlyWidget (abstract base) // ───────────────────────────────────────────────────────── @@ -236,17 +93,21 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject static const std::array month_binders{{ {&BS::januaryGroundTemperature, &BS::setJanuaryGroundTemperature, &BS::resetJanuaryGroundTemperature, &BS::isJanuaryGroundTemperatureDefaulted}, - {&BS::februaryGroundTemperature, &BS::setFebruaryGroundTemperature, &BS::resetFebruaryGroundTemperature, &BS::isFebruaryGroundTemperatureDefaulted}, + {&BS::februaryGroundTemperature, &BS::setFebruaryGroundTemperature, &BS::resetFebruaryGroundTemperature, + &BS::isFebruaryGroundTemperatureDefaulted}, {&BS::marchGroundTemperature, &BS::setMarchGroundTemperature, &BS::resetMarchGroundTemperature, &BS::isMarchGroundTemperatureDefaulted}, {&BS::aprilGroundTemperature, &BS::setAprilGroundTemperature, &BS::resetAprilGroundTemperature, &BS::isAprilGroundTemperatureDefaulted}, {&BS::mayGroundTemperature, &BS::setMayGroundTemperature, &BS::resetMayGroundTemperature, &BS::isMayGroundTemperatureDefaulted}, {&BS::juneGroundTemperature, &BS::setJuneGroundTemperature, &BS::resetJuneGroundTemperature, &BS::isJuneGroundTemperatureDefaulted}, {&BS::julyGroundTemperature, &BS::setJulyGroundTemperature, &BS::resetJulyGroundTemperature, &BS::isJulyGroundTemperatureDefaulted}, {&BS::augustGroundTemperature, &BS::setAugustGroundTemperature, &BS::resetAugustGroundTemperature, &BS::isAugustGroundTemperatureDefaulted}, - {&BS::septemberGroundTemperature, &BS::setSeptemberGroundTemperature, &BS::resetSeptemberGroundTemperature, &BS::isSeptemberGroundTemperatureDefaulted}, + {&BS::septemberGroundTemperature, &BS::setSeptemberGroundTemperature, &BS::resetSeptemberGroundTemperature, + &BS::isSeptemberGroundTemperatureDefaulted}, {&BS::octoberGroundTemperature, &BS::setOctoberGroundTemperature, &BS::resetOctoberGroundTemperature, &BS::isOctoberGroundTemperatureDefaulted}, - {&BS::novemberGroundTemperature, &BS::setNovemberGroundTemperature, &BS::resetNovemberGroundTemperature, &BS::isNovemberGroundTemperatureDefaulted}, - {&BS::decemberGroundTemperature, &BS::setDecemberGroundTemperature, &BS::resetDecemberGroundTemperature, &BS::isDecemberGroundTemperatureDefaulted}, + {&BS::novemberGroundTemperature, &BS::setNovemberGroundTemperature, &BS::resetNovemberGroundTemperature, + &BS::isNovemberGroundTemperatureDefaulted}, + {&BS::decemberGroundTemperature, &BS::setDecemberGroundTemperature, &BS::resetDecemberGroundTemperature, + &BS::isDecemberGroundTemperatureDefaulted}, }}; for (int i = 0; i < 12; ++i) { @@ -316,87 +177,4 @@ void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { } } -// ───────────────────────────────────────────────────────── -// GroundTemperatureView -// ───────────────────────────────────────────────────────── - -GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& model, QWidget* parent) - : QWidget(parent), m_model(model), m_isIP(isIP) { - auto* mainLayout = new QHBoxLayout(); - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->setSpacing(0); - setLayout(mainLayout); - - // Left pane (fixed width) - auto* leftPane = new QWidget(); - leftPane->setFixedWidth(200); - auto* leftLayout = new QVBoxLayout(); - leftLayout->setContentsMargins(0, 0, 0, 0); - leftLayout->setSpacing(0); - leftPane->setLayout(leftLayout); - - m_listView = new GroundTemperatureListView(leftPane); - leftLayout->addWidget(m_listView); - leftLayout->addStretch(); - - mainLayout->addWidget(leftPane); - - // Right pane: stacked widget - m_rightStack = new QStackedWidget(); - mainLayout->addWidget(m_rightStack, 1); - - m_notPresentView = new GroundTemperatureNotPresentView(); - m_rightStack->addWidget(m_notPresentView); // index 0 - - m_bsView = new SiteGroundTemperatureBuildingSurfaceWidget(isIP); - m_rightStack->addWidget(m_bsView); // index 1 - - m_shView = new SiteGroundTemperatureShallowWidget(isIP); - m_rightStack->addWidget(m_shView); // index 2 - - connect(m_listView, &GroundTemperatureListView::typeSelected, this, &GroundTemperatureView::onTypeSelected); - connect(m_notPresentView, &GroundTemperatureNotPresentView::addClicked, this, &GroundTemperatureView::onObjectCreated); - - // Forward unit toggle to sub-views (signal-to-signal) - connect(this, &GroundTemperatureView::toggleUnitsClicked, m_bsView, &SiteGroundTemperatureBuildingSurfaceWidget::toggleUnitsClicked); - connect(this, &GroundTemperatureView::toggleUnitsClicked, m_shView, &SiteGroundTemperatureShallowWidget::toggleUnitsClicked); - - // Auto-select first entry after the event loop starts (triggers both visual selection and right-pane update) - QTimer::singleShot(0, this, [this]() { m_listView->selectFirst(); }); -} - -void GroundTemperatureView::onTypeSelected(GroundTempType type) { - if (type == GroundTempType::BuildingSurface) { - auto opt = m_model.getOptionalUniqueModelObject(); - if (opt) { - m_bsView->attach(*opt); - m_rightStack->setCurrentIndex(1); - } else { - m_notPresentView->setType(type, tr("Site:GroundTemperature:BuildingSurface"), m_model); - m_rightStack->setCurrentIndex(0); - } - } else { - auto opt = m_model.getOptionalUniqueModelObject(); - if (opt) { - m_shView->attach(*opt); - m_rightStack->setCurrentIndex(2); - } else { - m_notPresentView->setType(type, tr("Site:GroundTemperature:Shallow"), m_model); - m_rightStack->setCurrentIndex(0); - } - } -} - -void GroundTemperatureView::onObjectCreated(GroundTempType type) { - if (type == GroundTempType::BuildingSurface) { - auto obj = m_model.getUniqueModelObject(); - m_bsView->attach(obj); - m_rightStack->setCurrentIndex(1); - } else { - auto obj = m_model.getUniqueModelObject(); - m_shView->attach(obj); - m_rightStack->setCurrentIndex(2); - } -} - } // namespace openstudio diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp new file mode 100644 index 000000000..f0b7fed2a --- /dev/null +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -0,0 +1,74 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#ifndef OPENSTUDIO_GROUNDTEMPERATUREMONTHLYINSPECTORVIEW_HPP +#define OPENSTUDIO_GROUNDTEMPERATUREMONTHLYINSPECTORVIEW_HPP + +#include +#include +#include + +#include + +#include + +class QLabel; + +namespace openstudio { + +class OSQuantityEdit2; + +/** Abstract base for inspector widgets that show 12 monthly temperature fields. */ +class SiteGroundTemperatureMonthlyWidget : public QWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureMonthlyWidget(bool isIP, QWidget* parent = nullptr); + virtual ~SiteGroundTemperatureMonthlyWidget() = default; + + virtual void attach(const model::ModelObject& obj) = 0; + void detach(); + + signals: + void toggleUnitsClicked(bool displayIP); + + protected: + bool m_isIP; + QLabel* m_titleLabel = nullptr; + std::array m_edits{}; +}; + +/** Inspector for Site:GroundTemperature:BuildingSurface — 12 monthly fields. */ +class SiteGroundTemperatureBuildingSurfaceWidget : public SiteGroundTemperatureMonthlyWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureBuildingSurfaceWidget(bool isIP, QWidget* parent = nullptr); + + void attach(const model::ModelObject& obj) override; + + private: + boost::optional m_obj; +}; + +/** Inspector for Site:GroundTemperature:Shallow — 12 monthly fields. */ +class SiteGroundTemperatureShallowWidget : public SiteGroundTemperatureMonthlyWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureShallowWidget(bool isIP, QWidget* parent = nullptr); + + void attach(const model::ModelObject& obj) override; + + private: + boost::optional m_obj; +}; + +} // namespace openstudio + +#endif // OPENSTUDIO_GROUNDTEMPERATUREMONTHLYINSPECTORVIEW_HPP diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp new file mode 100644 index 000000000..7de465af1 --- /dev/null +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -0,0 +1,238 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#include "GroundTemperatureView.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openstudio { + +// ───────────────────────────────────────────────────────── +// GroundTemperatureEntry +// ───────────────────────────────────────────────────────── + +GroundTemperatureEntry::GroundTemperatureEntry(const QString& label, QWidget* parent) : QWidget(parent) { + setFixedHeight(25); + setMouseTracking(true); + + auto* layout = new QHBoxLayout(); + layout->setContentsMargins(10, 0, 0, 0); + setLayout(layout); + + m_label = new QLabel(label); + m_label->setMouseTracking(true); + layout->addWidget(m_label); +} + +void GroundTemperatureEntry::setSelected(bool selected) { + m_selected = selected; + update(); +} + +void GroundTemperatureEntry::paintEvent(QPaintEvent* /*event*/) { + QStyleOption opt; + opt.initFrom(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + if (m_hovering || m_selected) { + p.setBrush(QBrush(QColor(207, 207, 207))); + p.setPen(Qt::NoPen); + p.drawRect(0, 0, size().width() - 1, size().height() - 1); + } + + p.setPen(Qt::SolidLine); + p.setBrush(QBrush(QColor(Qt::black))); + p.drawLine(0, 0, size().width(), 0); + p.drawLine(0, size().height() - 1, size().width(), size().height() - 1); +} + +void GroundTemperatureEntry::mousePressEvent(QMouseEvent* event) { + m_mouseDown = true; + event->accept(); +} + +void GroundTemperatureEntry::mouseReleaseEvent(QMouseEvent* event) { + if (m_mouseDown) { + emit clicked(); + } + m_mouseDown = false; + event->accept(); +} + +void GroundTemperatureEntry::mouseMoveEvent(QMouseEvent* event) { + m_hovering = true; + update(); + event->accept(); +} + +void GroundTemperatureEntry::leaveEvent(QEvent* event) { + m_mouseDown = false; + m_hovering = false; + update(); + event->accept(); +} + +// ───────────────────────────────────────────────────────── +// GroundTemperatureListView +// ───────────────────────────────────────────────────────── + +GroundTemperatureListView::GroundTemperatureListView(QWidget* parent) : QWidget(parent) { + auto* layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + setLayout(layout); + + m_bsEntry = new GroundTemperatureEntry(tr("Building Surface Ground Temperatures"), this); + m_shEntry = new GroundTemperatureEntry(tr("Shallow Ground Temperatures"), this); + + connect(m_bsEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onBuildingSurfaceClicked); + connect(m_shEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onShallowClicked); + + layout->addWidget(m_bsEntry); + layout->addWidget(m_shEntry); + layout->addStretch(); +} + +void GroundTemperatureListView::selectFirst() { + onBuildingSurfaceClicked(); +} + +void GroundTemperatureListView::onBuildingSurfaceClicked() { + m_bsEntry->setSelected(true); + m_shEntry->setSelected(false); + emit typeSelected(GroundTempType::BuildingSurface); +} + +void GroundTemperatureListView::onShallowClicked() { + m_bsEntry->setSelected(false); + m_shEntry->setSelected(true); + emit typeSelected(GroundTempType::Shallow); +} + +// ───────────────────────────────────────────────────────── +// GroundTemperatureNotPresentView +// ───────────────────────────────────────────────────────── + +GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent) : QWidget(parent) { + auto* layout = new QVBoxLayout(); + layout->setContentsMargins(10, 10, 10, 10); + layout->setSpacing(10); + setLayout(layout); + + m_label = new QLabel(); + m_label->setWordWrap(true); + layout->addWidget(m_label); + + m_addButton = new QPushButton(tr("Add")); + m_addButton->setObjectName("StandardBlueButton"); + layout->addWidget(m_addButton, 0, Qt::AlignLeft); + + layout->addStretch(); + + connect(m_addButton, &QPushButton::clicked, this, [this]() { emit addClicked(m_type); }); +} + +void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString& typeName, model::Model model) { + m_type = type; + m_model = model; + m_label->setText(tr("The %1 object is not present in this model. Click Add to create it.").arg(typeName)); +} + +// ───────────────────────────────────────────────────────── +// GroundTemperatureView +// ───────────────────────────────────────────────────────── + +GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& model, QWidget* parent) : QWidget(parent), m_model(model), m_isIP(isIP) { + auto* mainLayout = new QHBoxLayout(); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + setLayout(mainLayout); + + // Left pane (fixed width) + auto* leftPane = new QWidget(); + leftPane->setFixedWidth(200); + auto* leftLayout = new QVBoxLayout(); + leftLayout->setContentsMargins(0, 0, 0, 0); + leftLayout->setSpacing(0); + leftPane->setLayout(leftLayout); + + m_listView = new GroundTemperatureListView(leftPane); + leftLayout->addWidget(m_listView); + leftLayout->addStretch(); + + mainLayout->addWidget(leftPane); + + // Right pane: stacked widget + m_rightStack = new QStackedWidget(); + mainLayout->addWidget(m_rightStack, 1); + + m_notPresentView = new GroundTemperatureNotPresentView(); + m_rightStack->addWidget(m_notPresentView); // index 0 + + m_bsView = new SiteGroundTemperatureBuildingSurfaceWidget(isIP); + m_rightStack->addWidget(m_bsView); // index 1 + + m_shView = new SiteGroundTemperatureShallowWidget(isIP); + m_rightStack->addWidget(m_shView); // index 2 + + connect(m_listView, &GroundTemperatureListView::typeSelected, this, &GroundTemperatureView::onTypeSelected); + connect(m_notPresentView, &GroundTemperatureNotPresentView::addClicked, this, &GroundTemperatureView::onObjectCreated); + + // Forward unit toggle to sub-views (signal-to-signal) + connect(this, &GroundTemperatureView::toggleUnitsClicked, m_bsView, &SiteGroundTemperatureBuildingSurfaceWidget::toggleUnitsClicked); + connect(this, &GroundTemperatureView::toggleUnitsClicked, m_shView, &SiteGroundTemperatureShallowWidget::toggleUnitsClicked); + + // Auto-select first entry after the event loop starts + QTimer::singleShot(0, this, [this]() { m_listView->selectFirst(); }); +} + +void GroundTemperatureView::onTypeSelected(GroundTempType type) { + if (type == GroundTempType::BuildingSurface) { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_bsView->attach(*opt); + m_rightStack->setCurrentIndex(1); + } else { + m_notPresentView->setType(type, tr("Site:GroundTemperature:BuildingSurface"), m_model); + m_rightStack->setCurrentIndex(0); + } + } else { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_shView->attach(*opt); + m_rightStack->setCurrentIndex(2); + } else { + m_notPresentView->setType(type, tr("Site:GroundTemperature:Shallow"), m_model); + m_rightStack->setCurrentIndex(0); + } + } +} + +void GroundTemperatureView::onObjectCreated(GroundTempType type) { + if (type == GroundTempType::BuildingSurface) { + auto obj = m_model.getUniqueModelObject(); + m_bsView->attach(obj); + m_rightStack->setCurrentIndex(1); + } else { + auto obj = m_model.getUniqueModelObject(); + m_shView->attach(obj); + m_rightStack->setCurrentIndex(2); + } +} + +} // namespace openstudio diff --git a/src/openstudio_lib/GroundTemperatureWidget.hpp b/src/openstudio_lib/GroundTemperatureView.hpp similarity index 56% rename from src/openstudio_lib/GroundTemperatureWidget.hpp rename to src/openstudio_lib/GroundTemperatureView.hpp index 61d144533..65b721e9e 100644 --- a/src/openstudio_lib/GroundTemperatureWidget.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -3,26 +3,21 @@ * See also https://openstudiocoalition.org/about/software_license/ ***********************************************************************************************************************/ -#ifndef OPENSTUDIO_GROUNDTEMPERATUREWIDGET_HPP -#define OPENSTUDIO_GROUNDTEMPERATUREWIDGET_HPP +#ifndef OPENSTUDIO_GROUNDTEMPERATUREVIEW_HPP +#define OPENSTUDIO_GROUNDTEMPERATUREVIEW_HPP + +#include "GroundTemperatureMonthlyInspectorView.hpp" #include -#include -#include -#include #include -#include - class QLabel; class QPushButton; class QStackedWidget; namespace openstudio { -class OSQuantityEdit2; - enum class GroundTempType { BuildingSurface, @@ -30,12 +25,12 @@ enum class GroundTempType }; /** A single clickable entry in the left-hand list (ScheduleTabDefault style). */ -class SiteGroundTemperatureEntry : public QWidget +class GroundTemperatureEntry : public QWidget { Q_OBJECT public: - explicit SiteGroundTemperatureEntry(const QString& label, QWidget* parent = nullptr); + explicit GroundTemperatureEntry(const QString& label, QWidget* parent = nullptr); void setSelected(bool selected); @@ -56,7 +51,7 @@ class SiteGroundTemperatureEntry : public QWidget QLabel* m_label = nullptr; }; -/** Left-hand list: two SiteGroundTemperatureEntry items. */ +/** Left-hand list: one entry per ground-temperature type. */ class GroundTemperatureListView : public QWidget { Q_OBJECT @@ -74,11 +69,11 @@ class GroundTemperatureListView : public QWidget void onShallowClicked(); private: - SiteGroundTemperatureEntry* m_bsEntry = nullptr; - SiteGroundTemperatureEntry* m_shEntry = nullptr; + GroundTemperatureEntry* m_bsEntry = nullptr; + GroundTemperatureEntry* m_shEntry = nullptr; }; -/** "Not present" right pane: message label + Add button. */ +/** Right pane shown when the selected object is not yet in the model. */ class GroundTemperatureNotPresentView : public QWidget { Q_OBJECT @@ -98,55 +93,6 @@ class GroundTemperatureNotPresentView : public QWidget model::Model m_model; }; -/** Abstract base for widgets showing 12 monthly temperature fields. */ -class SiteGroundTemperatureMonthlyWidget : public QWidget -{ - Q_OBJECT - - public: - explicit SiteGroundTemperatureMonthlyWidget(bool isIP, QWidget* parent = nullptr); - virtual ~SiteGroundTemperatureMonthlyWidget() = default; - - virtual void attach(const model::ModelObject& obj) = 0; - void detach(); - - signals: - void toggleUnitsClicked(bool displayIP); - - protected: - bool m_isIP; - QLabel* m_titleLabel = nullptr; - std::array m_edits{}; -}; - -/** Concrete: Site:GroundTemperature:BuildingSurface */ -class SiteGroundTemperatureBuildingSurfaceWidget : public SiteGroundTemperatureMonthlyWidget -{ - Q_OBJECT - - public: - explicit SiteGroundTemperatureBuildingSurfaceWidget(bool isIP, QWidget* parent = nullptr); - - void attach(const model::ModelObject& obj) override; - - private: - boost::optional m_obj; -}; - -/** Concrete: Site:GroundTemperature:Shallow */ -class SiteGroundTemperatureShallowWidget : public SiteGroundTemperatureMonthlyWidget -{ - Q_OBJECT - - public: - explicit SiteGroundTemperatureShallowWidget(bool isIP, QWidget* parent = nullptr); - - void attach(const model::ModelObject& obj) override; - - private: - boost::optional m_obj; -}; - /** Top-level widget for the Ground Temperatures sub-tab. */ class GroundTemperatureView : public QWidget { @@ -174,4 +120,4 @@ class GroundTemperatureView : public QWidget } // namespace openstudio -#endif // OPENSTUDIO_GROUNDTEMPERATUREWIDGET_HPP +#endif // OPENSTUDIO_GROUNDTEMPERATUREVIEW_HPP diff --git a/src/openstudio_lib/LocationTabController.cpp b/src/openstudio_lib/LocationTabController.cpp index b157aa289..30fffea96 100644 --- a/src/openstudio_lib/LocationTabController.cpp +++ b/src/openstudio_lib/LocationTabController.cpp @@ -7,7 +7,7 @@ #include "LifeCycleCostsTabView.hpp" #include "LocationTabView.hpp" -#include "GroundTemperatureWidget.hpp" +#include "GroundTemperatureView.hpp" #include "UtilityBillsView.hpp" #include "UtilityBillsController.hpp" From 7c56825cff0c6f053870e121b748b925f40fd5f2 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 11:51:07 +0100 Subject: [PATCH 08/90] Style a bit more --- src/openstudio_lib/GroundTemperatureView.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index 7de465af1..f11994217 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -140,6 +140,7 @@ GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent m_addButton = new QPushButton(tr("Add")); m_addButton->setObjectName("StandardBlueButton"); + m_addButton->setMinimumWidth(100); layout->addWidget(m_addButton, 0, Qt::AlignLeft); layout->addStretch(); @@ -149,8 +150,8 @@ GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString& typeName, model::Model model) { m_type = type; - m_model = model; - m_label->setText(tr("The %1 object is not present in this model. Click Add to create it.").arg(typeName)); + m_model = std::move(model); + m_label->setText(tr("The %1 Unique ModelObject is not present in this model. Click Add to instantiate it.").arg(typeName)); } // ───────────────────────────────────────────────────────── @@ -158,14 +159,16 @@ void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString // ───────────────────────────────────────────────────────── GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& model, QWidget* parent) : QWidget(parent), m_model(model), m_isIP(isIP) { + setObjectName("GrayWidgetWithLeftTopBorders"); + auto* mainLayout = new QHBoxLayout(); - mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setContentsMargins(1, 1, 0, 0); mainLayout->setSpacing(0); setLayout(mainLayout); - // Left pane (fixed width) + // Left pane (sizes to content; right stack takes remaining space via stretch=1) auto* leftPane = new QWidget(); - leftPane->setFixedWidth(200); + leftPane->setFixedWidth(250); auto* leftLayout = new QVBoxLayout(); leftLayout->setContentsMargins(0, 0, 0, 0); leftLayout->setSpacing(0); @@ -177,6 +180,13 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode mainLayout->addWidget(leftPane); + // Vertical separator (matches Schedules tab style) + auto* vLine = new QWidget(); + vLine->setObjectName("VLine"); + vLine->setStyleSheet("QWidget#VLine { background: #445051;}"); + vLine->setFixedWidth(2); + mainLayout->addWidget(vLine); + // Right pane: stacked widget m_rightStack = new QStackedWidget(); mainLayout->addWidget(m_rightStack, 1); From cc0f46943c5d4aaf1ef66dd07ebcbeea24b43b47 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 12:11:39 +0100 Subject: [PATCH 09/90] OSQuantityEdit2::completeBind sets Enabled but not Locked=False MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit unbind() (line 190): setLocked(true); // → m_lineEdit->setReadOnly(true) Old completeBind() (before my fix) — line 160: setEnabled(true); // enables outer widget, but m_lineEdit->readOnly is still true! My fix (line 159): m_lineEdit->setLocked(false); // resets readOnly before enabling setEnabled(true); The fix is safe because QuantityLineEdit::setLocked guards with if (m_locked != locked), so calling setLocked(false) on a fresh (never-unbound) edit is a no-op. The old code had the same latent bug; it was just never exposed because the original UI was broken in a more visible way ("NO ICON" items) before the full switch to the new widget pattern. --- src/shared_gui_components/OSQuantityEdit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared_gui_components/OSQuantityEdit.cpp b/src/shared_gui_components/OSQuantityEdit.cpp index 6b705b65a..1111fde65 100644 --- a/src/shared_gui_components/OSQuantityEdit.cpp +++ b/src/shared_gui_components/OSQuantityEdit.cpp @@ -156,6 +156,7 @@ void OSQuantityEdit2::completeBind(bool isIP, const model::ModelObject& modelObj m_isAutosized = isAutosized; m_isAutocalculated = isAutocalculated; + m_lineEdit->setLocked(false); setEnabled(true); connect(m_lineEdit, &QLineEdit::editingFinished, this, From 7620beff37f5be52dbf544ef9d5bec6a3fb53060 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 12:37:50 +0100 Subject: [PATCH 10/90] Add Site:GroundTemperature:Deep too --- .../GroundTemperatureMonthlyInspectorView.cpp | 290 +++++++++++++++--- .../GroundTemperatureMonthlyInspectorView.hpp | 15 + src/openstudio_lib/GroundTemperatureView.cpp | 34 +- src/openstudio_lib/GroundTemperatureView.hpp | 6 +- 4 files changed, 302 insertions(+), 43 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index ba1543393..fc97c2040 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -92,22 +93,78 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject }; static const std::array month_binders{{ - {&BS::januaryGroundTemperature, &BS::setJanuaryGroundTemperature, &BS::resetJanuaryGroundTemperature, &BS::isJanuaryGroundTemperatureDefaulted}, - {&BS::februaryGroundTemperature, &BS::setFebruaryGroundTemperature, &BS::resetFebruaryGroundTemperature, - &BS::isFebruaryGroundTemperatureDefaulted}, - {&BS::marchGroundTemperature, &BS::setMarchGroundTemperature, &BS::resetMarchGroundTemperature, &BS::isMarchGroundTemperatureDefaulted}, - {&BS::aprilGroundTemperature, &BS::setAprilGroundTemperature, &BS::resetAprilGroundTemperature, &BS::isAprilGroundTemperatureDefaulted}, - {&BS::mayGroundTemperature, &BS::setMayGroundTemperature, &BS::resetMayGroundTemperature, &BS::isMayGroundTemperatureDefaulted}, - {&BS::juneGroundTemperature, &BS::setJuneGroundTemperature, &BS::resetJuneGroundTemperature, &BS::isJuneGroundTemperatureDefaulted}, - {&BS::julyGroundTemperature, &BS::setJulyGroundTemperature, &BS::resetJulyGroundTemperature, &BS::isJulyGroundTemperatureDefaulted}, - {&BS::augustGroundTemperature, &BS::setAugustGroundTemperature, &BS::resetAugustGroundTemperature, &BS::isAugustGroundTemperatureDefaulted}, - {&BS::septemberGroundTemperature, &BS::setSeptemberGroundTemperature, &BS::resetSeptemberGroundTemperature, - &BS::isSeptemberGroundTemperatureDefaulted}, - {&BS::octoberGroundTemperature, &BS::setOctoberGroundTemperature, &BS::resetOctoberGroundTemperature, &BS::isOctoberGroundTemperatureDefaulted}, - {&BS::novemberGroundTemperature, &BS::setNovemberGroundTemperature, &BS::resetNovemberGroundTemperature, - &BS::isNovemberGroundTemperatureDefaulted}, - {&BS::decemberGroundTemperature, &BS::setDecemberGroundTemperature, &BS::resetDecemberGroundTemperature, - &BS::isDecemberGroundTemperatureDefaulted}, + { + &BS::januaryGroundTemperature, + &BS::setJanuaryGroundTemperature, + &BS::resetJanuaryGroundTemperature, + &BS::isJanuaryGroundTemperatureDefaulted, + }, + { + &BS::februaryGroundTemperature, + &BS::setFebruaryGroundTemperature, + &BS::resetFebruaryGroundTemperature, + &BS::isFebruaryGroundTemperatureDefaulted, + }, + { + &BS::marchGroundTemperature, + &BS::setMarchGroundTemperature, + &BS::resetMarchGroundTemperature, + &BS::isMarchGroundTemperatureDefaulted, + }, + { + &BS::aprilGroundTemperature, + &BS::setAprilGroundTemperature, + &BS::resetAprilGroundTemperature, + &BS::isAprilGroundTemperatureDefaulted, + }, + { + &BS::mayGroundTemperature, + &BS::setMayGroundTemperature, + &BS::resetMayGroundTemperature, + &BS::isMayGroundTemperatureDefaulted, + }, + { + &BS::juneGroundTemperature, + &BS::setJuneGroundTemperature, + &BS::resetJuneGroundTemperature, + &BS::isJuneGroundTemperatureDefaulted, + }, + { + &BS::julyGroundTemperature, + &BS::setJulyGroundTemperature, + &BS::resetJulyGroundTemperature, + &BS::isJulyGroundTemperatureDefaulted, + }, + { + &BS::augustGroundTemperature, + &BS::setAugustGroundTemperature, + &BS::resetAugustGroundTemperature, + &BS::isAugustGroundTemperatureDefaulted, + }, + { + &BS::septemberGroundTemperature, + &BS::setSeptemberGroundTemperature, + &BS::resetSeptemberGroundTemperature, + &BS::isSeptemberGroundTemperatureDefaulted, + }, + { + &BS::octoberGroundTemperature, + &BS::setOctoberGroundTemperature, + &BS::resetOctoberGroundTemperature, + &BS::isOctoberGroundTemperatureDefaulted, + }, + { + &BS::novemberGroundTemperature, + &BS::setNovemberGroundTemperature, + &BS::resetNovemberGroundTemperature, + &BS::isNovemberGroundTemperatureDefaulted, + }, + { + &BS::decemberGroundTemperature, + &BS::setDecemberGroundTemperature, + &BS::resetDecemberGroundTemperature, + &BS::isDecemberGroundTemperatureDefaulted, + }, }}; for (int i = 0; i < 12; ++i) { @@ -142,30 +199,183 @@ void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { }; static const std::array month_binders{{ - {&SH::januarySurfaceGroundTemperature, &SH::setJanuarySurfaceGroundTemperature, &SH::resetJanuarySurfaceGroundTemperature, - &SH::isJanuarySurfaceGroundTemperatureDefaulted}, - {&SH::februarySurfaceGroundTemperature, &SH::setFebruarySurfaceGroundTemperature, &SH::resetFebruarySurfaceGroundTemperature, - &SH::isFebruarySurfaceGroundTemperatureDefaulted}, - {&SH::marchSurfaceGroundTemperature, &SH::setMarchSurfaceGroundTemperature, &SH::resetMarchSurfaceGroundTemperature, - &SH::isMarchSurfaceGroundTemperatureDefaulted}, - {&SH::aprilSurfaceGroundTemperature, &SH::setAprilSurfaceGroundTemperature, &SH::resetAprilSurfaceGroundTemperature, - &SH::isAprilSurfaceGroundTemperatureDefaulted}, - {&SH::maySurfaceGroundTemperature, &SH::setMaySurfaceGroundTemperature, &SH::resetMaySurfaceGroundTemperature, - &SH::isMaySurfaceGroundTemperatureDefaulted}, - {&SH::juneSurfaceGroundTemperature, &SH::setJuneSurfaceGroundTemperature, &SH::resetJuneSurfaceGroundTemperature, - &SH::isJuneSurfaceGroundTemperatureDefaulted}, - {&SH::julySurfaceGroundTemperature, &SH::setJulySurfaceGroundTemperature, &SH::resetJulySurfaceGroundTemperature, - &SH::isJulySurfaceGroundTemperatureDefaulted}, - {&SH::augustSurfaceGroundTemperature, &SH::setAugustSurfaceGroundTemperature, &SH::resetAugustSurfaceGroundTemperature, - &SH::isAugustSurfaceGroundTemperatureDefaulted}, - {&SH::septemberSurfaceGroundTemperature, &SH::setSeptemberSurfaceGroundTemperature, &SH::resetSeptemberSurfaceGroundTemperature, - &SH::isSeptemberSurfaceGroundTemperatureDefaulted}, - {&SH::octoberSurfaceGroundTemperature, &SH::setOctoberSurfaceGroundTemperature, &SH::resetOctoberSurfaceGroundTemperature, - &SH::isOctoberSurfaceGroundTemperatureDefaulted}, - {&SH::novemberSurfaceGroundTemperature, &SH::setNovemberSurfaceGroundTemperature, &SH::resetNovemberSurfaceGroundTemperature, - &SH::isNovemberSurfaceGroundTemperatureDefaulted}, - {&SH::decemberSurfaceGroundTemperature, &SH::setDecemberSurfaceGroundTemperature, &SH::resetDecemberSurfaceGroundTemperature, - &SH::isDecemberSurfaceGroundTemperatureDefaulted}, + { + &SH::januarySurfaceGroundTemperature, + &SH::setJanuarySurfaceGroundTemperature, + &SH::resetJanuarySurfaceGroundTemperature, + &SH::isJanuarySurfaceGroundTemperatureDefaulted, + }, + { + &SH::februarySurfaceGroundTemperature, + &SH::setFebruarySurfaceGroundTemperature, + &SH::resetFebruarySurfaceGroundTemperature, + &SH::isFebruarySurfaceGroundTemperatureDefaulted, + }, + { + &SH::marchSurfaceGroundTemperature, + &SH::setMarchSurfaceGroundTemperature, + &SH::resetMarchSurfaceGroundTemperature, + &SH::isMarchSurfaceGroundTemperatureDefaulted, + }, + { + &SH::aprilSurfaceGroundTemperature, + &SH::setAprilSurfaceGroundTemperature, + &SH::resetAprilSurfaceGroundTemperature, + &SH::isAprilSurfaceGroundTemperatureDefaulted, + }, + { + &SH::maySurfaceGroundTemperature, + &SH::setMaySurfaceGroundTemperature, + &SH::resetMaySurfaceGroundTemperature, + &SH::isMaySurfaceGroundTemperatureDefaulted, + }, + { + &SH::juneSurfaceGroundTemperature, + &SH::setJuneSurfaceGroundTemperature, + &SH::resetJuneSurfaceGroundTemperature, + &SH::isJuneSurfaceGroundTemperatureDefaulted, + }, + { + &SH::julySurfaceGroundTemperature, + &SH::setJulySurfaceGroundTemperature, + &SH::resetJulySurfaceGroundTemperature, + &SH::isJulySurfaceGroundTemperatureDefaulted, + }, + { + &SH::augustSurfaceGroundTemperature, + &SH::setAugustSurfaceGroundTemperature, + &SH::resetAugustSurfaceGroundTemperature, + &SH::isAugustSurfaceGroundTemperatureDefaulted, + }, + { + &SH::septemberSurfaceGroundTemperature, + &SH::setSeptemberSurfaceGroundTemperature, + &SH::resetSeptemberSurfaceGroundTemperature, + &SH::isSeptemberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::octoberSurfaceGroundTemperature, + &SH::setOctoberSurfaceGroundTemperature, + &SH::resetOctoberSurfaceGroundTemperature, + &SH::isOctoberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::novemberSurfaceGroundTemperature, + &SH::setNovemberSurfaceGroundTemperature, + &SH::resetNovemberSurfaceGroundTemperature, + &SH::isNovemberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::decemberSurfaceGroundTemperature, + &SH::setDecemberSurfaceGroundTemperature, + &SH::resetDecemberSurfaceGroundTemperature, + &SH::isDecemberSurfaceGroundTemperatureDefaulted, + }, + }}; + + for (int i = 0; i < 12; ++i) { + const auto& mb = month_binders[i]; + m_edits[i]->bind(m_isIP, *m_obj, DoubleGetter([this, g = mb.getter]() { return (m_obj.get_ptr()->*g)(); }), + boost::optional([this, s = mb.setter](double v) { return (m_obj.get_ptr()->*s)(v); }), + boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, + boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); + } +} + +// ───────────────────────────────────────────────────────── +// SiteGroundTemperatureDeepWidget +// ───────────────────────────────────────────────────────── + +SiteGroundTemperatureDeepWidget::SiteGroundTemperatureDeepWidget(bool isIP, QWidget* parent) : SiteGroundTemperatureMonthlyWidget(isIP, parent) {} + +void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { + detach(); + m_obj = obj.cast(); + m_titleLabel->setText("Site:GroundTemperature:Deep"); + + using DW = model::SiteGroundTemperatureDeep; + + struct MonthBinding + { + double (DW::*getter)() const; + bool (DW::*setter)(double); + void (DW::*resetter)(); + bool (DW::*defaulted)() const; + }; + + static const std::array month_binders{{ + { + &DW::januaryDeepGroundTemperature, + &DW::setJanuaryDeepGroundTemperature, + &DW::resetJanuaryDeepGroundTemperature, + &DW::isJanuaryDeepGroundTemperatureDefaulted, + }, + { + &DW::februaryDeepGroundTemperature, + &DW::setFebruaryDeepGroundTemperature, + &DW::resetFebruaryDeepGroundTemperature, + &DW::isFebruaryDeepGroundTemperatureDefaulted, + }, + { + &DW::marchDeepGroundTemperature, + &DW::setMarchDeepGroundTemperature, + &DW::resetMarchDeepGroundTemperature, + &DW::isMarchDeepGroundTemperatureDefaulted, + }, + { + &DW::aprilDeepGroundTemperature, + &DW::setAprilDeepGroundTemperature, + &DW::resetAprilDeepGroundTemperature, + &DW::isAprilDeepGroundTemperatureDefaulted, + }, + { + &DW::mayDeepGroundTemperature, + &DW::setMayDeepGroundTemperature, + &DW::resetMayDeepGroundTemperature, + &DW::isMayDeepGroundTemperatureDefaulted, + }, + { + &DW::juneDeepGroundTemperature, + &DW::setJuneDeepGroundTemperature, + &DW::resetJuneDeepGroundTemperature, + &DW::isJuneDeepGroundTemperatureDefaulted, + }, + { + &DW::julyDeepGroundTemperature, + &DW::setJulyDeepGroundTemperature, + &DW::resetJulyDeepGroundTemperature, + &DW::isJulyDeepGroundTemperatureDefaulted, + }, + { + &DW::augustDeepGroundTemperature, + &DW::setAugustDeepGroundTemperature, + &DW::resetAugustDeepGroundTemperature, + &DW::isAugustDeepGroundTemperatureDefaulted, + }, + { + &DW::septemberDeepGroundTemperature, + &DW::setSeptemberDeepGroundTemperature, + &DW::resetSeptemberDeepGroundTemperature, + &DW::isSeptemberDeepGroundTemperatureDefaulted, + }, + { + &DW::octoberDeepGroundTemperature, + &DW::setOctoberDeepGroundTemperature, + &DW::resetOctoberDeepGroundTemperature, + &DW::isOctoberDeepGroundTemperatureDefaulted, + }, + { + &DW::novemberDeepGroundTemperature, + &DW::setNovemberDeepGroundTemperature, + &DW::resetNovemberDeepGroundTemperature, + &DW::isNovemberDeepGroundTemperatureDefaulted, + }, + { + &DW::decemberDeepGroundTemperature, + &DW::setDecemberDeepGroundTemperature, + &DW::resetDecemberDeepGroundTemperature, + &DW::isDecemberDeepGroundTemperatureDefaulted, + }, }}; for (int i = 0; i < 12; ++i) { diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp index f0b7fed2a..e879d8397 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -69,6 +70,20 @@ class SiteGroundTemperatureShallowWidget : public SiteGroundTemperatureMonthlyWi boost::optional m_obj; }; +/** Inspector for Site:GroundTemperature:Deep — 12 monthly fields. */ +class SiteGroundTemperatureDeepWidget : public SiteGroundTemperatureMonthlyWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureDeepWidget(bool isIP, QWidget* parent = nullptr); + + void attach(const model::ModelObject& obj) override; + + private: + boost::optional m_obj; +}; + } // namespace openstudio #endif // OPENSTUDIO_GROUNDTEMPERATUREMONTHLYINSPECTORVIEW_HPP diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index f11994217..871257e72 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -99,12 +100,15 @@ GroundTemperatureListView::GroundTemperatureListView(QWidget* parent) : QWidget( m_bsEntry = new GroundTemperatureEntry(tr("Building Surface Ground Temperatures"), this); m_shEntry = new GroundTemperatureEntry(tr("Shallow Ground Temperatures"), this); + m_deepEntry = new GroundTemperatureEntry(tr("Deep Ground Temperatures"), this); connect(m_bsEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onBuildingSurfaceClicked); connect(m_shEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onShallowClicked); + connect(m_deepEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onDeepClicked); layout->addWidget(m_bsEntry); layout->addWidget(m_shEntry); + layout->addWidget(m_deepEntry); layout->addStretch(); } @@ -115,15 +119,24 @@ void GroundTemperatureListView::selectFirst() { void GroundTemperatureListView::onBuildingSurfaceClicked() { m_bsEntry->setSelected(true); m_shEntry->setSelected(false); + m_deepEntry->setSelected(false); emit typeSelected(GroundTempType::BuildingSurface); } void GroundTemperatureListView::onShallowClicked() { m_bsEntry->setSelected(false); m_shEntry->setSelected(true); + m_deepEntry->setSelected(false); emit typeSelected(GroundTempType::Shallow); } +void GroundTemperatureListView::onDeepClicked() { + m_bsEntry->setSelected(false); + m_shEntry->setSelected(false); + m_deepEntry->setSelected(true); + emit typeSelected(GroundTempType::Deep); +} + // ───────────────────────────────────────────────────────── // GroundTemperatureNotPresentView // ───────────────────────────────────────────────────────── @@ -200,12 +213,16 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode m_shView = new SiteGroundTemperatureShallowWidget(isIP); m_rightStack->addWidget(m_shView); // index 2 + m_deepView = new SiteGroundTemperatureDeepWidget(isIP); + m_rightStack->addWidget(m_deepView); // index 3 + connect(m_listView, &GroundTemperatureListView::typeSelected, this, &GroundTemperatureView::onTypeSelected); connect(m_notPresentView, &GroundTemperatureNotPresentView::addClicked, this, &GroundTemperatureView::onObjectCreated); // Forward unit toggle to sub-views (signal-to-signal) connect(this, &GroundTemperatureView::toggleUnitsClicked, m_bsView, &SiteGroundTemperatureBuildingSurfaceWidget::toggleUnitsClicked); connect(this, &GroundTemperatureView::toggleUnitsClicked, m_shView, &SiteGroundTemperatureShallowWidget::toggleUnitsClicked); + connect(this, &GroundTemperatureView::toggleUnitsClicked, m_deepView, &SiteGroundTemperatureDeepWidget::toggleUnitsClicked); // Auto-select first entry after the event loop starts QTimer::singleShot(0, this, [this]() { m_listView->selectFirst(); }); @@ -221,7 +238,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_notPresentView->setType(type, tr("Site:GroundTemperature:BuildingSurface"), m_model); m_rightStack->setCurrentIndex(0); } - } else { + } else if (type == GroundTempType::Shallow) { auto opt = m_model.getOptionalUniqueModelObject(); if (opt) { m_shView->attach(*opt); @@ -230,6 +247,15 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_notPresentView->setType(type, tr("Site:GroundTemperature:Shallow"), m_model); m_rightStack->setCurrentIndex(0); } + } else { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_deepView->attach(*opt); + m_rightStack->setCurrentIndex(3); + } else { + m_notPresentView->setType(type, tr("Site:GroundTemperature:Deep"), m_model); + m_rightStack->setCurrentIndex(0); + } } } @@ -238,10 +264,14 @@ void GroundTemperatureView::onObjectCreated(GroundTempType type) { auto obj = m_model.getUniqueModelObject(); m_bsView->attach(obj); m_rightStack->setCurrentIndex(1); - } else { + } else if (type == GroundTempType::Shallow) { auto obj = m_model.getUniqueModelObject(); m_shView->attach(obj); m_rightStack->setCurrentIndex(2); + } else { + auto obj = m_model.getUniqueModelObject(); + m_deepView->attach(obj); + m_rightStack->setCurrentIndex(3); } } diff --git a/src/openstudio_lib/GroundTemperatureView.hpp b/src/openstudio_lib/GroundTemperatureView.hpp index 65b721e9e..5f0293b07 100644 --- a/src/openstudio_lib/GroundTemperatureView.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -21,7 +21,8 @@ namespace openstudio { enum class GroundTempType { BuildingSurface, - Shallow + Shallow, + Deep }; /** A single clickable entry in the left-hand list (ScheduleTabDefault style). */ @@ -67,10 +68,12 @@ class GroundTemperatureListView : public QWidget private slots: void onBuildingSurfaceClicked(); void onShallowClicked(); + void onDeepClicked(); private: GroundTemperatureEntry* m_bsEntry = nullptr; GroundTemperatureEntry* m_shEntry = nullptr; + GroundTemperatureEntry* m_deepEntry = nullptr; }; /** Right pane shown when the selected object is not yet in the model. */ @@ -115,6 +118,7 @@ class GroundTemperatureView : public QWidget GroundTemperatureNotPresentView* m_notPresentView = nullptr; SiteGroundTemperatureBuildingSurfaceWidget* m_bsView = nullptr; SiteGroundTemperatureShallowWidget* m_shView = nullptr; + SiteGroundTemperatureDeepWidget* m_deepView = nullptr; QStackedWidget* m_rightStack = nullptr; }; From 82ea35ff2b15cbc22bf17f30bee23fae9d0f3e0c Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 13:36:19 +0100 Subject: [PATCH 11/90] Add ability to import Site:GroundTemperature:Shallow/Deep from EPW if found --- src/openstudio_lib/GroundTemperatureView.cpp | 118 ++++++++++++++++++- src/openstudio_lib/GroundTemperatureView.hpp | 1 + 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index 871257e72..f8b732597 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -5,9 +5,16 @@ #include "GroundTemperatureView.hpp" +#include "OSAppBase.hpp" +#include "OSDocument.hpp" +#include "../model_editor/Utilities.hpp" + #include #include #include +#include +#include +#include #include #include @@ -156,6 +163,11 @@ GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent m_addButton->setMinimumWidth(100); layout->addWidget(m_addButton, 0, Qt::AlignLeft); + m_importFromEPWButton = new QPushButton(tr("Import from EPW")); + m_importFromEPWButton->setObjectName("StandardGrayButton"); + m_importFromEPWButton->setEnabled(false); + layout->addWidget(m_importFromEPWButton, 0, Qt::AlignLeft); + layout->addStretch(); connect(m_addButton, &QPushButton::clicked, this, [this]() { emit addClicked(m_type); }); @@ -164,7 +176,111 @@ GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString& typeName, model::Model model) { m_type = type; m_model = std::move(model); - m_label->setText(tr("The %1 Unique ModelObject is not present in this model. Click Add to instantiate it.").arg(typeName)); + + disconnect(m_importFromEPWButton, &QPushButton::clicked, nullptr, nullptr); + + QString label = tr("The %1 Unique ModelObject is not present in this model. Click Add to instantiate it.").arg(typeName); + if (type == GroundTempType::BuildingSurface) { + m_label->setText(label); + m_importFromEPWButton->setVisible(false); + return; + } + + m_importFromEPWButton->setVisible(true); + + boost::optional weatherFile_ = m_model.weatherFile(); + if (!weatherFile_) { + label.append(tr(" No weather file is associated with the model, so the object will be added with default values.")); + m_label->setText(label); + return; + } + + openstudio::path filesDir; + { + auto companionFolder = toPath(OSAppBase::instance()->currentDocument()->modelTempDir()); + // auto savePath = OSAppBase::instance()->currentDocument()->savePath(); + // if (!savePath.isEmpty()) { + // openstudio::path companionFolder = getCompanionFolder(toPath(savePath)); + filesDir = companionFolder / toPath("resources/files/"); + } + + boost::optional epwFile_ = weatherFile_->file(filesDir); + if (!epwFile_) { + + label.append(tr(" While a weather file is associated with the model, could not locate the underlying EpwFile, so the object will be added with " + "default values.")); + m_label->setText(label); + return; + } + + std::vector ground_temps = epwFile_->groundTemperatureDepths(); + if (ground_temps.empty()) { + label.append(tr(" The weather file does not contain any ground temperature data, so the object will be added with default values.")); + m_label->setText(label); + return; + } + + double target_depth = -999; + if (type == GroundTempType::Shallow) { + target_depth = 0.5; + } else { // GroundTempType::Deep + target_depth = 4.0; + } + // Now try to find the target_depth in the epw data, allowing for some tolerance since the epw spec doesn't require exact depths + const double tolerance = 0.1; + auto it = std::find_if(ground_temps.begin(), ground_temps.end(), [target_depth, tolerance](const EpwGroundTemperatureDepth& gtd) { + return std::abs(gtd.groundTemperatureDepth() - target_depth) < tolerance; + }); + if (it == ground_temps.end()) { + label.append( + tr(" The weather file does not contain ground temperature data at the expected depth of %1 m, so the object will be added with default values.") + .arg(target_depth)); + m_label->setText(label); + return; + } + + label.append(tr(" The weather file contains ground temperature data at a depth of %1 m, so you can choose to import those values or add the object " + "with default values.") + .arg(it->groundTemperatureDepth())); + + m_importFromEPWButton->setEnabled(true); + // Connect button to a lambda that creates the object directly and fills up the value + connect(m_importFromEPWButton, &QPushButton::clicked, this, [this, type, it]() { + if (type == GroundTempType::Shallow) { + auto ts = m_model.getUniqueModelObject(); + + ts.setJanuarySurfaceGroundTemperature(it->janGroundTemperature()); + ts.setFebruarySurfaceGroundTemperature(it->febGroundTemperature()); + ts.setMarchSurfaceGroundTemperature(it->marGroundTemperature()); + ts.setAprilSurfaceGroundTemperature(it->aprGroundTemperature()); + ts.setMaySurfaceGroundTemperature(it->mayGroundTemperature()); + ts.setJuneSurfaceGroundTemperature(it->junGroundTemperature()); + ts.setJulySurfaceGroundTemperature(it->julGroundTemperature()); + ts.setAugustSurfaceGroundTemperature(it->augGroundTemperature()); + ts.setSeptemberSurfaceGroundTemperature(it->sepGroundTemperature()); + ts.setOctoberSurfaceGroundTemperature(it->octGroundTemperature()); + ts.setNovemberSurfaceGroundTemperature(it->novGroundTemperature()); + ts.setDecemberSurfaceGroundTemperature(it->decGroundTemperature()); + } else { // GroundTempType::Deep + auto td = m_model.getUniqueModelObject(); + + td.setJanuaryDeepGroundTemperature(it->janGroundTemperature()); + td.setFebruaryDeepGroundTemperature(it->febGroundTemperature()); + td.setMarchDeepGroundTemperature(it->marGroundTemperature()); + td.setAprilDeepGroundTemperature(it->aprGroundTemperature()); + td.setMayDeepGroundTemperature(it->mayGroundTemperature()); + td.setJuneDeepGroundTemperature(it->junGroundTemperature()); + td.setJulyDeepGroundTemperature(it->julGroundTemperature()); + td.setAugustDeepGroundTemperature(it->augGroundTemperature()); + td.setSeptemberDeepGroundTemperature(it->sepGroundTemperature()); + td.setOctoberDeepGroundTemperature(it->octGroundTemperature()); + td.setNovemberDeepGroundTemperature(it->novGroundTemperature()); + td.setDecemberDeepGroundTemperature(it->decGroundTemperature()); + } + emit addClicked(type); + }); + + m_label->setText(label); } // ───────────────────────────────────────────────────────── diff --git a/src/openstudio_lib/GroundTemperatureView.hpp b/src/openstudio_lib/GroundTemperatureView.hpp index 5f0293b07..5d4871610 100644 --- a/src/openstudio_lib/GroundTemperatureView.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -92,6 +92,7 @@ class GroundTemperatureNotPresentView : public QWidget private: QLabel* m_label = nullptr; QPushButton* m_addButton = nullptr; + QPushButton* m_importFromEPWButton = nullptr; GroundTempType m_type = GroundTempType::BuildingSurface; model::Model m_model; }; From 522676c603f788dfd7de1611b224fb923a3f7191 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 13:55:16 +0100 Subject: [PATCH 12/90] Switch m_isIP when toggle Units clicked. --- src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index fc97c2040..d9c2538cb 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -59,6 +59,8 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP gridLayout->addWidget(m_edits[i], i + 1, 1, Qt::AlignLeft); } + connect(this, &SiteGroundTemperatureMonthlyWidget::toggleUnitsClicked, this, [this](bool isIP) { m_isIP = isIP; }); + mainLayout->addStretch(); } From ae6d2ab313b033772e20ba62025662260cc5f686 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 14:14:40 +0100 Subject: [PATCH 13/90] Fixup (existing) bug that didn't update m_isIP in LocationTabController --- src/openstudio_lib/LocationTabController.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/openstudio_lib/LocationTabController.cpp b/src/openstudio_lib/LocationTabController.cpp index 30fffea96..03b6241d6 100644 --- a/src/openstudio_lib/LocationTabController.cpp +++ b/src/openstudio_lib/LocationTabController.cpp @@ -38,6 +38,7 @@ LocationTabController::LocationTabController(bool isIP, const model::Model& mode this->mainContentWidget()->setSubTab(locationView); m_currentView = locationView; + connect(this, &LocationTabController::toggleUnitsClicked, this, [this](bool isIP) { m_isIP = isIP; }); connect(this->mainContentWidget(), &MainTabView::tabSelected, this, &LocationTabController::setSubTab); } @@ -103,10 +104,10 @@ void LocationTabController::setSubTab(int index) { break; } case 3: { - auto* view = new GroundTemperatureView(m_isIP, m_model); - connect(this, &LocationTabController::toggleUnitsClicked, view, &GroundTemperatureView::toggleUnitsClicked); - this->mainContentWidget()->setSubTab(view); - m_currentView = view; + auto* gtView = new GroundTemperatureView(m_isIP, m_model); + connect(this, &LocationTabController::toggleUnitsClicked, gtView, &GroundTemperatureView::toggleUnitsClicked); + this->mainContentWidget()->setSubTab(gtView); + m_currentView = gtView; break; } default: From c4cf313d71bd05d656182e9845dde34f7734f390 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 3 Mar 2026 15:15:29 +0100 Subject: [PATCH 14/90] Initial UI for Site:WaterMains:Temperature --- src/openstudio_lib/CMakeLists.txt | 3 + src/openstudio_lib/GroundTemperatureView.cpp | 38 +++- src/openstudio_lib/GroundTemperatureView.hpp | 7 +- .../MainRightColumnController.cpp | 38 ++++ .../SiteWaterMainsTemperatureWidget.cpp | 181 ++++++++++++++++++ .../SiteWaterMainsTemperatureWidget.hpp | 65 +++++++ 6 files changed, 328 insertions(+), 4 deletions(-) create mode 100644 src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp create mode 100644 src/openstudio_lib/SiteWaterMainsTemperatureWidget.hpp diff --git a/src/openstudio_lib/CMakeLists.txt b/src/openstudio_lib/CMakeLists.txt index 65a9bb9b6..e59dfa77b 100644 --- a/src/openstudio_lib/CMakeLists.txt +++ b/src/openstudio_lib/CMakeLists.txt @@ -282,6 +282,8 @@ set(${target_name}_SRC GroundTemperatureMonthlyInspectorView.hpp GroundTemperatureView.cpp GroundTemperatureView.hpp + SiteWaterMainsTemperatureWidget.cpp + SiteWaterMainsTemperatureWidget.hpp SimSettingsTabView.cpp SimSettingsTabView.hpp SimSettingsView.cpp @@ -641,6 +643,7 @@ set(${target_name}_moc SimSettingsView.hpp GroundTemperatureMonthlyInspectorView.hpp GroundTemperatureView.hpp + SiteWaterMainsTemperatureWidget.hpp SpaceLoadInstancesWidget.hpp SpacesDaylightingGridView.hpp SpacesInteriorPartitionsGridView.hpp diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index f8b732597..bd360797e 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -108,14 +109,17 @@ GroundTemperatureListView::GroundTemperatureListView(QWidget* parent) : QWidget( m_bsEntry = new GroundTemperatureEntry(tr("Building Surface Ground Temperatures"), this); m_shEntry = new GroundTemperatureEntry(tr("Shallow Ground Temperatures"), this); m_deepEntry = new GroundTemperatureEntry(tr("Deep Ground Temperatures"), this); + m_waterMainsEntry = new GroundTemperatureEntry(tr("Water Mains Temperature"), this); connect(m_bsEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onBuildingSurfaceClicked); connect(m_shEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onShallowClicked); connect(m_deepEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onDeepClicked); + connect(m_waterMainsEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onWaterMainsClicked); layout->addWidget(m_bsEntry); layout->addWidget(m_shEntry); layout->addWidget(m_deepEntry); + layout->addWidget(m_waterMainsEntry); layout->addStretch(); } @@ -127,6 +131,7 @@ void GroundTemperatureListView::onBuildingSurfaceClicked() { m_bsEntry->setSelected(true); m_shEntry->setSelected(false); m_deepEntry->setSelected(false); + m_waterMainsEntry->setSelected(false); emit typeSelected(GroundTempType::BuildingSurface); } @@ -134,6 +139,7 @@ void GroundTemperatureListView::onShallowClicked() { m_bsEntry->setSelected(false); m_shEntry->setSelected(true); m_deepEntry->setSelected(false); + m_waterMainsEntry->setSelected(false); emit typeSelected(GroundTempType::Shallow); } @@ -141,9 +147,18 @@ void GroundTemperatureListView::onDeepClicked() { m_bsEntry->setSelected(false); m_shEntry->setSelected(false); m_deepEntry->setSelected(true); + m_waterMainsEntry->setSelected(false); emit typeSelected(GroundTempType::Deep); } +void GroundTemperatureListView::onWaterMainsClicked() { + m_bsEntry->setSelected(false); + m_shEntry->setSelected(false); + m_deepEntry->setSelected(false); + m_waterMainsEntry->setSelected(true); + emit typeSelected(GroundTempType::WaterMains); +} + // ───────────────────────────────────────────────────────── // GroundTemperatureNotPresentView // ───────────────────────────────────────────────────────── @@ -180,7 +195,7 @@ void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString disconnect(m_importFromEPWButton, &QPushButton::clicked, nullptr, nullptr); QString label = tr("The %1 Unique ModelObject is not present in this model. Click Add to instantiate it.").arg(typeName); - if (type == GroundTempType::BuildingSurface) { + if (type == GroundTempType::BuildingSurface || type == GroundTempType::WaterMains) { m_label->setText(label); m_importFromEPWButton->setVisible(false); return; @@ -332,6 +347,9 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode m_deepView = new SiteGroundTemperatureDeepWidget(isIP); m_rightStack->addWidget(m_deepView); // index 3 + m_waterMainsView = new SiteWaterMainsTemperatureWidget(isIP); + m_rightStack->addWidget(m_waterMainsView); // index 4 + connect(m_listView, &GroundTemperatureListView::typeSelected, this, &GroundTemperatureView::onTypeSelected); connect(m_notPresentView, &GroundTemperatureNotPresentView::addClicked, this, &GroundTemperatureView::onObjectCreated); @@ -339,6 +357,7 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode connect(this, &GroundTemperatureView::toggleUnitsClicked, m_bsView, &SiteGroundTemperatureBuildingSurfaceWidget::toggleUnitsClicked); connect(this, &GroundTemperatureView::toggleUnitsClicked, m_shView, &SiteGroundTemperatureShallowWidget::toggleUnitsClicked); connect(this, &GroundTemperatureView::toggleUnitsClicked, m_deepView, &SiteGroundTemperatureDeepWidget::toggleUnitsClicked); + connect(this, &GroundTemperatureView::toggleUnitsClicked, m_waterMainsView, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked); // Auto-select first entry after the event loop starts QTimer::singleShot(0, this, [this]() { m_listView->selectFirst(); }); @@ -363,7 +382,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_notPresentView->setType(type, tr("Site:GroundTemperature:Shallow"), m_model); m_rightStack->setCurrentIndex(0); } - } else { + } else if (type == GroundTempType::Deep) { auto opt = m_model.getOptionalUniqueModelObject(); if (opt) { m_deepView->attach(*opt); @@ -372,6 +391,15 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_notPresentView->setType(type, tr("Site:GroundTemperature:Deep"), m_model); m_rightStack->setCurrentIndex(0); } + } else { + auto opt = m_model.siteWaterMainsTemperature(); + if (opt) { + m_waterMainsView->attach(*opt); + m_rightStack->setCurrentIndex(4); + } else { + m_notPresentView->setType(type, tr("OS:Site:WaterMainsTemperature"), m_model); + m_rightStack->setCurrentIndex(0); + } } } @@ -384,10 +412,14 @@ void GroundTemperatureView::onObjectCreated(GroundTempType type) { auto obj = m_model.getUniqueModelObject(); m_shView->attach(obj); m_rightStack->setCurrentIndex(2); - } else { + } else if (type == GroundTempType::Deep) { auto obj = m_model.getUniqueModelObject(); m_deepView->attach(obj); m_rightStack->setCurrentIndex(3); + } else { + auto obj = m_model.getUniqueModelObject(); + m_waterMainsView->attach(obj); + m_rightStack->setCurrentIndex(4); } } diff --git a/src/openstudio_lib/GroundTemperatureView.hpp b/src/openstudio_lib/GroundTemperatureView.hpp index 5d4871610..5aec50906 100644 --- a/src/openstudio_lib/GroundTemperatureView.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -7,6 +7,7 @@ #define OPENSTUDIO_GROUNDTEMPERATUREVIEW_HPP #include "GroundTemperatureMonthlyInspectorView.hpp" +#include "SiteWaterMainsTemperatureWidget.hpp" #include @@ -22,7 +23,8 @@ enum class GroundTempType { BuildingSurface, Shallow, - Deep + Deep, + WaterMains }; /** A single clickable entry in the left-hand list (ScheduleTabDefault style). */ @@ -69,11 +71,13 @@ class GroundTemperatureListView : public QWidget void onBuildingSurfaceClicked(); void onShallowClicked(); void onDeepClicked(); + void onWaterMainsClicked(); private: GroundTemperatureEntry* m_bsEntry = nullptr; GroundTemperatureEntry* m_shEntry = nullptr; GroundTemperatureEntry* m_deepEntry = nullptr; + GroundTemperatureEntry* m_waterMainsEntry = nullptr; }; /** Right pane shown when the selected object is not yet in the model. */ @@ -120,6 +124,7 @@ class GroundTemperatureView : public QWidget SiteGroundTemperatureBuildingSurfaceWidget* m_bsView = nullptr; SiteGroundTemperatureShallowWidget* m_shView = nullptr; SiteGroundTemperatureDeepWidget* m_deepView = nullptr; + SiteWaterMainsTemperatureWidget* m_waterMainsView = nullptr; QStackedWidget* m_rightStack = nullptr; }; diff --git a/src/openstudio_lib/MainRightColumnController.cpp b/src/openstudio_lib/MainRightColumnController.cpp index cffb0f17b..f19e3b4d1 100644 --- a/src/openstudio_lib/MainRightColumnController.cpp +++ b/src/openstudio_lib/MainRightColumnController.cpp @@ -220,6 +220,44 @@ void MainRightColumnController::configureForSiteSubTab(int subTabID) { if (subTabID == 0) { doc->closeSidebar(); + } else if (subTabID == 3) { // Ground Temperatures + model::Model lib = doc->componentLibrary(); + + // my model + auto* myModelList = new ModelObjectTypeListView(m_model, true, OSItemType::CollapsibleListHeader, false); + myModelList->setItemsType(OSItemType::LibraryItem); + myModelList->setItemsDraggable(true); + myModelList->setItemsRemoveable(false); + + myModelList->addModelObjectType(IddObjectType::OS_Schedule_File, "Schedule File"); + myModelList->addModelObjectType(IddObjectType::OS_Schedule_VariableInterval, "Variable Interval Schedules"); + myModelList->addModelObjectType(IddObjectType::OS_Schedule_FixedInterval, "Fixed Interval Schedules"); + myModelList->addModelObjectType(IddObjectType::OS_Schedule_Year, "Year Schedules"); + myModelList->addModelObjectType(IddObjectType::OS_Schedule_Constant, "Constant Schedules"); + myModelList->addModelObjectType(IddObjectType::OS_Schedule_Compact, "Compact Schedules"); + myModelList->addModelObjectType(IddObjectType::OS_Schedule_Ruleset, "Ruleset Schedules"); + myModelList->addModelObjectCategoryPlaceholder("Schedules"); + + setMyModelView(myModelList); + + // my library + auto* myLibraryList = new ModelObjectTypeListView(lib, true, OSItemType::CollapsibleListHeader, true); + myLibraryList->setItemsDraggable(true); + myLibraryList->setItemsRemoveable(false); + myLibraryList->setItemsType(OSItemType::LibraryItem); + + myLibraryList->addModelObjectType(IddObjectType::OS_Schedule_File, "Schedule File"); + myLibraryList->addModelObjectType(IddObjectType::OS_Schedule_VariableInterval, "Variable Interval Schedules"); + myLibraryList->addModelObjectType(IddObjectType::OS_Schedule_FixedInterval, "Fixed Interval Schedules"); + myLibraryList->addModelObjectType(IddObjectType::OS_Schedule_Year, "Year Schedules"); + myLibraryList->addModelObjectType(IddObjectType::OS_Schedule_Constant, "Constant Schedules"); + myLibraryList->addModelObjectType(IddObjectType::OS_Schedule_Compact, "Compact Schedules"); + myLibraryList->addModelObjectType(IddObjectType::OS_Schedule_Ruleset, "Ruleset Schedules"); + myLibraryList->addModelObjectCategoryPlaceholder("Schedules"); + + setLibraryView(myLibraryList); + doc->openSidebar(); + } else { doc->openSidebar(); } diff --git a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp new file mode 100644 index 000000000..4831aca61 --- /dev/null +++ b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp @@ -0,0 +1,181 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#include "SiteWaterMainsTemperatureWidget.hpp" + +#include "OSDropZone.hpp" + +#include "../shared_gui_components/OSComboBox.hpp" +#include "../shared_gui_components/OSQuantityEdit.hpp" +#include "../shared_gui_components/FieldMethodTypedefs.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +namespace openstudio { + +SiteWaterMainsTemperatureWidget::SiteWaterMainsTemperatureWidget(bool isIP, QWidget* parent) : QWidget(parent), m_isIP(isIP) { + auto* mainLayout = new QVBoxLayout(); + mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); + mainLayout->setContentsMargins(10, 10, 10, 10); + mainLayout->setSpacing(20); + setLayout(mainLayout); + + auto* titleLabel = new QLabel("OS:Site:WaterMainsTemperature"); + titleLabel->setObjectName("H2"); + mainLayout->addWidget(titleLabel); + + auto* gridLayout = new QGridLayout(); + gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setSpacing(10); + gridLayout->setColumnStretch(1, 1); + mainLayout->addLayout(gridLayout); + + int row = 0; + + // Calculation Method + gridLayout->addWidget(new QLabel(tr("Calculation Method")), row, 0); + m_calculationMethod = new OSComboBox2(); + gridLayout->addWidget(m_calculationMethod, row++, 1, Qt::AlignLeft); + + // Temperature Schedule (visible only when method == "Schedule") + m_scheduleLabel = new QLabel(tr("Temperature Schedule")); + gridLayout->addWidget(m_scheduleLabel, row, 0); + m_scheduleDropZone = new OSDropZone2(); + gridLayout->addWidget(m_scheduleDropZone, row++, 1); + + // Annual Average Outdoor Air Temperature (visible only when method == "Correlation") + m_annualAvgLabel = new QLabel(tr("Annual Average Outdoor Air Temperature")); + gridLayout->addWidget(m_annualAvgLabel, row, 0); + m_annualAvgTemp = new OSQuantityEdit2("C", "C", "F", m_isIP); + connect(this, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked, m_annualAvgTemp, &OSQuantityEdit2::onUnitSystemChange); + gridLayout->addWidget(m_annualAvgTemp, row++, 1, Qt::AlignLeft); + + // Maximum Difference In Monthly Average Outdoor Air Temperatures (visible only when method == "Correlation") + m_maxDiffLabel = new QLabel(tr("Maximum Difference In Monthly Average Outdoor Air Temperatures")); + gridLayout->addWidget(m_maxDiffLabel, row, 0); + m_maxDiffTemp = new OSQuantityEdit2("K", "K", "R", m_isIP); + connect(this, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked, m_maxDiffTemp, &OSQuantityEdit2::onUnitSystemChange); + gridLayout->addWidget(m_maxDiffTemp, row++, 1, Qt::AlignLeft); + + // Temperature Multiplier (hidden when method == "Schedule") + m_multiplierLabel = new QLabel(tr("Temperature Multiplier")); + gridLayout->addWidget(m_multiplierLabel, row, 0); + m_multiplier = new OSQuantityEdit2("", "", "", m_isIP); + gridLayout->addWidget(m_multiplier, row++, 1, Qt::AlignLeft); + + // Temperature Offset (hidden when method == "Schedule") + m_offsetLabel = new QLabel(tr("Temperature Offset")); + gridLayout->addWidget(m_offsetLabel, row, 0); + m_offset = new OSQuantityEdit2("K", "K", "R", m_isIP); + connect(this, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked, m_offset, &OSQuantityEdit2::onUnitSystemChange); + gridLayout->addWidget(m_offset, row++, 1, Qt::AlignLeft); + + connect(this, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked, this, [this](bool isIP) { m_isIP = isIP; }); + + // Refresh field visibility whenever the combo selection changes + connect(m_calculationMethod, &QComboBox::currentIndexChanged, this, [this](int) { refreshVisibility(); }); + + mainLayout->addStretch(); +} + +void SiteWaterMainsTemperatureWidget::attach(const model::ModelObject& obj) { + detach(); + m_obj = obj.cast(); + + m_calculationMethod->bind( + *m_obj, + // toString + [](const std::string& s) -> std::string { return s; }, + // choices + []() -> std::vector { return model::SiteWaterMainsTemperature::calculationMethodValues(); }, + // getter + [this]() -> std::string { return m_obj->calculationMethod(); }, + // setter + [this](const std::string& s) -> bool { return m_obj->setCalculationMethod(s); }, + // reset + boost::none, + // isDefaulted + boost::none); + + m_scheduleDropZone->bind( + // modelObject + *m_obj, + // get + OptionalModelObjectGetter([this]() -> boost::optional { + auto opt = m_obj->temperatureSchedule(); + if (opt) { + return boost::optional(opt.get()); + } + return boost::none; + }), + // set + ModelObjectSetter([this](const model::ModelObject& mo) -> bool { + auto sch = mo.optionalCast(); + if (!sch) { + return false; + } + return m_obj->setTemperatureSchedule(sch.get()); + }), + // reset + boost::optional([this]() { m_obj->resetTemperatureSchedule(); })); + + m_annualAvgTemp->bind(m_isIP, *m_obj, OptionalDoubleGetter([this]() { return m_obj->annualAverageOutdoorAirTemperature(); }), + boost::optional([this](double v) { return m_obj->setAnnualAverageOutdoorAirTemperature(v); }), + boost::optional([this]() { m_obj->resetAnnualAverageOutdoorAirTemperature(); })); + + m_maxDiffTemp->bind( + m_isIP, *m_obj, OptionalDoubleGetter([this]() { return m_obj->maximumDifferenceInMonthlyAverageOutdoorAirTemperatures(); }), + boost::optional([this](double v) { return m_obj->setMaximumDifferenceInMonthlyAverageOutdoorAirTemperatures(v); }), + boost::optional([this]() { m_obj->resetMaximumDifferenceInMonthlyAverageOutdoorAirTemperatures(); })); + + m_multiplier->bind(m_isIP, *m_obj, DoubleGetter([this]() { return m_obj->temperatureMultiplier(); }), + boost::optional([this](double v) { return m_obj->setTemperatureMultiplier(v); })); + + m_offset->bind(m_isIP, *m_obj, DoubleGetter([this]() { return m_obj->temperatureOffset(); }), + boost::optional([this](double v) { return m_obj->setTemperatureOffset(v); })); + + refreshVisibility(); +} + +void SiteWaterMainsTemperatureWidget::detach() { + m_calculationMethod->unbind(); + if (m_obj) { + // OSDropZone2::unbind() crashes if it was never bound (no guard on m_modelObject) + m_scheduleDropZone->unbind(); + } + m_annualAvgTemp->unbind(); + m_maxDiffTemp->unbind(); + m_multiplier->unbind(); + m_offset->unbind(); + m_obj = boost::none; +} + +void SiteWaterMainsTemperatureWidget::refreshVisibility() { + const QString method = m_calculationMethod->currentText(); + const bool isSchedule = (method == "Schedule"); + const bool isCorrelation = (method == "Correlation"); + + m_scheduleLabel->setVisible(isSchedule); + m_scheduleDropZone->setVisible(isSchedule); + + m_annualAvgLabel->setVisible(isCorrelation); + m_annualAvgTemp->setVisible(isCorrelation); + m_maxDiffLabel->setVisible(isCorrelation); + m_maxDiffTemp->setVisible(isCorrelation); + + m_multiplierLabel->setVisible(!isSchedule); + m_multiplier->setVisible(!isSchedule); + m_offsetLabel->setVisible(!isSchedule); + m_offset->setVisible(!isSchedule); +} + +} // namespace openstudio diff --git a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.hpp b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.hpp new file mode 100644 index 000000000..0d227f056 --- /dev/null +++ b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.hpp @@ -0,0 +1,65 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#ifndef OPENSTUDIO_SITEWATERMAINSTEMPERATUREWIDGET_HPP +#define OPENSTUDIO_SITEWATERMAINSTEMPERATUREWIDGET_HPP + +#include + +#include + +#include + +class QLabel; + +namespace openstudio { + +class OSComboBox2; +class OSDropZone2; +class OSQuantityEdit2; + +/** Inspector widget for OS:Site:WaterMainsTemperature. */ +class SiteWaterMainsTemperatureWidget : public QWidget +{ + Q_OBJECT + + public: + explicit SiteWaterMainsTemperatureWidget(bool isIP, QWidget* parent = nullptr); + + void attach(const model::ModelObject& obj); + void detach(); + + signals: + void toggleUnitsClicked(bool displayIP); + + private slots: + void refreshVisibility(); + + private: + bool m_isIP; + boost::optional m_obj; + + OSComboBox2* m_calculationMethod = nullptr; + + // Visible only when method == "Schedule" + QLabel* m_scheduleLabel = nullptr; + OSDropZone2* m_scheduleDropZone = nullptr; + + // Visible only when method == "Correlation" + QLabel* m_annualAvgLabel = nullptr; + OSQuantityEdit2* m_annualAvgTemp = nullptr; + QLabel* m_maxDiffLabel = nullptr; + OSQuantityEdit2* m_maxDiffTemp = nullptr; + + // Hidden when method == "Schedule" + QLabel* m_multiplierLabel = nullptr; + OSQuantityEdit2* m_multiplier = nullptr; + QLabel* m_offsetLabel = nullptr; + OSQuantityEdit2* m_offset = nullptr; +}; + +} // namespace openstudio + +#endif // OPENSTUDIO_SITEWATERMAINSTEMPERATUREWIDGET_HPP From 14697d51cc09eecdf447c461389b5fb6ad37558e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 10:45:20 +0100 Subject: [PATCH 15/90] Tweak the layout --- .../SiteWaterMainsTemperatureWidget.cpp | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp index 4831aca61..c96398c21 100644 --- a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp +++ b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp @@ -36,48 +36,60 @@ SiteWaterMainsTemperatureWidget::SiteWaterMainsTemperatureWidget(bool isIP, QWid auto* gridLayout = new QGridLayout(); gridLayout->setContentsMargins(0, 0, 0, 0); gridLayout->setSpacing(10); + gridLayout->setColumnStretch(0, 1); gridLayout->setColumnStretch(1, 1); mainLayout->addLayout(gridLayout); int row = 0; - // Calculation Method - gridLayout->addWidget(new QLabel(tr("Calculation Method")), row, 0); + // Calculation Method — label above, combobox left-aligned + auto* methodLabel = new QLabel(tr("Calculation Method")); + methodLabel->setObjectName("H2"); + gridLayout->addWidget(methodLabel, row++, 0, 1, 2); + m_calculationMethod = new OSComboBox2(); - gridLayout->addWidget(m_calculationMethod, row++, 1, Qt::AlignLeft); + gridLayout->addWidget(m_calculationMethod, row++, 0, Qt::AlignLeft); // Temperature Schedule (visible only when method == "Schedule") m_scheduleLabel = new QLabel(tr("Temperature Schedule")); - gridLayout->addWidget(m_scheduleLabel, row, 0); + m_scheduleLabel->setObjectName("H2"); + gridLayout->addWidget(m_scheduleLabel, row++, 0, 1, 2); + m_scheduleDropZone = new OSDropZone2(); - gridLayout->addWidget(m_scheduleDropZone, row++, 1); + gridLayout->addWidget(m_scheduleDropZone, row++, 0, 1, 2); - // Annual Average Outdoor Air Temperature (visible only when method == "Correlation") + // Annual Average Outdoor Air Temperature and Max Diff (visible only when method == "Correlation") m_annualAvgLabel = new QLabel(tr("Annual Average Outdoor Air Temperature")); + m_annualAvgLabel->setObjectName("H2"); gridLayout->addWidget(m_annualAvgLabel, row, 0); + + m_maxDiffLabel = new QLabel(tr("Maximum Difference In Monthly Average\nOutdoor Air Temperatures")); + m_maxDiffLabel->setObjectName("H2"); + gridLayout->addWidget(m_maxDiffLabel, row++, 1); + m_annualAvgTemp = new OSQuantityEdit2("C", "C", "F", m_isIP); connect(this, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked, m_annualAvgTemp, &OSQuantityEdit2::onUnitSystemChange); - gridLayout->addWidget(m_annualAvgTemp, row++, 1, Qt::AlignLeft); + gridLayout->addWidget(m_annualAvgTemp, row, 0); - // Maximum Difference In Monthly Average Outdoor Air Temperatures (visible only when method == "Correlation") - m_maxDiffLabel = new QLabel(tr("Maximum Difference In Monthly Average Outdoor Air Temperatures")); - gridLayout->addWidget(m_maxDiffLabel, row, 0); m_maxDiffTemp = new OSQuantityEdit2("K", "K", "R", m_isIP); connect(this, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked, m_maxDiffTemp, &OSQuantityEdit2::onUnitSystemChange); - gridLayout->addWidget(m_maxDiffTemp, row++, 1, Qt::AlignLeft); + gridLayout->addWidget(m_maxDiffTemp, row++, 1); - // Temperature Multiplier (hidden when method == "Schedule") + // Temperature Multiplier and Offset (hidden when method == "Schedule") m_multiplierLabel = new QLabel(tr("Temperature Multiplier")); + m_multiplierLabel->setObjectName("H2"); gridLayout->addWidget(m_multiplierLabel, row, 0); - m_multiplier = new OSQuantityEdit2("", "", "", m_isIP); - gridLayout->addWidget(m_multiplier, row++, 1, Qt::AlignLeft); - // Temperature Offset (hidden when method == "Schedule") m_offsetLabel = new QLabel(tr("Temperature Offset")); - gridLayout->addWidget(m_offsetLabel, row, 0); + m_offsetLabel->setObjectName("H2"); + gridLayout->addWidget(m_offsetLabel, row++, 1); + + m_multiplier = new OSQuantityEdit2("", "", "", m_isIP); + gridLayout->addWidget(m_multiplier, row, 0); + m_offset = new OSQuantityEdit2("K", "K", "R", m_isIP); connect(this, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked, m_offset, &OSQuantityEdit2::onUnitSystemChange); - gridLayout->addWidget(m_offset, row++, 1, Qt::AlignLeft); + gridLayout->addWidget(m_offset, row++, 1); connect(this, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked, this, [this](bool isIP) { m_isIP = isIP; }); From 731a258c2c133787361f3c1c954ca39b3352d67f Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 11:01:13 +0100 Subject: [PATCH 16/90] Style the OSDropZone2 --- src/openstudio_lib/OSDropZone.cpp | 8 ++++++-- src/openstudio_lib/OSDropZone.hpp | 2 ++ src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/openstudio_lib/OSDropZone.cpp b/src/openstudio_lib/OSDropZone.cpp index ebef87824..efd164bca 100644 --- a/src/openstudio_lib/OSDropZone.cpp +++ b/src/openstudio_lib/OSDropZone.cpp @@ -561,6 +561,10 @@ bool OSDropZone2::deleteObject() const { return m_deleteObject; } +void OSDropZone2::setPlaceholderText(const QString& text) { + m_placeholderText = text; +} + void OSDropZone2::refresh() { boost::optional getterResult = updateGetterResult(); @@ -582,8 +586,8 @@ void OSDropZone2::refresh() { //setFixedWidth(width + 10); } else { - if (!m_label->text().isEmpty()) { - m_label->setText(""); + if (m_label->text() != m_placeholderText) { + m_label->setText(m_placeholderText); m_label->setToolTip(""); } } diff --git a/src/openstudio_lib/OSDropZone.hpp b/src/openstudio_lib/OSDropZone.hpp index e71ae2cda..a1f20a383 100644 --- a/src/openstudio_lib/OSDropZone.hpp +++ b/src/openstudio_lib/OSDropZone.hpp @@ -57,6 +57,7 @@ class OSDropZone2 void setLocked(bool locked); void setDeleteObject(bool deleteObject); bool deleteObject() const; + void setPlaceholderText(const QString& text); void bind(const model::ModelObject& modelObject, OptionalModelObjectGetter get, ModelObjectSetter set, boost::optional reset = boost::none, boost::optional isDefaulted = boost::none, @@ -119,6 +120,7 @@ class OSDropZone2 OSItem* m_item = nullptr; bool m_deleteObject = false; QLabel* m_label; + QString m_placeholderText; }; class OSDropZone diff --git a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp index c96398c21..1d55b6f12 100644 --- a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp +++ b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp @@ -6,6 +6,7 @@ #include "SiteWaterMainsTemperatureWidget.hpp" #include "OSDropZone.hpp" +#include "OSItem.hpp" #include "../shared_gui_components/OSComboBox.hpp" #include "../shared_gui_components/OSQuantityEdit.hpp" @@ -56,6 +57,9 @@ SiteWaterMainsTemperatureWidget::SiteWaterMainsTemperatureWidget(bool isIP, QWid gridLayout->addWidget(m_scheduleLabel, row++, 0, 1, 2); m_scheduleDropZone = new OSDropZone2(); + m_scheduleDropZone->setPlaceholderText(tr("Drag From Library")); + m_scheduleDropZone->setFixedHeight(OSItem::ITEM_HEIGHT); + m_scheduleDropZone->setMaximumWidth(OSItem::ITEM_WIDTH + 20); gridLayout->addWidget(m_scheduleDropZone, row++, 0, 1, 2); // Annual Average Outdoor Air Temperature and Max Diff (visible only when method == "Correlation") From fc4af9da4fca1c8af18d2b5f8d2aba0c3df173de Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 11:01:29 +0100 Subject: [PATCH 17/90] Revert "Style the OSDropZone2" - I think it was a mistake to use that and not OSDropZone... This reverts commit 731a258c2c133787361f3c1c954ca39b3352d67f. --- src/openstudio_lib/OSDropZone.cpp | 8 ++------ src/openstudio_lib/OSDropZone.hpp | 2 -- src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp | 4 ---- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/openstudio_lib/OSDropZone.cpp b/src/openstudio_lib/OSDropZone.cpp index efd164bca..ebef87824 100644 --- a/src/openstudio_lib/OSDropZone.cpp +++ b/src/openstudio_lib/OSDropZone.cpp @@ -561,10 +561,6 @@ bool OSDropZone2::deleteObject() const { return m_deleteObject; } -void OSDropZone2::setPlaceholderText(const QString& text) { - m_placeholderText = text; -} - void OSDropZone2::refresh() { boost::optional getterResult = updateGetterResult(); @@ -586,8 +582,8 @@ void OSDropZone2::refresh() { //setFixedWidth(width + 10); } else { - if (m_label->text() != m_placeholderText) { - m_label->setText(m_placeholderText); + if (!m_label->text().isEmpty()) { + m_label->setText(""); m_label->setToolTip(""); } } diff --git a/src/openstudio_lib/OSDropZone.hpp b/src/openstudio_lib/OSDropZone.hpp index a1f20a383..e71ae2cda 100644 --- a/src/openstudio_lib/OSDropZone.hpp +++ b/src/openstudio_lib/OSDropZone.hpp @@ -57,7 +57,6 @@ class OSDropZone2 void setLocked(bool locked); void setDeleteObject(bool deleteObject); bool deleteObject() const; - void setPlaceholderText(const QString& text); void bind(const model::ModelObject& modelObject, OptionalModelObjectGetter get, ModelObjectSetter set, boost::optional reset = boost::none, boost::optional isDefaulted = boost::none, @@ -120,7 +119,6 @@ class OSDropZone2 OSItem* m_item = nullptr; bool m_deleteObject = false; QLabel* m_label; - QString m_placeholderText; }; class OSDropZone diff --git a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp index 1d55b6f12..c96398c21 100644 --- a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp +++ b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp @@ -6,7 +6,6 @@ #include "SiteWaterMainsTemperatureWidget.hpp" #include "OSDropZone.hpp" -#include "OSItem.hpp" #include "../shared_gui_components/OSComboBox.hpp" #include "../shared_gui_components/OSQuantityEdit.hpp" @@ -57,9 +56,6 @@ SiteWaterMainsTemperatureWidget::SiteWaterMainsTemperatureWidget(bool isIP, QWid gridLayout->addWidget(m_scheduleLabel, row++, 0, 1, 2); m_scheduleDropZone = new OSDropZone2(); - m_scheduleDropZone->setPlaceholderText(tr("Drag From Library")); - m_scheduleDropZone->setFixedHeight(OSItem::ITEM_HEIGHT); - m_scheduleDropZone->setMaximumWidth(OSItem::ITEM_WIDTH + 20); gridLayout->addWidget(m_scheduleDropZone, row++, 0, 1, 2); // Annual Average Outdoor Air Temperature and Max Diff (visible only when method == "Correlation") From eefb94f453e729e915ed68d2acdcfcf7aecd858b Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 11:10:41 +0100 Subject: [PATCH 18/90] Replace OSDropZone2 with OSDropZone --- .../SiteWaterMainsTemperatureWidget.cpp | 83 +++++++++++++------ .../SiteWaterMainsTemperatureWidget.hpp | 19 ++++- 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp index c96398c21..9835e5c53 100644 --- a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp +++ b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp @@ -6,6 +6,7 @@ #include "SiteWaterMainsTemperatureWidget.hpp" #include "OSDropZone.hpp" +#include "ModelObjectItem.hpp" #include "../shared_gui_components/OSComboBox.hpp" #include "../shared_gui_components/OSQuantityEdit.hpp" @@ -15,6 +16,8 @@ #include #include +#include + #include #include #include @@ -22,6 +25,52 @@ namespace openstudio { +// TemperatureScheduleVC + +void TemperatureScheduleVC::onChangeRelationship(const model::ModelObject& modelObject, int index, Handle /*newHandle*/, Handle /*oldHandle*/) { + if (index == OS_Site_WaterMainsTemperatureFields::TemperatureScheduleName) { + emit itemIds(makeVector()); + } +} + +std::vector TemperatureScheduleVC::makeVector() { + std::vector result; + if (m_modelObject) { + auto obj = m_modelObject->cast(); + if (auto schedule = obj.temperatureSchedule()) { + result.push_back(modelObjectToItemId(*schedule, false)); + } + } + return result; +} + +void TemperatureScheduleVC::onRemoveItem(OSItem* /*item*/) { + if (m_modelObject) { + m_modelObject->cast().resetTemperatureSchedule(); + } +} + +void TemperatureScheduleVC::onReplaceItem(OSItem* /*currentItem*/, const OSItemId& replacementItemId) { + onDrop(replacementItemId); +} + +void TemperatureScheduleVC::onDrop(const OSItemId& itemId) { + if (m_modelObject) { + auto obj = m_modelObject->cast(); + if (auto modelObject = this->getModelObject(itemId)) { + if (auto schedule = modelObject->optionalCast()) { + if (this->fromComponentLibrary(itemId)) { + modelObject = modelObject->clone(m_modelObject->model()); + schedule = modelObject->cast(); + } + obj.setTemperatureSchedule(*schedule); + } + } + } +} + +// SiteWaterMainsTemperatureWidget + SiteWaterMainsTemperatureWidget::SiteWaterMainsTemperatureWidget(bool isIP, QWidget* parent) : QWidget(parent), m_isIP(isIP) { auto* mainLayout = new QVBoxLayout(); mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); @@ -55,7 +104,11 @@ SiteWaterMainsTemperatureWidget::SiteWaterMainsTemperatureWidget(bool isIP, QWid m_scheduleLabel->setObjectName("H2"); gridLayout->addWidget(m_scheduleLabel, row++, 0, 1, 2); - m_scheduleDropZone = new OSDropZone2(); + m_scheduleVC = new TemperatureScheduleVC(); + m_scheduleDropZone = new OSDropZone(m_scheduleVC); + m_scheduleDropZone->setMinItems(0); + m_scheduleDropZone->setMaxItems(1); + m_scheduleDropZone->setItemsAcceptDrops(true); gridLayout->addWidget(m_scheduleDropZone, row++, 0, 1, 2); // Annual Average Outdoor Air Temperature and Max Diff (visible only when method == "Correlation") @@ -118,27 +171,8 @@ void SiteWaterMainsTemperatureWidget::attach(const model::ModelObject& obj) { // isDefaulted boost::none); - m_scheduleDropZone->bind( - // modelObject - *m_obj, - // get - OptionalModelObjectGetter([this]() -> boost::optional { - auto opt = m_obj->temperatureSchedule(); - if (opt) { - return boost::optional(opt.get()); - } - return boost::none; - }), - // set - ModelObjectSetter([this](const model::ModelObject& mo) -> bool { - auto sch = mo.optionalCast(); - if (!sch) { - return false; - } - return m_obj->setTemperatureSchedule(sch.get()); - }), - // reset - boost::optional([this]() { m_obj->resetTemperatureSchedule(); })); + m_scheduleVC->attach(*m_obj); + m_scheduleVC->reportItems(); m_annualAvgTemp->bind(m_isIP, *m_obj, OptionalDoubleGetter([this]() { return m_obj->annualAverageOutdoorAirTemperature(); }), boost::optional([this](double v) { return m_obj->setAnnualAverageOutdoorAirTemperature(v); }), @@ -160,10 +194,7 @@ void SiteWaterMainsTemperatureWidget::attach(const model::ModelObject& obj) { void SiteWaterMainsTemperatureWidget::detach() { m_calculationMethod->unbind(); - if (m_obj) { - // OSDropZone2::unbind() crashes if it was never bound (no guard on m_modelObject) - m_scheduleDropZone->unbind(); - } + m_scheduleVC->detach(); m_annualAvgTemp->unbind(); m_maxDiffTemp->unbind(); m_multiplier->unbind(); diff --git a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.hpp b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.hpp index 0d227f056..f8ec57545 100644 --- a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.hpp +++ b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.hpp @@ -6,6 +6,8 @@ #ifndef OPENSTUDIO_SITEWATERMAINSTEMPERATUREWIDGET_HPP #define OPENSTUDIO_SITEWATERMAINSTEMPERATUREWIDGET_HPP +#include "ModelObjectVectorController.hpp" + #include #include @@ -17,9 +19,21 @@ class QLabel; namespace openstudio { class OSComboBox2; -class OSDropZone2; +class OSDropZone; class OSQuantityEdit2; +class TemperatureScheduleVC : public ModelObjectVectorController +{ + Q_OBJECT + + protected: + void onChangeRelationship(const model::ModelObject& modelObject, int index, Handle newHandle, Handle oldHandle) override; + std::vector makeVector() override; + void onRemoveItem(OSItem* item) override; + void onReplaceItem(OSItem* currentItem, const OSItemId& replacementItemId) override; + void onDrop(const OSItemId& itemId) override; +}; + /** Inspector widget for OS:Site:WaterMainsTemperature. */ class SiteWaterMainsTemperatureWidget : public QWidget { @@ -45,7 +59,8 @@ class SiteWaterMainsTemperatureWidget : public QWidget // Visible only when method == "Schedule" QLabel* m_scheduleLabel = nullptr; - OSDropZone2* m_scheduleDropZone = nullptr; + TemperatureScheduleVC* m_scheduleVC = nullptr; + OSDropZone* m_scheduleDropZone = nullptr; // Visible only when method == "Correlation" QLabel* m_annualAvgLabel = nullptr; From 52f661a5b9ff799c04ee4710a38861958a51b5ba Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 11:30:14 +0100 Subject: [PATCH 19/90] Style the left list (GroundTemperatureEntry(List)) --- src/openstudio_lib/GroundTemperatureView.cpp | 31 +++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index bd360797e..5670a55b6 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -35,14 +36,16 @@ namespace openstudio { // ───────────────────────────────────────────────────────── GroundTemperatureEntry::GroundTemperatureEntry(const QString& label, QWidget* parent) : QWidget(parent) { - setFixedHeight(25); + setFixedHeight(50); setMouseTracking(true); auto* layout = new QHBoxLayout(); - layout->setContentsMargins(10, 0, 0, 0); + layout->setContentsMargins(9, 0, 9, 0); setLayout(layout); m_label = new QLabel(label); + m_label->setObjectName("H2"); + m_label->setWordWrap(true); m_label->setMouseTracking(true); layout->addWidget(m_label); } @@ -58,16 +61,24 @@ void GroundTemperatureEntry::paintEvent(QPaintEvent* /*event*/) { QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - if (m_hovering || m_selected) { - p.setBrush(QBrush(QColor(207, 207, 207))); - p.setPen(Qt::NoPen); - p.drawRect(0, 0, size().width() - 1, size().height() - 1); + const int w = size().width(); + const int h = size().height(); + + if (m_selected) { + // Gradient matching OSCollapsibleItemHeader selected state + QLinearGradient gradient(0, 0, 0, h); + gradient.setColorAt(0.00, QColor(0x63, 0x61, 0x61)); + gradient.setColorAt(0.10, QColor(0x63, 0x61, 0x61)); + gradient.setColorAt(0.15, QColor(0xA3, 0xA3, 0xA3)); + gradient.setColorAt(1.00, QColor(0xA3, 0xA3, 0xA3)); + p.fillRect(0, 0, w, h, gradient); + } else if (m_hovering) { + p.fillRect(0, 0, w, h, QColor(0xCE, 0xCE, 0xCE)); } - p.setPen(Qt::SolidLine); - p.setBrush(QBrush(QColor(Qt::black))); - p.drawLine(0, 0, size().width(), 0); - p.drawLine(0, size().height() - 1, size().width(), size().height() - 1); + // Bottom border (matches OSCollapsibleItemHeader border-bottom: 1px solid black) + p.setPen(QPen(Qt::black, 1)); + p.drawLine(0, h - 1, w, h - 1); } void GroundTemperatureEntry::mousePressEvent(QMouseEvent* event) { From e0819cffc1bfdf8ca70e235bc48ae7f3f6501626 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 11:39:32 +0100 Subject: [PATCH 20/90] Style the left list via a Stylesheet instead, leaving Qt to handle all rendering. The QSS + property approach is strictly better: - Qt handles all rendering (no manual fillRect/drawLine) - :hover pseudo-selector replaces m_hovering, mouseMoveEvent, leaveEvent - setSelected() just toggles the property + unpolish/polish - paintEvent reduces to the standard QStyleOption boilerplate --- src/openstudio_lib/GroundTemperatureView.cpp | 47 ++++---------------- src/openstudio_lib/GroundTemperatureView.hpp | 4 -- 2 files changed, 9 insertions(+), 42 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index 5670a55b6..404d6ee46 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -17,11 +17,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -37,7 +35,12 @@ namespace openstudio { GroundTemperatureEntry::GroundTemperatureEntry(const QString& label, QWidget* parent) : QWidget(parent) { setFixedHeight(50); - setMouseTracking(true); + setObjectName("GroundTemperatureEntry"); + setProperty("style", "0"); + setStyleSheet("QWidget#GroundTemperatureEntry[style=\"0\"] { background: #CECECE; border-bottom: 1px solid black; }" + "QWidget#GroundTemperatureEntry[style=\"0\"]:hover { background: #BEBEBE; border-bottom: 1px solid black; }" + "QWidget#GroundTemperatureEntry[style=\"1\"] { background: qlineargradient(x1:0,y1:0,x2:0,y2:1," + " stop: 0.0 #636161, stop: 0.10 #636161, stop: 0.15 #A3A3A3, stop: 1.0 #A3A3A3); border-bottom: 1px solid black; }"); auto* layout = new QHBoxLayout(); layout->setContentsMargins(9, 0, 9, 0); @@ -46,13 +49,13 @@ GroundTemperatureEntry::GroundTemperatureEntry(const QString& label, QWidget* pa m_label = new QLabel(label); m_label->setObjectName("H2"); m_label->setWordWrap(true); - m_label->setMouseTracking(true); layout->addWidget(m_label); } void GroundTemperatureEntry::setSelected(bool selected) { - m_selected = selected; - update(); + setProperty("style", selected ? "1" : "0"); + style()->unpolish(this); + style()->polish(this); } void GroundTemperatureEntry::paintEvent(QPaintEvent* /*event*/) { @@ -60,25 +63,6 @@ void GroundTemperatureEntry::paintEvent(QPaintEvent* /*event*/) { opt.initFrom(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - - const int w = size().width(); - const int h = size().height(); - - if (m_selected) { - // Gradient matching OSCollapsibleItemHeader selected state - QLinearGradient gradient(0, 0, 0, h); - gradient.setColorAt(0.00, QColor(0x63, 0x61, 0x61)); - gradient.setColorAt(0.10, QColor(0x63, 0x61, 0x61)); - gradient.setColorAt(0.15, QColor(0xA3, 0xA3, 0xA3)); - gradient.setColorAt(1.00, QColor(0xA3, 0xA3, 0xA3)); - p.fillRect(0, 0, w, h, gradient); - } else if (m_hovering) { - p.fillRect(0, 0, w, h, QColor(0xCE, 0xCE, 0xCE)); - } - - // Bottom border (matches OSCollapsibleItemHeader border-bottom: 1px solid black) - p.setPen(QPen(Qt::black, 1)); - p.drawLine(0, h - 1, w, h - 1); } void GroundTemperatureEntry::mousePressEvent(QMouseEvent* event) { @@ -94,19 +78,6 @@ void GroundTemperatureEntry::mouseReleaseEvent(QMouseEvent* event) { event->accept(); } -void GroundTemperatureEntry::mouseMoveEvent(QMouseEvent* event) { - m_hovering = true; - update(); - event->accept(); -} - -void GroundTemperatureEntry::leaveEvent(QEvent* event) { - m_mouseDown = false; - m_hovering = false; - update(); - event->accept(); -} - // ───────────────────────────────────────────────────────── // GroundTemperatureListView // ───────────────────────────────────────────────────────── diff --git a/src/openstudio_lib/GroundTemperatureView.hpp b/src/openstudio_lib/GroundTemperatureView.hpp index 5aec50906..09da22cc3 100644 --- a/src/openstudio_lib/GroundTemperatureView.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -44,13 +44,9 @@ class GroundTemperatureEntry : public QWidget void paintEvent(QPaintEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void leaveEvent(QEvent* event) override; private: bool m_mouseDown = false; - bool m_hovering = false; - bool m_selected = false; QLabel* m_label = nullptr; }; From aa93262f41c9d47bae822e8a96885f4548174eec Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 11:46:05 +0100 Subject: [PATCH 21/90] Shush cppcheck incorrectly flagging unused --- .../GroundTemperatureMonthlyInspectorView.cpp | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index d9c2538cb..c88bf885b 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -88,10 +88,10 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject struct MonthBinding { - double (BS::*getter)() const; - bool (BS::*setter)(double); - void (BS::*resetter)(); - bool (BS::*defaulted)() const; + double (BS::*getter)() const; // cppcheck-suppress unusedStructMember + bool (BS::*setter)(double); // cppcheck-suppress unusedStructMember + void (BS::*resetter)(); // cppcheck-suppress unusedStructMember + bool (BS::*defaulted)() const; // cppcheck-suppress unusedStructMember }; static const std::array month_binders{{ @@ -194,10 +194,10 @@ void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { struct MonthBinding { - double (SH::*getter)() const; - bool (SH::*setter)(double); - void (SH::*resetter)(); - bool (SH::*defaulted)() const; + double (SH::*getter)() const; // cppcheck-suppress unusedStructMember + bool (SH::*setter)(double); // cppcheck-suppress unusedStructMember + void (SH::*resetter)(); // cppcheck-suppress unusedStructMember + bool (SH::*defaulted)() const; // cppcheck-suppress unusedStructMember }; static const std::array month_binders{{ @@ -295,88 +295,88 @@ void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { m_obj = obj.cast(); m_titleLabel->setText("Site:GroundTemperature:Deep"); - using DW = model::SiteGroundTemperatureDeep; + using DP = model::SiteGroundTemperatureDeep; struct MonthBinding { - double (DW::*getter)() const; - bool (DW::*setter)(double); - void (DW::*resetter)(); - bool (DW::*defaulted)() const; + double (DP::*getter)() const; // cppcheck-suppress unusedStructMember + bool (DP::*setter)(double); // cppcheck-suppress unusedStructMember + void (DP::*resetter)(); // cppcheck-suppress unusedStructMember + bool (DP::*defaulted)() const; // cppcheck-suppress unusedStructMember }; static const std::array month_binders{{ { - &DW::januaryDeepGroundTemperature, - &DW::setJanuaryDeepGroundTemperature, - &DW::resetJanuaryDeepGroundTemperature, - &DW::isJanuaryDeepGroundTemperatureDefaulted, + &DP::januaryDeepGroundTemperature, + &DP::setJanuaryDeepGroundTemperature, + &DP::resetJanuaryDeepGroundTemperature, + &DP::isJanuaryDeepGroundTemperatureDefaulted, }, { - &DW::februaryDeepGroundTemperature, - &DW::setFebruaryDeepGroundTemperature, - &DW::resetFebruaryDeepGroundTemperature, - &DW::isFebruaryDeepGroundTemperatureDefaulted, + &DP::februaryDeepGroundTemperature, + &DP::setFebruaryDeepGroundTemperature, + &DP::resetFebruaryDeepGroundTemperature, + &DP::isFebruaryDeepGroundTemperatureDefaulted, }, { - &DW::marchDeepGroundTemperature, - &DW::setMarchDeepGroundTemperature, - &DW::resetMarchDeepGroundTemperature, - &DW::isMarchDeepGroundTemperatureDefaulted, + &DP::marchDeepGroundTemperature, + &DP::setMarchDeepGroundTemperature, + &DP::resetMarchDeepGroundTemperature, + &DP::isMarchDeepGroundTemperatureDefaulted, }, { - &DW::aprilDeepGroundTemperature, - &DW::setAprilDeepGroundTemperature, - &DW::resetAprilDeepGroundTemperature, - &DW::isAprilDeepGroundTemperatureDefaulted, + &DP::aprilDeepGroundTemperature, + &DP::setAprilDeepGroundTemperature, + &DP::resetAprilDeepGroundTemperature, + &DP::isAprilDeepGroundTemperatureDefaulted, }, { - &DW::mayDeepGroundTemperature, - &DW::setMayDeepGroundTemperature, - &DW::resetMayDeepGroundTemperature, - &DW::isMayDeepGroundTemperatureDefaulted, + &DP::mayDeepGroundTemperature, + &DP::setMayDeepGroundTemperature, + &DP::resetMayDeepGroundTemperature, + &DP::isMayDeepGroundTemperatureDefaulted, }, { - &DW::juneDeepGroundTemperature, - &DW::setJuneDeepGroundTemperature, - &DW::resetJuneDeepGroundTemperature, - &DW::isJuneDeepGroundTemperatureDefaulted, + &DP::juneDeepGroundTemperature, + &DP::setJuneDeepGroundTemperature, + &DP::resetJuneDeepGroundTemperature, + &DP::isJuneDeepGroundTemperatureDefaulted, }, { - &DW::julyDeepGroundTemperature, - &DW::setJulyDeepGroundTemperature, - &DW::resetJulyDeepGroundTemperature, - &DW::isJulyDeepGroundTemperatureDefaulted, + &DP::julyDeepGroundTemperature, + &DP::setJulyDeepGroundTemperature, + &DP::resetJulyDeepGroundTemperature, + &DP::isJulyDeepGroundTemperatureDefaulted, }, { - &DW::augustDeepGroundTemperature, - &DW::setAugustDeepGroundTemperature, - &DW::resetAugustDeepGroundTemperature, - &DW::isAugustDeepGroundTemperatureDefaulted, + &DP::augustDeepGroundTemperature, + &DP::setAugustDeepGroundTemperature, + &DP::resetAugustDeepGroundTemperature, + &DP::isAugustDeepGroundTemperatureDefaulted, }, { - &DW::septemberDeepGroundTemperature, - &DW::setSeptemberDeepGroundTemperature, - &DW::resetSeptemberDeepGroundTemperature, - &DW::isSeptemberDeepGroundTemperatureDefaulted, + &DP::septemberDeepGroundTemperature, + &DP::setSeptemberDeepGroundTemperature, + &DP::resetSeptemberDeepGroundTemperature, + &DP::isSeptemberDeepGroundTemperatureDefaulted, }, { - &DW::octoberDeepGroundTemperature, - &DW::setOctoberDeepGroundTemperature, - &DW::resetOctoberDeepGroundTemperature, - &DW::isOctoberDeepGroundTemperatureDefaulted, + &DP::octoberDeepGroundTemperature, + &DP::setOctoberDeepGroundTemperature, + &DP::resetOctoberDeepGroundTemperature, + &DP::isOctoberDeepGroundTemperatureDefaulted, }, { - &DW::novemberDeepGroundTemperature, - &DW::setNovemberDeepGroundTemperature, - &DW::resetNovemberDeepGroundTemperature, - &DW::isNovemberDeepGroundTemperatureDefaulted, + &DP::novemberDeepGroundTemperature, + &DP::setNovemberDeepGroundTemperature, + &DP::resetNovemberDeepGroundTemperature, + &DP::isNovemberDeepGroundTemperatureDefaulted, }, { - &DW::decemberDeepGroundTemperature, - &DW::setDecemberDeepGroundTemperature, - &DW::resetDecemberDeepGroundTemperature, - &DW::isDecemberDeepGroundTemperatureDefaulted, + &DP::decemberDeepGroundTemperature, + &DP::setDecemberDeepGroundTemperature, + &DP::resetDecemberDeepGroundTemperature, + &DP::isDecemberDeepGroundTemperatureDefaulted, }, }}; From 24579cf14c00e3d251db1881fde2be2f731a0cc8 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 16:11:57 +0100 Subject: [PATCH 22/90] Add a remove button to remove the Unique ModelObjects related to ground temps --- src/openstudio_lib/GroundTemperatureView.cpp | 66 +++++++++++++++++++- src/openstudio_lib/GroundTemperatureView.hpp | 5 ++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index 404d6ee46..973cedf0d 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -7,6 +7,7 @@ #include "OSAppBase.hpp" #include "OSDocument.hpp" +#include "OSItemSelectorButtons.hpp" #include "../model_editor/Utilities.hpp" #include @@ -302,7 +303,16 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode m_listView = new GroundTemperatureListView(leftPane); leftLayout->addWidget(m_listView); - leftLayout->addStretch(); + + m_selectorButtons = new OSItemSelectorButtons(); + m_selectorButtons->hideDropZone(); + m_selectorButtons->showAddButton(); + m_selectorButtons->disableAddButton(); + m_selectorButtons->hideCopyButton(); + m_selectorButtons->hidePurgeButton(); + m_selectorButtons->showRemoveButton(); + m_selectorButtons->disableRemoveButton(); + leftLayout->addWidget(m_selectorButtons); mainLayout->addWidget(leftPane); @@ -334,6 +344,8 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode connect(m_listView, &GroundTemperatureListView::typeSelected, this, &GroundTemperatureView::onTypeSelected); connect(m_notPresentView, &GroundTemperatureNotPresentView::addClicked, this, &GroundTemperatureView::onObjectCreated); + connect(m_selectorButtons, &OSItemSelectorButtons::addClicked, this, [this]() { onObjectCreated(m_currentType); }); + connect(m_selectorButtons, &OSItemSelectorButtons::removeClicked, this, &GroundTemperatureView::onRemoveClicked); // Forward unit toggle to sub-views (signal-to-signal) connect(this, &GroundTemperatureView::toggleUnitsClicked, m_bsView, &SiteGroundTemperatureBuildingSurfaceWidget::toggleUnitsClicked); @@ -346,9 +358,13 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode } void GroundTemperatureView::onTypeSelected(GroundTempType type) { + m_currentType = type; + bool objectExists = false; + if (type == GroundTempType::BuildingSurface) { auto opt = m_model.getOptionalUniqueModelObject(); if (opt) { + objectExists = true; m_bsView->attach(*opt); m_rightStack->setCurrentIndex(1); } else { @@ -358,6 +374,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { } else if (type == GroundTempType::Shallow) { auto opt = m_model.getOptionalUniqueModelObject(); if (opt) { + objectExists = true; m_shView->attach(*opt); m_rightStack->setCurrentIndex(2); } else { @@ -367,6 +384,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { } else if (type == GroundTempType::Deep) { auto opt = m_model.getOptionalUniqueModelObject(); if (opt) { + objectExists = true; m_deepView->attach(*opt); m_rightStack->setCurrentIndex(3); } else { @@ -376,6 +394,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { } else { auto opt = m_model.siteWaterMainsTemperature(); if (opt) { + objectExists = true; m_waterMainsView->attach(*opt); m_rightStack->setCurrentIndex(4); } else { @@ -383,6 +402,14 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_rightStack->setCurrentIndex(0); } } + + if (objectExists) { + m_selectorButtons->enableRemoveButton(); + m_selectorButtons->disableAddButton(); + } else { + m_selectorButtons->disableRemoveButton(); + m_selectorButtons->enableAddButton(); + } } void GroundTemperatureView::onObjectCreated(GroundTempType type) { @@ -403,6 +430,43 @@ void GroundTemperatureView::onObjectCreated(GroundTempType type) { m_waterMainsView->attach(obj); m_rightStack->setCurrentIndex(4); } + m_selectorButtons->enableRemoveButton(); + m_selectorButtons->disableAddButton(); +} + +void GroundTemperatureView::onRemoveClicked() { + if (m_currentType == GroundTempType::BuildingSurface) { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_bsView->detach(); + opt->remove(); + } + m_notPresentView->setType(m_currentType, tr("Site:GroundTemperature:BuildingSurface"), m_model); + } else if (m_currentType == GroundTempType::Shallow) { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_shView->detach(); + opt->remove(); + } + m_notPresentView->setType(m_currentType, tr("Site:GroundTemperature:Shallow"), m_model); + } else if (m_currentType == GroundTempType::Deep) { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_deepView->detach(); + opt->remove(); + } + m_notPresentView->setType(m_currentType, tr("Site:GroundTemperature:Deep"), m_model); + } else { + auto opt = m_model.siteWaterMainsTemperature(); + if (opt) { + m_waterMainsView->detach(); + opt->remove(); + } + m_notPresentView->setType(m_currentType, tr("OS:Site:WaterMainsTemperature"), m_model); + } + m_rightStack->setCurrentIndex(0); + m_selectorButtons->disableRemoveButton(); + m_selectorButtons->enableAddButton(); } } // namespace openstudio diff --git a/src/openstudio_lib/GroundTemperatureView.hpp b/src/openstudio_lib/GroundTemperatureView.hpp index 09da22cc3..0f573c12f 100644 --- a/src/openstudio_lib/GroundTemperatureView.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -19,6 +19,8 @@ class QStackedWidget; namespace openstudio { +class OSItemSelectorButtons; + enum class GroundTempType { BuildingSurface, @@ -111,11 +113,14 @@ class GroundTemperatureView : public QWidget private slots: void onTypeSelected(openstudio::GroundTempType type); void onObjectCreated(openstudio::GroundTempType type); + void onRemoveClicked(); private: model::Model m_model; bool m_isIP; + GroundTempType m_currentType = GroundTempType::BuildingSurface; GroundTemperatureListView* m_listView = nullptr; + OSItemSelectorButtons* m_selectorButtons = nullptr; GroundTemperatureNotPresentView* m_notPresentView = nullptr; SiteGroundTemperatureBuildingSurfaceWidget* m_bsView = nullptr; SiteGroundTemperatureShallowWidget* m_shView = nullptr; From 940a95e5444ba2bab878289b8660faf2cf65ad00 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 16:33:55 +0100 Subject: [PATCH 23/90] Style the not present page --- src/openstudio_lib/GroundTemperatureView.cpp | 91 ++++++++++++------- src/openstudio_lib/GroundTemperatureView.hpp | 3 +- .../SiteWaterMainsTemperatureWidget.cpp | 2 +- 3 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index 973cedf0d..e3430654e 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -154,6 +154,7 @@ GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent m_label = new QLabel(); m_label->setWordWrap(true); + m_label->setTextFormat(Qt::RichText); layout->addWidget(m_label); m_addButton = new QPushButton(tr("Add")); @@ -161,6 +162,12 @@ GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent m_addButton->setMinimumWidth(100); layout->addWidget(m_addButton, 0, Qt::AlignLeft); + m_epwInfoLabel = new QLabel(); + m_epwInfoLabel->setWordWrap(true); + m_epwInfoLabel->setTextFormat(Qt::RichText); + m_epwInfoLabel->hide(); + layout->addWidget(m_epwInfoLabel); + m_importFromEPWButton = new QPushButton(tr("Import from EPW")); m_importFromEPWButton->setObjectName("StandardGrayButton"); m_importFromEPWButton->setEnabled(false); @@ -171,15 +178,37 @@ GroundTemperatureNotPresentView::GroundTemperatureNotPresentView(QWidget* parent connect(m_addButton, &QPushButton::clicked, this, [this]() { emit addClicked(m_type); }); } -void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString& typeName, model::Model model) { +QString typeNameForGroundTempType(GroundTempType type) { + switch (type) { + case GroundTempType::BuildingSurface: + return "Site:GroundTemperature:BuildingSurface"; + case GroundTempType::Shallow: + return "Site:GroundTemperature:Shallow"; + case GroundTempType::Deep: + return "Site:GroundTemperature:Deep"; + case GroundTempType::WaterMains: + return "Site:WaterMainsTemperature"; + default: + // raise + throw std::runtime_error("Invalid GroundTempType"); + } +} + +void GroundTemperatureNotPresentView::setType(GroundTempType type, model::Model model) { m_type = type; m_model = std::move(model); + QString typeName = typeNameForGroundTempType(type); + disconnect(m_importFromEPWButton, &QPushButton::clicked, nullptr, nullptr); + m_importFromEPWButton->setEnabled(false); + + m_label->setText(tr("

The %1 Unique ModelObject is not present in this model.

" + "

Click Add to instantiate it.

") + .arg(typeName)); - QString label = tr("The %1 Unique ModelObject is not present in this model. Click Add to instantiate it.").arg(typeName); if (type == GroundTempType::BuildingSurface || type == GroundTempType::WaterMains) { - m_label->setText(label); + m_epwInfoLabel->hide(); m_importFromEPWButton->setVisible(false); return; } @@ -188,8 +217,8 @@ void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString boost::optional weatherFile_ = m_model.weatherFile(); if (!weatherFile_) { - label.append(tr(" No weather file is associated with the model, so the object will be added with default values.")); - m_label->setText(label); + m_epwInfoLabel->setText(tr("No weather file is associated with the model, so the object will be added with default values.")); + m_epwInfoLabel->show(); return; } @@ -204,42 +233,38 @@ void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString boost::optional epwFile_ = weatherFile_->file(filesDir); if (!epwFile_) { - - label.append(tr(" While a weather file is associated with the model, could not locate the underlying EpwFile, so the object will be added with " - "default values.")); - m_label->setText(label); + m_epwInfoLabel->setText(tr("While a weather file is associated with the model, could not locate the underlying EpwFile, " + "so the object will be added with default values.")); + m_epwInfoLabel->show(); return; } std::vector ground_temps = epwFile_->groundTemperatureDepths(); if (ground_temps.empty()) { - label.append(tr(" The weather file does not contain any ground temperature data, so the object will be added with default values.")); - m_label->setText(label); + m_epwInfoLabel->setText(tr("The weather file does not contain any ground temperature data, so the object will be added with default values.")); + m_epwInfoLabel->show(); return; } - double target_depth = -999; - if (type == GroundTempType::Shallow) { - target_depth = 0.5; - } else { // GroundTempType::Deep - target_depth = 4.0; - } + const double target_depth = (type == GroundTempType::Shallow) ? 0.5 : 4.0; // Now try to find the target_depth in the epw data, allowing for some tolerance since the epw spec doesn't require exact depths const double tolerance = 0.1; auto it = std::find_if(ground_temps.begin(), ground_temps.end(), [target_depth, tolerance](const EpwGroundTemperatureDepth& gtd) { return std::abs(gtd.groundTemperatureDepth() - target_depth) < tolerance; }); if (it == ground_temps.end()) { - label.append( - tr(" The weather file does not contain ground temperature data at the expected depth of %1 m, so the object will be added with default values.") - .arg(target_depth)); - m_label->setText(label); + m_epwInfoLabel->setText(tr("The weather file does not contain ground temperature data at the expected depth of %1 m, " + "so the object will be added with default values.") + .arg(QString::number(target_depth, 'f', 1))); + m_epwInfoLabel->show(); return; } - label.append(tr(" The weather file contains ground temperature data at a depth of %1 m, so you can choose to import those values or add the object " - "with default values.") - .arg(it->groundTemperatureDepth())); + m_epwInfoLabel->setText(tr("The weather file contains ground temperature data at a depth of " + "%1 m, " + "so you can choose to import those values or add the object with default values.") + .arg(QString::number(it->groundTemperatureDepth(), 'f', 1))); + m_epwInfoLabel->show(); m_importFromEPWButton->setEnabled(true); // Connect button to a lambda that creates the object directly and fills up the value @@ -277,8 +302,6 @@ void GroundTemperatureNotPresentView::setType(GroundTempType type, const QString } emit addClicked(type); }); - - m_label->setText(label); } // ───────────────────────────────────────────────────────── @@ -368,7 +391,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_bsView->attach(*opt); m_rightStack->setCurrentIndex(1); } else { - m_notPresentView->setType(type, tr("Site:GroundTemperature:BuildingSurface"), m_model); + m_notPresentView->setType(type, m_model); m_rightStack->setCurrentIndex(0); } } else if (type == GroundTempType::Shallow) { @@ -378,7 +401,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_shView->attach(*opt); m_rightStack->setCurrentIndex(2); } else { - m_notPresentView->setType(type, tr("Site:GroundTemperature:Shallow"), m_model); + m_notPresentView->setType(type, m_model); m_rightStack->setCurrentIndex(0); } } else if (type == GroundTempType::Deep) { @@ -388,7 +411,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_deepView->attach(*opt); m_rightStack->setCurrentIndex(3); } else { - m_notPresentView->setType(type, tr("Site:GroundTemperature:Deep"), m_model); + m_notPresentView->setType(type, m_model); m_rightStack->setCurrentIndex(0); } } else { @@ -398,7 +421,7 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_waterMainsView->attach(*opt); m_rightStack->setCurrentIndex(4); } else { - m_notPresentView->setType(type, tr("OS:Site:WaterMainsTemperature"), m_model); + m_notPresentView->setType(type, m_model); m_rightStack->setCurrentIndex(0); } } @@ -441,28 +464,28 @@ void GroundTemperatureView::onRemoveClicked() { m_bsView->detach(); opt->remove(); } - m_notPresentView->setType(m_currentType, tr("Site:GroundTemperature:BuildingSurface"), m_model); + m_notPresentView->setType(m_currentType, m_model); } else if (m_currentType == GroundTempType::Shallow) { auto opt = m_model.getOptionalUniqueModelObject(); if (opt) { m_shView->detach(); opt->remove(); } - m_notPresentView->setType(m_currentType, tr("Site:GroundTemperature:Shallow"), m_model); + m_notPresentView->setType(m_currentType, m_model); } else if (m_currentType == GroundTempType::Deep) { auto opt = m_model.getOptionalUniqueModelObject(); if (opt) { m_deepView->detach(); opt->remove(); } - m_notPresentView->setType(m_currentType, tr("Site:GroundTemperature:Deep"), m_model); + m_notPresentView->setType(m_currentType, m_model); } else { auto opt = m_model.siteWaterMainsTemperature(); if (opt) { m_waterMainsView->detach(); opt->remove(); } - m_notPresentView->setType(m_currentType, tr("OS:Site:WaterMainsTemperature"), m_model); + m_notPresentView->setType(m_currentType, m_model); } m_rightStack->setCurrentIndex(0); m_selectorButtons->disableRemoveButton(); diff --git a/src/openstudio_lib/GroundTemperatureView.hpp b/src/openstudio_lib/GroundTemperatureView.hpp index 0f573c12f..7235ad1a3 100644 --- a/src/openstudio_lib/GroundTemperatureView.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -86,13 +86,14 @@ class GroundTemperatureNotPresentView : public QWidget public: explicit GroundTemperatureNotPresentView(QWidget* parent = nullptr); - void setType(GroundTempType type, const QString& typeName, model::Model model); + void setType(GroundTempType type, model::Model model); signals: void addClicked(openstudio::GroundTempType type); private: QLabel* m_label = nullptr; + QLabel* m_epwInfoLabel = nullptr; QPushButton* m_addButton = nullptr; QPushButton* m_importFromEPWButton = nullptr; GroundTempType m_type = GroundTempType::BuildingSurface; diff --git a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp index 9835e5c53..597e908a5 100644 --- a/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp +++ b/src/openstudio_lib/SiteWaterMainsTemperatureWidget.cpp @@ -78,7 +78,7 @@ SiteWaterMainsTemperatureWidget::SiteWaterMainsTemperatureWidget(bool isIP, QWid mainLayout->setSpacing(20); setLayout(mainLayout); - auto* titleLabel = new QLabel("OS:Site:WaterMainsTemperature"); + auto* titleLabel = new QLabel("Site:WaterMainsTemperature"); titleLabel->setObjectName("H2"); mainLayout->addWidget(titleLabel); From fabb08cc18405a76ba7411aa4c919729b0538d37 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 16:45:00 +0100 Subject: [PATCH 24/90] Add a batch edit mode to set all months to the same value --- .../GroundTemperatureMonthlyInspectorView.cpp | 100 ++++++++++++++++++ .../GroundTemperatureMonthlyInspectorView.hpp | 9 ++ 2 files changed, 109 insertions(+) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index c88bf885b..3aa5c7466 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -13,8 +13,12 @@ #include #include +#include +#include #include +#include #include +#include #include #include @@ -59,8 +63,56 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP gridLayout->addWidget(m_edits[i], i + 1, 1, Qt::AlignLeft); } + auto* hRule = new QFrame(); + hRule->setFrameShape(QFrame::HLine); + hRule->setFrameShadow(QFrame::Sunken); + mainLayout->addWidget(hRule); + + auto* setAllLayout = new QHBoxLayout(); + setAllLayout->setContentsMargins(0, 0, 0, 0); + setAllLayout->setSpacing(6); + + auto* setAllLabel = new QLabel(tr("Set all months to:")); + setAllLayout->addWidget(setAllLabel); + + m_constantValueEdit = new QDoubleSpinBox(); + m_constantValueEdit->setDecimals(2); + if (isIP) { + m_constantValueEdit->setRange(-148.0, 212.0); + m_constantValueEdit->setSuffix(tr(" °F")); + } else { + m_constantValueEdit->setRange(-100.0, 100.0); + m_constantValueEdit->setSuffix(tr(" °C")); + } + setAllLayout->addWidget(m_constantValueEdit); + + m_applyConstantButton = new QPushButton(tr("Apply")); + setAllLayout->addWidget(m_applyConstantButton); + setAllLayout->addStretch(); + + mainLayout->addLayout(setAllLayout); + connect(this, &SiteGroundTemperatureMonthlyWidget::toggleUnitsClicked, this, [this](bool isIP) { m_isIP = isIP; }); + connect(this, &SiteGroundTemperatureMonthlyWidget::toggleUnitsClicked, this, [this](bool isIP) { + const double val = m_constantValueEdit->value(); + if (isIP) { + m_constantValueEdit->setRange(-148.0, 212.0); + m_constantValueEdit->setSuffix(tr(" °F")); + m_constantValueEdit->setValue(val * 9.0 / 5.0 + 32.0); + } else { + m_constantValueEdit->setRange(-100.0, 100.0); + m_constantValueEdit->setSuffix(tr(" °C")); + m_constantValueEdit->setValue((val - 32.0) * 5.0 / 9.0); + } + }); + + connect(m_applyConstantButton, &QPushButton::clicked, this, [this]() { + const double val = m_constantValueEdit->value(); + const double celsius = m_isIP ? (val - 32.0) * 5.0 / 9.0 : val; + applyConstantValue(celsius); + }); + mainLayout->addStretch(); } @@ -178,6 +230,22 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject } } +void SiteGroundTemperatureBuildingSurfaceWidget::applyConstantValue(double celsius) { + if (!m_obj) return; + m_obj->setJanuaryGroundTemperature(celsius); + m_obj->setFebruaryGroundTemperature(celsius); + m_obj->setMarchGroundTemperature(celsius); + m_obj->setAprilGroundTemperature(celsius); + m_obj->setMayGroundTemperature(celsius); + m_obj->setJuneGroundTemperature(celsius); + m_obj->setJulyGroundTemperature(celsius); + m_obj->setAugustGroundTemperature(celsius); + m_obj->setSeptemberGroundTemperature(celsius); + m_obj->setOctoberGroundTemperature(celsius); + m_obj->setNovemberGroundTemperature(celsius); + m_obj->setDecemberGroundTemperature(celsius); +} + // ───────────────────────────────────────────────────────── // SiteGroundTemperatureShallowWidget // ───────────────────────────────────────────────────────── @@ -284,6 +352,22 @@ void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { } } +void SiteGroundTemperatureShallowWidget::applyConstantValue(double celsius) { + if (!m_obj) return; + m_obj->setJanuarySurfaceGroundTemperature(celsius); + m_obj->setFebruarySurfaceGroundTemperature(celsius); + m_obj->setMarchSurfaceGroundTemperature(celsius); + m_obj->setAprilSurfaceGroundTemperature(celsius); + m_obj->setMaySurfaceGroundTemperature(celsius); + m_obj->setJuneSurfaceGroundTemperature(celsius); + m_obj->setJulySurfaceGroundTemperature(celsius); + m_obj->setAugustSurfaceGroundTemperature(celsius); + m_obj->setSeptemberSurfaceGroundTemperature(celsius); + m_obj->setOctoberSurfaceGroundTemperature(celsius); + m_obj->setNovemberSurfaceGroundTemperature(celsius); + m_obj->setDecemberSurfaceGroundTemperature(celsius); +} + // ───────────────────────────────────────────────────────── // SiteGroundTemperatureDeepWidget // ───────────────────────────────────────────────────────── @@ -389,4 +473,20 @@ void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { } } +void SiteGroundTemperatureDeepWidget::applyConstantValue(double celsius) { + if (!m_obj) return; + m_obj->setJanuaryDeepGroundTemperature(celsius); + m_obj->setFebruaryDeepGroundTemperature(celsius); + m_obj->setMarchDeepGroundTemperature(celsius); + m_obj->setAprilDeepGroundTemperature(celsius); + m_obj->setMayDeepGroundTemperature(celsius); + m_obj->setJuneDeepGroundTemperature(celsius); + m_obj->setJulyDeepGroundTemperature(celsius); + m_obj->setAugustDeepGroundTemperature(celsius); + m_obj->setSeptemberDeepGroundTemperature(celsius); + m_obj->setOctoberDeepGroundTemperature(celsius); + m_obj->setNovemberDeepGroundTemperature(celsius); + m_obj->setDecemberDeepGroundTemperature(celsius); +} + } // namespace openstudio diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp index e879d8397..cb436a925 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -15,7 +15,9 @@ #include +class QDoubleSpinBox; class QLabel; +class QPushButton; namespace openstudio { @@ -33,12 +35,16 @@ class SiteGroundTemperatureMonthlyWidget : public QWidget virtual void attach(const model::ModelObject& obj) = 0; void detach(); + virtual void applyConstantValue(double celsius) = 0; + signals: void toggleUnitsClicked(bool displayIP); protected: bool m_isIP; QLabel* m_titleLabel = nullptr; + QDoubleSpinBox* m_constantValueEdit = nullptr; + QPushButton* m_applyConstantButton = nullptr; std::array m_edits{}; }; @@ -51,6 +57,7 @@ class SiteGroundTemperatureBuildingSurfaceWidget : public SiteGroundTemperatureM explicit SiteGroundTemperatureBuildingSurfaceWidget(bool isIP, QWidget* parent = nullptr); void attach(const model::ModelObject& obj) override; + void applyConstantValue(double celsius) override; private: boost::optional m_obj; @@ -65,6 +72,7 @@ class SiteGroundTemperatureShallowWidget : public SiteGroundTemperatureMonthlyWi explicit SiteGroundTemperatureShallowWidget(bool isIP, QWidget* parent = nullptr); void attach(const model::ModelObject& obj) override; + void applyConstantValue(double celsius) override; private: boost::optional m_obj; @@ -79,6 +87,7 @@ class SiteGroundTemperatureDeepWidget : public SiteGroundTemperatureMonthlyWidge explicit SiteGroundTemperatureDeepWidget(bool isIP, QWidget* parent = nullptr); void attach(const model::ModelObject& obj) override; + void applyConstantValue(double celsius) override; private: boost::optional m_obj; From 7618c71d0156b68bc910f298640fdb5daf7f560d Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 17:03:52 +0100 Subject: [PATCH 25/90] Move MonthBInding stuff to the header, so I can reuse the setter in applyConstantValue --- .../GroundTemperatureMonthlyInspectorView.cpp | 317 ++---------------- .../GroundTemperatureMonthlyInspectorView.hpp | 252 +++++++++++++- 2 files changed, 268 insertions(+), 301 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 3aa5c7466..1adb40b5c 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -62,6 +62,7 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP m_edits[i]->setFixedWidth(TEMP_EDIT_WIDTH); gridLayout->addWidget(m_edits[i], i + 1, 1, Qt::AlignLeft); } + gridLayout->setColumnStretch(2, 1); auto* hRule = new QFrame(); hRule->setFrameShape(QFrame::HLine); @@ -133,96 +134,11 @@ SiteGroundTemperatureBuildingSurfaceWidget::SiteGroundTemperatureBuildingSurface void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject& obj) { detach(); - m_obj = obj.cast(); + m_obj = obj.cast(); m_titleLabel->setText("Site:GroundTemperature:BuildingSurface"); - using BS = model::SiteGroundTemperatureBuildingSurface; - - struct MonthBinding - { - double (BS::*getter)() const; // cppcheck-suppress unusedStructMember - bool (BS::*setter)(double); // cppcheck-suppress unusedStructMember - void (BS::*resetter)(); // cppcheck-suppress unusedStructMember - bool (BS::*defaulted)() const; // cppcheck-suppress unusedStructMember - }; - - static const std::array month_binders{{ - { - &BS::januaryGroundTemperature, - &BS::setJanuaryGroundTemperature, - &BS::resetJanuaryGroundTemperature, - &BS::isJanuaryGroundTemperatureDefaulted, - }, - { - &BS::februaryGroundTemperature, - &BS::setFebruaryGroundTemperature, - &BS::resetFebruaryGroundTemperature, - &BS::isFebruaryGroundTemperatureDefaulted, - }, - { - &BS::marchGroundTemperature, - &BS::setMarchGroundTemperature, - &BS::resetMarchGroundTemperature, - &BS::isMarchGroundTemperatureDefaulted, - }, - { - &BS::aprilGroundTemperature, - &BS::setAprilGroundTemperature, - &BS::resetAprilGroundTemperature, - &BS::isAprilGroundTemperatureDefaulted, - }, - { - &BS::mayGroundTemperature, - &BS::setMayGroundTemperature, - &BS::resetMayGroundTemperature, - &BS::isMayGroundTemperatureDefaulted, - }, - { - &BS::juneGroundTemperature, - &BS::setJuneGroundTemperature, - &BS::resetJuneGroundTemperature, - &BS::isJuneGroundTemperatureDefaulted, - }, - { - &BS::julyGroundTemperature, - &BS::setJulyGroundTemperature, - &BS::resetJulyGroundTemperature, - &BS::isJulyGroundTemperatureDefaulted, - }, - { - &BS::augustGroundTemperature, - &BS::setAugustGroundTemperature, - &BS::resetAugustGroundTemperature, - &BS::isAugustGroundTemperatureDefaulted, - }, - { - &BS::septemberGroundTemperature, - &BS::setSeptemberGroundTemperature, - &BS::resetSeptemberGroundTemperature, - &BS::isSeptemberGroundTemperatureDefaulted, - }, - { - &BS::octoberGroundTemperature, - &BS::setOctoberGroundTemperature, - &BS::resetOctoberGroundTemperature, - &BS::isOctoberGroundTemperatureDefaulted, - }, - { - &BS::novemberGroundTemperature, - &BS::setNovemberGroundTemperature, - &BS::resetNovemberGroundTemperature, - &BS::isNovemberGroundTemperatureDefaulted, - }, - { - &BS::decemberGroundTemperature, - &BS::setDecemberGroundTemperature, - &BS::resetDecemberGroundTemperature, - &BS::isDecemberGroundTemperatureDefaulted, - }, - }}; - for (int i = 0; i < 12; ++i) { - const auto& mb = month_binders[i]; + const auto& mb = s_monthBinders[i]; m_edits[i]->bind(m_isIP, *m_obj, DoubleGetter([this, g = mb.getter]() { return (m_obj.get_ptr()->*g)(); }), boost::optional([this, s = mb.setter](double v) { return (m_obj.get_ptr()->*s)(v); }), boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, @@ -232,18 +148,9 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject void SiteGroundTemperatureBuildingSurfaceWidget::applyConstantValue(double celsius) { if (!m_obj) return; - m_obj->setJanuaryGroundTemperature(celsius); - m_obj->setFebruaryGroundTemperature(celsius); - m_obj->setMarchGroundTemperature(celsius); - m_obj->setAprilGroundTemperature(celsius); - m_obj->setMayGroundTemperature(celsius); - m_obj->setJuneGroundTemperature(celsius); - m_obj->setJulyGroundTemperature(celsius); - m_obj->setAugustGroundTemperature(celsius); - m_obj->setSeptemberGroundTemperature(celsius); - m_obj->setOctoberGroundTemperature(celsius); - m_obj->setNovemberGroundTemperature(celsius); - m_obj->setDecemberGroundTemperature(celsius); + for (const auto& mb : s_monthBinders) { + (m_obj.get_ptr()->*mb.setter)(celsius); + } } // ───────────────────────────────────────────────────────── @@ -255,96 +162,11 @@ SiteGroundTemperatureShallowWidget::SiteGroundTemperatureShallowWidget(bool isIP void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { detach(); - m_obj = obj.cast(); + m_obj = obj.cast(); m_titleLabel->setText("Site:GroundTemperature:Shallow"); - using SH = model::SiteGroundTemperatureShallow; - - struct MonthBinding - { - double (SH::*getter)() const; // cppcheck-suppress unusedStructMember - bool (SH::*setter)(double); // cppcheck-suppress unusedStructMember - void (SH::*resetter)(); // cppcheck-suppress unusedStructMember - bool (SH::*defaulted)() const; // cppcheck-suppress unusedStructMember - }; - - static const std::array month_binders{{ - { - &SH::januarySurfaceGroundTemperature, - &SH::setJanuarySurfaceGroundTemperature, - &SH::resetJanuarySurfaceGroundTemperature, - &SH::isJanuarySurfaceGroundTemperatureDefaulted, - }, - { - &SH::februarySurfaceGroundTemperature, - &SH::setFebruarySurfaceGroundTemperature, - &SH::resetFebruarySurfaceGroundTemperature, - &SH::isFebruarySurfaceGroundTemperatureDefaulted, - }, - { - &SH::marchSurfaceGroundTemperature, - &SH::setMarchSurfaceGroundTemperature, - &SH::resetMarchSurfaceGroundTemperature, - &SH::isMarchSurfaceGroundTemperatureDefaulted, - }, - { - &SH::aprilSurfaceGroundTemperature, - &SH::setAprilSurfaceGroundTemperature, - &SH::resetAprilSurfaceGroundTemperature, - &SH::isAprilSurfaceGroundTemperatureDefaulted, - }, - { - &SH::maySurfaceGroundTemperature, - &SH::setMaySurfaceGroundTemperature, - &SH::resetMaySurfaceGroundTemperature, - &SH::isMaySurfaceGroundTemperatureDefaulted, - }, - { - &SH::juneSurfaceGroundTemperature, - &SH::setJuneSurfaceGroundTemperature, - &SH::resetJuneSurfaceGroundTemperature, - &SH::isJuneSurfaceGroundTemperatureDefaulted, - }, - { - &SH::julySurfaceGroundTemperature, - &SH::setJulySurfaceGroundTemperature, - &SH::resetJulySurfaceGroundTemperature, - &SH::isJulySurfaceGroundTemperatureDefaulted, - }, - { - &SH::augustSurfaceGroundTemperature, - &SH::setAugustSurfaceGroundTemperature, - &SH::resetAugustSurfaceGroundTemperature, - &SH::isAugustSurfaceGroundTemperatureDefaulted, - }, - { - &SH::septemberSurfaceGroundTemperature, - &SH::setSeptemberSurfaceGroundTemperature, - &SH::resetSeptemberSurfaceGroundTemperature, - &SH::isSeptemberSurfaceGroundTemperatureDefaulted, - }, - { - &SH::octoberSurfaceGroundTemperature, - &SH::setOctoberSurfaceGroundTemperature, - &SH::resetOctoberSurfaceGroundTemperature, - &SH::isOctoberSurfaceGroundTemperatureDefaulted, - }, - { - &SH::novemberSurfaceGroundTemperature, - &SH::setNovemberSurfaceGroundTemperature, - &SH::resetNovemberSurfaceGroundTemperature, - &SH::isNovemberSurfaceGroundTemperatureDefaulted, - }, - { - &SH::decemberSurfaceGroundTemperature, - &SH::setDecemberSurfaceGroundTemperature, - &SH::resetDecemberSurfaceGroundTemperature, - &SH::isDecemberSurfaceGroundTemperatureDefaulted, - }, - }}; - for (int i = 0; i < 12; ++i) { - const auto& mb = month_binders[i]; + const auto& mb = s_monthBinders[i]; m_edits[i]->bind(m_isIP, *m_obj, DoubleGetter([this, g = mb.getter]() { return (m_obj.get_ptr()->*g)(); }), boost::optional([this, s = mb.setter](double v) { return (m_obj.get_ptr()->*s)(v); }), boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, @@ -353,19 +175,12 @@ void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { } void SiteGroundTemperatureShallowWidget::applyConstantValue(double celsius) { - if (!m_obj) return; - m_obj->setJanuarySurfaceGroundTemperature(celsius); - m_obj->setFebruarySurfaceGroundTemperature(celsius); - m_obj->setMarchSurfaceGroundTemperature(celsius); - m_obj->setAprilSurfaceGroundTemperature(celsius); - m_obj->setMaySurfaceGroundTemperature(celsius); - m_obj->setJuneSurfaceGroundTemperature(celsius); - m_obj->setJulySurfaceGroundTemperature(celsius); - m_obj->setAugustSurfaceGroundTemperature(celsius); - m_obj->setSeptemberSurfaceGroundTemperature(celsius); - m_obj->setOctoberSurfaceGroundTemperature(celsius); - m_obj->setNovemberSurfaceGroundTemperature(celsius); - m_obj->setDecemberSurfaceGroundTemperature(celsius); + if (!m_obj) { + return; + } + for (const auto& mb : s_monthBinders) { + (m_obj.get_ptr()->*mb.setter)(celsius); + } } // ───────────────────────────────────────────────────────── @@ -376,96 +191,11 @@ SiteGroundTemperatureDeepWidget::SiteGroundTemperatureDeepWidget(bool isIP, QWid void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { detach(); - m_obj = obj.cast(); + m_obj = obj.cast(); m_titleLabel->setText("Site:GroundTemperature:Deep"); - using DP = model::SiteGroundTemperatureDeep; - - struct MonthBinding - { - double (DP::*getter)() const; // cppcheck-suppress unusedStructMember - bool (DP::*setter)(double); // cppcheck-suppress unusedStructMember - void (DP::*resetter)(); // cppcheck-suppress unusedStructMember - bool (DP::*defaulted)() const; // cppcheck-suppress unusedStructMember - }; - - static const std::array month_binders{{ - { - &DP::januaryDeepGroundTemperature, - &DP::setJanuaryDeepGroundTemperature, - &DP::resetJanuaryDeepGroundTemperature, - &DP::isJanuaryDeepGroundTemperatureDefaulted, - }, - { - &DP::februaryDeepGroundTemperature, - &DP::setFebruaryDeepGroundTemperature, - &DP::resetFebruaryDeepGroundTemperature, - &DP::isFebruaryDeepGroundTemperatureDefaulted, - }, - { - &DP::marchDeepGroundTemperature, - &DP::setMarchDeepGroundTemperature, - &DP::resetMarchDeepGroundTemperature, - &DP::isMarchDeepGroundTemperatureDefaulted, - }, - { - &DP::aprilDeepGroundTemperature, - &DP::setAprilDeepGroundTemperature, - &DP::resetAprilDeepGroundTemperature, - &DP::isAprilDeepGroundTemperatureDefaulted, - }, - { - &DP::mayDeepGroundTemperature, - &DP::setMayDeepGroundTemperature, - &DP::resetMayDeepGroundTemperature, - &DP::isMayDeepGroundTemperatureDefaulted, - }, - { - &DP::juneDeepGroundTemperature, - &DP::setJuneDeepGroundTemperature, - &DP::resetJuneDeepGroundTemperature, - &DP::isJuneDeepGroundTemperatureDefaulted, - }, - { - &DP::julyDeepGroundTemperature, - &DP::setJulyDeepGroundTemperature, - &DP::resetJulyDeepGroundTemperature, - &DP::isJulyDeepGroundTemperatureDefaulted, - }, - { - &DP::augustDeepGroundTemperature, - &DP::setAugustDeepGroundTemperature, - &DP::resetAugustDeepGroundTemperature, - &DP::isAugustDeepGroundTemperatureDefaulted, - }, - { - &DP::septemberDeepGroundTemperature, - &DP::setSeptemberDeepGroundTemperature, - &DP::resetSeptemberDeepGroundTemperature, - &DP::isSeptemberDeepGroundTemperatureDefaulted, - }, - { - &DP::octoberDeepGroundTemperature, - &DP::setOctoberDeepGroundTemperature, - &DP::resetOctoberDeepGroundTemperature, - &DP::isOctoberDeepGroundTemperatureDefaulted, - }, - { - &DP::novemberDeepGroundTemperature, - &DP::setNovemberDeepGroundTemperature, - &DP::resetNovemberDeepGroundTemperature, - &DP::isNovemberDeepGroundTemperatureDefaulted, - }, - { - &DP::decemberDeepGroundTemperature, - &DP::setDecemberDeepGroundTemperature, - &DP::resetDecemberDeepGroundTemperature, - &DP::isDecemberDeepGroundTemperatureDefaulted, - }, - }}; - for (int i = 0; i < 12; ++i) { - const auto& mb = month_binders[i]; + const auto& mb = s_monthBinders[i]; m_edits[i]->bind(m_isIP, *m_obj, DoubleGetter([this, g = mb.getter]() { return (m_obj.get_ptr()->*g)(); }), boost::optional([this, s = mb.setter](double v) { return (m_obj.get_ptr()->*s)(v); }), boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, @@ -475,18 +205,9 @@ void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { void SiteGroundTemperatureDeepWidget::applyConstantValue(double celsius) { if (!m_obj) return; - m_obj->setJanuaryDeepGroundTemperature(celsius); - m_obj->setFebruaryDeepGroundTemperature(celsius); - m_obj->setMarchDeepGroundTemperature(celsius); - m_obj->setAprilDeepGroundTemperature(celsius); - m_obj->setMayDeepGroundTemperature(celsius); - m_obj->setJuneDeepGroundTemperature(celsius); - m_obj->setJulyDeepGroundTemperature(celsius); - m_obj->setAugustDeepGroundTemperature(celsius); - m_obj->setSeptemberDeepGroundTemperature(celsius); - m_obj->setOctoberDeepGroundTemperature(celsius); - m_obj->setNovemberDeepGroundTemperature(celsius); - m_obj->setDecemberDeepGroundTemperature(celsius); + for (const auto& mb : s_monthBinders) { + (m_obj.get_ptr()->*mb.setter)(celsius); + } } } // namespace openstudio diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp index cb436a925..33a08b8de 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -60,7 +60,89 @@ class SiteGroundTemperatureBuildingSurfaceWidget : public SiteGroundTemperatureM void applyConstantValue(double celsius) override; private: - boost::optional m_obj; + using BS = model::SiteGroundTemperatureBuildingSurface; + struct MonthBinding + { + double (BS::*getter)() const; // cppcheck-suppress unusedStructMember + bool (BS::*setter)(double); // cppcheck-suppress unusedStructMember + void (BS::*resetter)(); // cppcheck-suppress unusedStructMember + bool (BS::*defaulted)() const; // cppcheck-suppress unusedStructMember + }; + inline static const std::array s_monthBinders{{ + { + &BS::januaryGroundTemperature, + &BS::setJanuaryGroundTemperature, + &BS::resetJanuaryGroundTemperature, + &BS::isJanuaryGroundTemperatureDefaulted, + }, + { + &BS::februaryGroundTemperature, + &BS::setFebruaryGroundTemperature, + &BS::resetFebruaryGroundTemperature, + &BS::isFebruaryGroundTemperatureDefaulted, + }, + { + &BS::marchGroundTemperature, + &BS::setMarchGroundTemperature, + &BS::resetMarchGroundTemperature, + &BS::isMarchGroundTemperatureDefaulted, + }, + { + &BS::aprilGroundTemperature, + &BS::setAprilGroundTemperature, + &BS::resetAprilGroundTemperature, + &BS::isAprilGroundTemperatureDefaulted, + }, + { + &BS::mayGroundTemperature, + &BS::setMayGroundTemperature, + &BS::resetMayGroundTemperature, + &BS::isMayGroundTemperatureDefaulted, + }, + { + &BS::juneGroundTemperature, + &BS::setJuneGroundTemperature, + &BS::resetJuneGroundTemperature, + &BS::isJuneGroundTemperatureDefaulted, + }, + { + &BS::julyGroundTemperature, + &BS::setJulyGroundTemperature, + &BS::resetJulyGroundTemperature, + &BS::isJulyGroundTemperatureDefaulted, + }, + { + &BS::augustGroundTemperature, + &BS::setAugustGroundTemperature, + &BS::resetAugustGroundTemperature, + &BS::isAugustGroundTemperatureDefaulted, + }, + { + &BS::septemberGroundTemperature, + &BS::setSeptemberGroundTemperature, + &BS::resetSeptemberGroundTemperature, + &BS::isSeptemberGroundTemperatureDefaulted, + }, + { + &BS::octoberGroundTemperature, + &BS::setOctoberGroundTemperature, + &BS::resetOctoberGroundTemperature, + &BS::isOctoberGroundTemperatureDefaulted, + }, + { + &BS::novemberGroundTemperature, + &BS::setNovemberGroundTemperature, + &BS::resetNovemberGroundTemperature, + &BS::isNovemberGroundTemperatureDefaulted, + }, + { + &BS::decemberGroundTemperature, + &BS::setDecemberGroundTemperature, + &BS::resetDecemberGroundTemperature, + &BS::isDecemberGroundTemperatureDefaulted, + }, + }}; + boost::optional m_obj; }; /** Inspector for Site:GroundTemperature:Shallow — 12 monthly fields. */ @@ -75,7 +157,89 @@ class SiteGroundTemperatureShallowWidget : public SiteGroundTemperatureMonthlyWi void applyConstantValue(double celsius) override; private: - boost::optional m_obj; + using SH = model::SiteGroundTemperatureShallow; + struct MonthBinding + { + double (SH::*getter)() const; // cppcheck-suppress unusedStructMember + bool (SH::*setter)(double); // cppcheck-suppress unusedStructMember + void (SH::*resetter)(); // cppcheck-suppress unusedStructMember + bool (SH::*defaulted)() const; // cppcheck-suppress unusedStructMember + }; + inline static const std::array s_monthBinders{{ + { + &SH::januarySurfaceGroundTemperature, + &SH::setJanuarySurfaceGroundTemperature, + &SH::resetJanuarySurfaceGroundTemperature, + &SH::isJanuarySurfaceGroundTemperatureDefaulted, + }, + { + &SH::februarySurfaceGroundTemperature, + &SH::setFebruarySurfaceGroundTemperature, + &SH::resetFebruarySurfaceGroundTemperature, + &SH::isFebruarySurfaceGroundTemperatureDefaulted, + }, + { + &SH::marchSurfaceGroundTemperature, + &SH::setMarchSurfaceGroundTemperature, + &SH::resetMarchSurfaceGroundTemperature, + &SH::isMarchSurfaceGroundTemperatureDefaulted, + }, + { + &SH::aprilSurfaceGroundTemperature, + &SH::setAprilSurfaceGroundTemperature, + &SH::resetAprilSurfaceGroundTemperature, + &SH::isAprilSurfaceGroundTemperatureDefaulted, + }, + { + &SH::maySurfaceGroundTemperature, + &SH::setMaySurfaceGroundTemperature, + &SH::resetMaySurfaceGroundTemperature, + &SH::isMaySurfaceGroundTemperatureDefaulted, + }, + { + &SH::juneSurfaceGroundTemperature, + &SH::setJuneSurfaceGroundTemperature, + &SH::resetJuneSurfaceGroundTemperature, + &SH::isJuneSurfaceGroundTemperatureDefaulted, + }, + { + &SH::julySurfaceGroundTemperature, + &SH::setJulySurfaceGroundTemperature, + &SH::resetJulySurfaceGroundTemperature, + &SH::isJulySurfaceGroundTemperatureDefaulted, + }, + { + &SH::augustSurfaceGroundTemperature, + &SH::setAugustSurfaceGroundTemperature, + &SH::resetAugustSurfaceGroundTemperature, + &SH::isAugustSurfaceGroundTemperatureDefaulted, + }, + { + &SH::septemberSurfaceGroundTemperature, + &SH::setSeptemberSurfaceGroundTemperature, + &SH::resetSeptemberSurfaceGroundTemperature, + &SH::isSeptemberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::octoberSurfaceGroundTemperature, + &SH::setOctoberSurfaceGroundTemperature, + &SH::resetOctoberSurfaceGroundTemperature, + &SH::isOctoberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::novemberSurfaceGroundTemperature, + &SH::setNovemberSurfaceGroundTemperature, + &SH::resetNovemberSurfaceGroundTemperature, + &SH::isNovemberSurfaceGroundTemperatureDefaulted, + }, + { + &SH::decemberSurfaceGroundTemperature, + &SH::setDecemberSurfaceGroundTemperature, + &SH::resetDecemberSurfaceGroundTemperature, + &SH::isDecemberSurfaceGroundTemperatureDefaulted, + }, + }}; + boost::optional m_obj; }; /** Inspector for Site:GroundTemperature:Deep — 12 monthly fields. */ @@ -90,7 +254,89 @@ class SiteGroundTemperatureDeepWidget : public SiteGroundTemperatureMonthlyWidge void applyConstantValue(double celsius) override; private: - boost::optional m_obj; + using DP = model::SiteGroundTemperatureDeep; + struct MonthBinding + { + double (DP::*getter)() const; // cppcheck-suppress unusedStructMember + bool (DP::*setter)(double); // cppcheck-suppress unusedStructMember + void (DP::*resetter)(); // cppcheck-suppress unusedStructMember + bool (DP::*defaulted)() const; // cppcheck-suppress unusedStructMember + }; + inline static const std::array s_monthBinders{{ + { + &DP::januaryDeepGroundTemperature, + &DP::setJanuaryDeepGroundTemperature, + &DP::resetJanuaryDeepGroundTemperature, + &DP::isJanuaryDeepGroundTemperatureDefaulted, + }, + { + &DP::februaryDeepGroundTemperature, + &DP::setFebruaryDeepGroundTemperature, + &DP::resetFebruaryDeepGroundTemperature, + &DP::isFebruaryDeepGroundTemperatureDefaulted, + }, + { + &DP::marchDeepGroundTemperature, + &DP::setMarchDeepGroundTemperature, + &DP::resetMarchDeepGroundTemperature, + &DP::isMarchDeepGroundTemperatureDefaulted, + }, + { + &DP::aprilDeepGroundTemperature, + &DP::setAprilDeepGroundTemperature, + &DP::resetAprilDeepGroundTemperature, + &DP::isAprilDeepGroundTemperatureDefaulted, + }, + { + &DP::mayDeepGroundTemperature, + &DP::setMayDeepGroundTemperature, + &DP::resetMayDeepGroundTemperature, + &DP::isMayDeepGroundTemperatureDefaulted, + }, + { + &DP::juneDeepGroundTemperature, + &DP::setJuneDeepGroundTemperature, + &DP::resetJuneDeepGroundTemperature, + &DP::isJuneDeepGroundTemperatureDefaulted, + }, + { + &DP::julyDeepGroundTemperature, + &DP::setJulyDeepGroundTemperature, + &DP::resetJulyDeepGroundTemperature, + &DP::isJulyDeepGroundTemperatureDefaulted, + }, + { + &DP::augustDeepGroundTemperature, + &DP::setAugustDeepGroundTemperature, + &DP::resetAugustDeepGroundTemperature, + &DP::isAugustDeepGroundTemperatureDefaulted, + }, + { + &DP::septemberDeepGroundTemperature, + &DP::setSeptemberDeepGroundTemperature, + &DP::resetSeptemberDeepGroundTemperature, + &DP::isSeptemberDeepGroundTemperatureDefaulted, + }, + { + &DP::octoberDeepGroundTemperature, + &DP::setOctoberDeepGroundTemperature, + &DP::resetOctoberDeepGroundTemperature, + &DP::isOctoberDeepGroundTemperatureDefaulted, + }, + { + &DP::novemberDeepGroundTemperature, + &DP::setNovemberDeepGroundTemperature, + &DP::resetNovemberDeepGroundTemperature, + &DP::isNovemberDeepGroundTemperatureDefaulted, + }, + { + &DP::decemberDeepGroundTemperature, + &DP::setDecemberDeepGroundTemperature, + &DP::resetDecemberDeepGroundTemperature, + &DP::isDecemberDeepGroundTemperatureDefaulted, + }, + }}; + boost::optional m_obj; }; } // namespace openstudio From e65a5b479524a8b13de59639808d4e18dfd4c491 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 17:07:36 +0100 Subject: [PATCH 26/90] Rename the "using" field to MOType (ModelObject Type) --- .../GroundTemperatureMonthlyInspectorView.cpp | 14 +- .../GroundTemperatureMonthlyInspectorView.hpp | 324 +++++++++--------- 2 files changed, 171 insertions(+), 167 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 1adb40b5c..4aa57a7fc 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -134,7 +134,7 @@ SiteGroundTemperatureBuildingSurfaceWidget::SiteGroundTemperatureBuildingSurface void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject& obj) { detach(); - m_obj = obj.cast(); + m_obj = obj.cast(); m_titleLabel->setText("Site:GroundTemperature:BuildingSurface"); for (int i = 0; i < 12; ++i) { @@ -147,7 +147,9 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject } void SiteGroundTemperatureBuildingSurfaceWidget::applyConstantValue(double celsius) { - if (!m_obj) return; + if (!m_obj) { + return; + } for (const auto& mb : s_monthBinders) { (m_obj.get_ptr()->*mb.setter)(celsius); } @@ -162,7 +164,7 @@ SiteGroundTemperatureShallowWidget::SiteGroundTemperatureShallowWidget(bool isIP void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { detach(); - m_obj = obj.cast(); + m_obj = obj.cast(); m_titleLabel->setText("Site:GroundTemperature:Shallow"); for (int i = 0; i < 12; ++i) { @@ -191,7 +193,7 @@ SiteGroundTemperatureDeepWidget::SiteGroundTemperatureDeepWidget(bool isIP, QWid void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { detach(); - m_obj = obj.cast(); + m_obj = obj.cast(); m_titleLabel->setText("Site:GroundTemperature:Deep"); for (int i = 0; i < 12; ++i) { @@ -204,7 +206,9 @@ void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { } void SiteGroundTemperatureDeepWidget::applyConstantValue(double celsius) { - if (!m_obj) return; + if (!m_obj) { + return; + } for (const auto& mb : s_monthBinders) { (m_obj.get_ptr()->*mb.setter)(celsius); } diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp index 33a08b8de..73247e3f8 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -60,89 +60,89 @@ class SiteGroundTemperatureBuildingSurfaceWidget : public SiteGroundTemperatureM void applyConstantValue(double celsius) override; private: - using BS = model::SiteGroundTemperatureBuildingSurface; + using MOType = model::SiteGroundTemperatureBuildingSurface; struct MonthBinding { - double (BS::*getter)() const; // cppcheck-suppress unusedStructMember - bool (BS::*setter)(double); // cppcheck-suppress unusedStructMember - void (BS::*resetter)(); // cppcheck-suppress unusedStructMember - bool (BS::*defaulted)() const; // cppcheck-suppress unusedStructMember + double (MOType::*getter)() const; // cppcheck-suppress unusedStructMember + bool (MOType::*setter)(double); // cppcheck-suppress unusedStructMember + void (MOType::*resetter)(); // cppcheck-suppress unusedStructMember + bool (MOType::*defaulted)() const; // cppcheck-suppress unusedStructMember }; inline static const std::array s_monthBinders{{ { - &BS::januaryGroundTemperature, - &BS::setJanuaryGroundTemperature, - &BS::resetJanuaryGroundTemperature, - &BS::isJanuaryGroundTemperatureDefaulted, + &MOType::januaryGroundTemperature, + &MOType::setJanuaryGroundTemperature, + &MOType::resetJanuaryGroundTemperature, + &MOType::isJanuaryGroundTemperatureDefaulted, }, { - &BS::februaryGroundTemperature, - &BS::setFebruaryGroundTemperature, - &BS::resetFebruaryGroundTemperature, - &BS::isFebruaryGroundTemperatureDefaulted, + &MOType::februaryGroundTemperature, + &MOType::setFebruaryGroundTemperature, + &MOType::resetFebruaryGroundTemperature, + &MOType::isFebruaryGroundTemperatureDefaulted, }, { - &BS::marchGroundTemperature, - &BS::setMarchGroundTemperature, - &BS::resetMarchGroundTemperature, - &BS::isMarchGroundTemperatureDefaulted, + &MOType::marchGroundTemperature, + &MOType::setMarchGroundTemperature, + &MOType::resetMarchGroundTemperature, + &MOType::isMarchGroundTemperatureDefaulted, }, { - &BS::aprilGroundTemperature, - &BS::setAprilGroundTemperature, - &BS::resetAprilGroundTemperature, - &BS::isAprilGroundTemperatureDefaulted, + &MOType::aprilGroundTemperature, + &MOType::setAprilGroundTemperature, + &MOType::resetAprilGroundTemperature, + &MOType::isAprilGroundTemperatureDefaulted, }, { - &BS::mayGroundTemperature, - &BS::setMayGroundTemperature, - &BS::resetMayGroundTemperature, - &BS::isMayGroundTemperatureDefaulted, + &MOType::mayGroundTemperature, + &MOType::setMayGroundTemperature, + &MOType::resetMayGroundTemperature, + &MOType::isMayGroundTemperatureDefaulted, }, { - &BS::juneGroundTemperature, - &BS::setJuneGroundTemperature, - &BS::resetJuneGroundTemperature, - &BS::isJuneGroundTemperatureDefaulted, + &MOType::juneGroundTemperature, + &MOType::setJuneGroundTemperature, + &MOType::resetJuneGroundTemperature, + &MOType::isJuneGroundTemperatureDefaulted, }, { - &BS::julyGroundTemperature, - &BS::setJulyGroundTemperature, - &BS::resetJulyGroundTemperature, - &BS::isJulyGroundTemperatureDefaulted, + &MOType::julyGroundTemperature, + &MOType::setJulyGroundTemperature, + &MOType::resetJulyGroundTemperature, + &MOType::isJulyGroundTemperatureDefaulted, }, { - &BS::augustGroundTemperature, - &BS::setAugustGroundTemperature, - &BS::resetAugustGroundTemperature, - &BS::isAugustGroundTemperatureDefaulted, + &MOType::augustGroundTemperature, + &MOType::setAugustGroundTemperature, + &MOType::resetAugustGroundTemperature, + &MOType::isAugustGroundTemperatureDefaulted, }, { - &BS::septemberGroundTemperature, - &BS::setSeptemberGroundTemperature, - &BS::resetSeptemberGroundTemperature, - &BS::isSeptemberGroundTemperatureDefaulted, + &MOType::septemberGroundTemperature, + &MOType::setSeptemberGroundTemperature, + &MOType::resetSeptemberGroundTemperature, + &MOType::isSeptemberGroundTemperatureDefaulted, }, { - &BS::octoberGroundTemperature, - &BS::setOctoberGroundTemperature, - &BS::resetOctoberGroundTemperature, - &BS::isOctoberGroundTemperatureDefaulted, + &MOType::octoberGroundTemperature, + &MOType::setOctoberGroundTemperature, + &MOType::resetOctoberGroundTemperature, + &MOType::isOctoberGroundTemperatureDefaulted, }, { - &BS::novemberGroundTemperature, - &BS::setNovemberGroundTemperature, - &BS::resetNovemberGroundTemperature, - &BS::isNovemberGroundTemperatureDefaulted, + &MOType::novemberGroundTemperature, + &MOType::setNovemberGroundTemperature, + &MOType::resetNovemberGroundTemperature, + &MOType::isNovemberGroundTemperatureDefaulted, }, { - &BS::decemberGroundTemperature, - &BS::setDecemberGroundTemperature, - &BS::resetDecemberGroundTemperature, - &BS::isDecemberGroundTemperatureDefaulted, + &MOType::decemberGroundTemperature, + &MOType::setDecemberGroundTemperature, + &MOType::resetDecemberGroundTemperature, + &MOType::isDecemberGroundTemperatureDefaulted, }, }}; - boost::optional m_obj; + boost::optional m_obj; }; /** Inspector for Site:GroundTemperature:Shallow — 12 monthly fields. */ @@ -157,89 +157,89 @@ class SiteGroundTemperatureShallowWidget : public SiteGroundTemperatureMonthlyWi void applyConstantValue(double celsius) override; private: - using SH = model::SiteGroundTemperatureShallow; + using MOType = model::SiteGroundTemperatureShallow; struct MonthBinding { - double (SH::*getter)() const; // cppcheck-suppress unusedStructMember - bool (SH::*setter)(double); // cppcheck-suppress unusedStructMember - void (SH::*resetter)(); // cppcheck-suppress unusedStructMember - bool (SH::*defaulted)() const; // cppcheck-suppress unusedStructMember + double (MOType::*getter)() const; // cppcheck-suppress unusedStructMember + bool (MOType::*setter)(double); // cppcheck-suppress unusedStructMember + void (MOType::*resetter)(); // cppcheck-suppress unusedStructMember + bool (MOType::*defaulted)() const; // cppcheck-suppress unusedStructMember }; inline static const std::array s_monthBinders{{ { - &SH::januarySurfaceGroundTemperature, - &SH::setJanuarySurfaceGroundTemperature, - &SH::resetJanuarySurfaceGroundTemperature, - &SH::isJanuarySurfaceGroundTemperatureDefaulted, + &MOType::januarySurfaceGroundTemperature, + &MOType::setJanuarySurfaceGroundTemperature, + &MOType::resetJanuarySurfaceGroundTemperature, + &MOType::isJanuarySurfaceGroundTemperatureDefaulted, }, { - &SH::februarySurfaceGroundTemperature, - &SH::setFebruarySurfaceGroundTemperature, - &SH::resetFebruarySurfaceGroundTemperature, - &SH::isFebruarySurfaceGroundTemperatureDefaulted, + &MOType::februarySurfaceGroundTemperature, + &MOType::setFebruarySurfaceGroundTemperature, + &MOType::resetFebruarySurfaceGroundTemperature, + &MOType::isFebruarySurfaceGroundTemperatureDefaulted, }, { - &SH::marchSurfaceGroundTemperature, - &SH::setMarchSurfaceGroundTemperature, - &SH::resetMarchSurfaceGroundTemperature, - &SH::isMarchSurfaceGroundTemperatureDefaulted, + &MOType::marchSurfaceGroundTemperature, + &MOType::setMarchSurfaceGroundTemperature, + &MOType::resetMarchSurfaceGroundTemperature, + &MOType::isMarchSurfaceGroundTemperatureDefaulted, }, { - &SH::aprilSurfaceGroundTemperature, - &SH::setAprilSurfaceGroundTemperature, - &SH::resetAprilSurfaceGroundTemperature, - &SH::isAprilSurfaceGroundTemperatureDefaulted, + &MOType::aprilSurfaceGroundTemperature, + &MOType::setAprilSurfaceGroundTemperature, + &MOType::resetAprilSurfaceGroundTemperature, + &MOType::isAprilSurfaceGroundTemperatureDefaulted, }, { - &SH::maySurfaceGroundTemperature, - &SH::setMaySurfaceGroundTemperature, - &SH::resetMaySurfaceGroundTemperature, - &SH::isMaySurfaceGroundTemperatureDefaulted, + &MOType::maySurfaceGroundTemperature, + &MOType::setMaySurfaceGroundTemperature, + &MOType::resetMaySurfaceGroundTemperature, + &MOType::isMaySurfaceGroundTemperatureDefaulted, }, { - &SH::juneSurfaceGroundTemperature, - &SH::setJuneSurfaceGroundTemperature, - &SH::resetJuneSurfaceGroundTemperature, - &SH::isJuneSurfaceGroundTemperatureDefaulted, + &MOType::juneSurfaceGroundTemperature, + &MOType::setJuneSurfaceGroundTemperature, + &MOType::resetJuneSurfaceGroundTemperature, + &MOType::isJuneSurfaceGroundTemperatureDefaulted, }, { - &SH::julySurfaceGroundTemperature, - &SH::setJulySurfaceGroundTemperature, - &SH::resetJulySurfaceGroundTemperature, - &SH::isJulySurfaceGroundTemperatureDefaulted, + &MOType::julySurfaceGroundTemperature, + &MOType::setJulySurfaceGroundTemperature, + &MOType::resetJulySurfaceGroundTemperature, + &MOType::isJulySurfaceGroundTemperatureDefaulted, }, { - &SH::augustSurfaceGroundTemperature, - &SH::setAugustSurfaceGroundTemperature, - &SH::resetAugustSurfaceGroundTemperature, - &SH::isAugustSurfaceGroundTemperatureDefaulted, + &MOType::augustSurfaceGroundTemperature, + &MOType::setAugustSurfaceGroundTemperature, + &MOType::resetAugustSurfaceGroundTemperature, + &MOType::isAugustSurfaceGroundTemperatureDefaulted, }, { - &SH::septemberSurfaceGroundTemperature, - &SH::setSeptemberSurfaceGroundTemperature, - &SH::resetSeptemberSurfaceGroundTemperature, - &SH::isSeptemberSurfaceGroundTemperatureDefaulted, + &MOType::septemberSurfaceGroundTemperature, + &MOType::setSeptemberSurfaceGroundTemperature, + &MOType::resetSeptemberSurfaceGroundTemperature, + &MOType::isSeptemberSurfaceGroundTemperatureDefaulted, }, { - &SH::octoberSurfaceGroundTemperature, - &SH::setOctoberSurfaceGroundTemperature, - &SH::resetOctoberSurfaceGroundTemperature, - &SH::isOctoberSurfaceGroundTemperatureDefaulted, + &MOType::octoberSurfaceGroundTemperature, + &MOType::setOctoberSurfaceGroundTemperature, + &MOType::resetOctoberSurfaceGroundTemperature, + &MOType::isOctoberSurfaceGroundTemperatureDefaulted, }, { - &SH::novemberSurfaceGroundTemperature, - &SH::setNovemberSurfaceGroundTemperature, - &SH::resetNovemberSurfaceGroundTemperature, - &SH::isNovemberSurfaceGroundTemperatureDefaulted, + &MOType::novemberSurfaceGroundTemperature, + &MOType::setNovemberSurfaceGroundTemperature, + &MOType::resetNovemberSurfaceGroundTemperature, + &MOType::isNovemberSurfaceGroundTemperatureDefaulted, }, { - &SH::decemberSurfaceGroundTemperature, - &SH::setDecemberSurfaceGroundTemperature, - &SH::resetDecemberSurfaceGroundTemperature, - &SH::isDecemberSurfaceGroundTemperatureDefaulted, + &MOType::decemberSurfaceGroundTemperature, + &MOType::setDecemberSurfaceGroundTemperature, + &MOType::resetDecemberSurfaceGroundTemperature, + &MOType::isDecemberSurfaceGroundTemperatureDefaulted, }, }}; - boost::optional m_obj; + boost::optional m_obj; }; /** Inspector for Site:GroundTemperature:Deep — 12 monthly fields. */ @@ -254,89 +254,89 @@ class SiteGroundTemperatureDeepWidget : public SiteGroundTemperatureMonthlyWidge void applyConstantValue(double celsius) override; private: - using DP = model::SiteGroundTemperatureDeep; + using MOType = model::SiteGroundTemperatureDeep; struct MonthBinding { - double (DP::*getter)() const; // cppcheck-suppress unusedStructMember - bool (DP::*setter)(double); // cppcheck-suppress unusedStructMember - void (DP::*resetter)(); // cppcheck-suppress unusedStructMember - bool (DP::*defaulted)() const; // cppcheck-suppress unusedStructMember + double (MOType::*getter)() const; // cppcheck-suppress unusedStructMember + bool (MOType::*setter)(double); // cppcheck-suppress unusedStructMember + void (MOType::*resetter)(); // cppcheck-suppress unusedStructMember + bool (MOType::*defaulted)() const; // cppcheck-suppress unusedStructMember }; inline static const std::array s_monthBinders{{ { - &DP::januaryDeepGroundTemperature, - &DP::setJanuaryDeepGroundTemperature, - &DP::resetJanuaryDeepGroundTemperature, - &DP::isJanuaryDeepGroundTemperatureDefaulted, + &MOType::januaryDeepGroundTemperature, + &MOType::setJanuaryDeepGroundTemperature, + &MOType::resetJanuaryDeepGroundTemperature, + &MOType::isJanuaryDeepGroundTemperatureDefaulted, }, { - &DP::februaryDeepGroundTemperature, - &DP::setFebruaryDeepGroundTemperature, - &DP::resetFebruaryDeepGroundTemperature, - &DP::isFebruaryDeepGroundTemperatureDefaulted, + &MOType::februaryDeepGroundTemperature, + &MOType::setFebruaryDeepGroundTemperature, + &MOType::resetFebruaryDeepGroundTemperature, + &MOType::isFebruaryDeepGroundTemperatureDefaulted, }, { - &DP::marchDeepGroundTemperature, - &DP::setMarchDeepGroundTemperature, - &DP::resetMarchDeepGroundTemperature, - &DP::isMarchDeepGroundTemperatureDefaulted, + &MOType::marchDeepGroundTemperature, + &MOType::setMarchDeepGroundTemperature, + &MOType::resetMarchDeepGroundTemperature, + &MOType::isMarchDeepGroundTemperatureDefaulted, }, { - &DP::aprilDeepGroundTemperature, - &DP::setAprilDeepGroundTemperature, - &DP::resetAprilDeepGroundTemperature, - &DP::isAprilDeepGroundTemperatureDefaulted, + &MOType::aprilDeepGroundTemperature, + &MOType::setAprilDeepGroundTemperature, + &MOType::resetAprilDeepGroundTemperature, + &MOType::isAprilDeepGroundTemperatureDefaulted, }, { - &DP::mayDeepGroundTemperature, - &DP::setMayDeepGroundTemperature, - &DP::resetMayDeepGroundTemperature, - &DP::isMayDeepGroundTemperatureDefaulted, + &MOType::mayDeepGroundTemperature, + &MOType::setMayDeepGroundTemperature, + &MOType::resetMayDeepGroundTemperature, + &MOType::isMayDeepGroundTemperatureDefaulted, }, { - &DP::juneDeepGroundTemperature, - &DP::setJuneDeepGroundTemperature, - &DP::resetJuneDeepGroundTemperature, - &DP::isJuneDeepGroundTemperatureDefaulted, + &MOType::juneDeepGroundTemperature, + &MOType::setJuneDeepGroundTemperature, + &MOType::resetJuneDeepGroundTemperature, + &MOType::isJuneDeepGroundTemperatureDefaulted, }, { - &DP::julyDeepGroundTemperature, - &DP::setJulyDeepGroundTemperature, - &DP::resetJulyDeepGroundTemperature, - &DP::isJulyDeepGroundTemperatureDefaulted, + &MOType::julyDeepGroundTemperature, + &MOType::setJulyDeepGroundTemperature, + &MOType::resetJulyDeepGroundTemperature, + &MOType::isJulyDeepGroundTemperatureDefaulted, }, { - &DP::augustDeepGroundTemperature, - &DP::setAugustDeepGroundTemperature, - &DP::resetAugustDeepGroundTemperature, - &DP::isAugustDeepGroundTemperatureDefaulted, + &MOType::augustDeepGroundTemperature, + &MOType::setAugustDeepGroundTemperature, + &MOType::resetAugustDeepGroundTemperature, + &MOType::isAugustDeepGroundTemperatureDefaulted, }, { - &DP::septemberDeepGroundTemperature, - &DP::setSeptemberDeepGroundTemperature, - &DP::resetSeptemberDeepGroundTemperature, - &DP::isSeptemberDeepGroundTemperatureDefaulted, + &MOType::septemberDeepGroundTemperature, + &MOType::setSeptemberDeepGroundTemperature, + &MOType::resetSeptemberDeepGroundTemperature, + &MOType::isSeptemberDeepGroundTemperatureDefaulted, }, { - &DP::octoberDeepGroundTemperature, - &DP::setOctoberDeepGroundTemperature, - &DP::resetOctoberDeepGroundTemperature, - &DP::isOctoberDeepGroundTemperatureDefaulted, + &MOType::octoberDeepGroundTemperature, + &MOType::setOctoberDeepGroundTemperature, + &MOType::resetOctoberDeepGroundTemperature, + &MOType::isOctoberDeepGroundTemperatureDefaulted, }, { - &DP::novemberDeepGroundTemperature, - &DP::setNovemberDeepGroundTemperature, - &DP::resetNovemberDeepGroundTemperature, - &DP::isNovemberDeepGroundTemperatureDefaulted, + &MOType::novemberDeepGroundTemperature, + &MOType::setNovemberDeepGroundTemperature, + &MOType::resetNovemberDeepGroundTemperature, + &MOType::isNovemberDeepGroundTemperatureDefaulted, }, { - &DP::decemberDeepGroundTemperature, - &DP::setDecemberDeepGroundTemperature, - &DP::resetDecemberDeepGroundTemperature, - &DP::isDecemberDeepGroundTemperatureDefaulted, + &MOType::decemberDeepGroundTemperature, + &MOType::setDecemberDeepGroundTemperature, + &MOType::resetDecemberDeepGroundTemperature, + &MOType::isDecemberDeepGroundTemperatureDefaulted, }, }}; - boost::optional m_obj; + boost::optional m_obj; }; } // namespace openstudio From e175f30efc22ee3abc23f56bcd274f864749eb00 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 17:09:44 +0100 Subject: [PATCH 27/90] Make the QDoubleSPinBox default to be january value for the object --- .../GroundTemperatureMonthlyInspectorView.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 4aa57a7fc..06bca7418 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -144,6 +144,10 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); } + + // Default value is the January one + const double januaryCelsius = (m_obj.get_ptr()->*s_monthBinders[0].getter)(); + m_constantValueEdit->setValue(m_isIP ? januaryCelsius * 9.0 / 5.0 + 32.0 : januaryCelsius); } void SiteGroundTemperatureBuildingSurfaceWidget::applyConstantValue(double celsius) { @@ -174,6 +178,9 @@ void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); } + + const double januaryCelsius = (m_obj.get_ptr()->*s_monthBinders[0].getter)(); + m_constantValueEdit->setValue(m_isIP ? januaryCelsius * 9.0 / 5.0 + 32.0 : januaryCelsius); } void SiteGroundTemperatureShallowWidget::applyConstantValue(double celsius) { @@ -203,6 +210,9 @@ void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); } + + const double januaryCelsius = (m_obj.get_ptr()->*s_monthBinders[0].getter)(); + m_constantValueEdit->setValue(m_isIP ? januaryCelsius * 9.0 / 5.0 + 32.0 : januaryCelsius); } void SiteGroundTemperatureDeepWidget::applyConstantValue(double celsius) { From bf6e5cf050bbf9c0625a49522e09239be57a050e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 17:25:33 +0100 Subject: [PATCH 28/90] Add a bar chart to display monthly values? --- .../GroundTemperatureMonthlyInspectorView.cpp | 116 ++++++++++++++++-- .../GroundTemperatureMonthlyInspectorView.hpp | 14 +++ 2 files changed, 122 insertions(+), 8 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 06bca7418..2c03ac56b 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -13,13 +13,21 @@ #include #include +#include +#include +#include +#include +#include #include #include #include #include #include +#include +#include #include #include +#include #include #define TEMP_EDIT_WIDTH 90 @@ -59,6 +67,9 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP gridLayout->addWidget(new QLabel(monthNames[i]), i + 1, 0); m_edits[i] = new OSQuantityEdit2("C", "C", "F", m_isIP); connect(this, &SiteGroundTemperatureMonthlyWidget::toggleUnitsClicked, m_edits[i], &OSQuantityEdit2::onUnitSystemChange); + if (auto* lineEdit = m_edits[i]->findChild()) { + connect(lineEdit, &QLineEdit::editingFinished, this, &SiteGroundTemperatureMonthlyWidget::refreshChartFromModel); + } m_edits[i]->setFixedWidth(TEMP_EDIT_WIDTH); gridLayout->addWidget(m_edits[i], i + 1, 1, Qt::AlignLeft); } @@ -112,9 +123,42 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP const double val = m_constantValueEdit->value(); const double celsius = m_isIP ? (val - 32.0) * 5.0 / 9.0 : val; applyConstantValue(celsius); + m_cachedCelsius.fill(celsius); + refreshChartDisplay(); }); - mainLayout->addStretch(); + // Chart + m_chartBarSet = new QBarSet(QString()); + for (int i = 0; i < 12; ++i) { + m_chartBarSet->append(0.0); + } + + auto* barSeries = new QBarSeries; + barSeries->append(m_chartBarSet); + + const QStringList monthAbbrevs = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + auto* axisX = new QBarCategoryAxis; + axisX->append(monthAbbrevs); + + m_chartYAxis = new QValueAxis; + m_chartYAxis->setTitleText(isIP ? tr("Temperature [°F]") : tr("Temperature [°C]")); + + auto* chart = new QChart; + chart->legend()->hide(); + chart->addSeries(barSeries); + chart->addAxis(axisX, Qt::AlignBottom); + chart->addAxis(m_chartYAxis, Qt::AlignLeft); + barSeries->attachAxis(axisX); + barSeries->attachAxis(m_chartYAxis); + chart->setAnimationOptions(QChart::SeriesAnimations); + + auto* chartView = new QChartView(chart); + chartView->setRenderHint(QPainter::Antialiasing); + chartView->setMinimumHeight(220); + chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mainLayout->addWidget(chartView, 1); + + connect(this, &SiteGroundTemperatureMonthlyWidget::toggleUnitsClicked, this, [this](bool) { refreshChartDisplay(); }); } void SiteGroundTemperatureMonthlyWidget::detach() { @@ -123,6 +167,39 @@ void SiteGroundTemperatureMonthlyWidget::detach() { edit->unbind(); } } + m_valuesGetter = nullptr; +} + +void SiteGroundTemperatureMonthlyWidget::refreshChartFromModel() { + if (m_valuesGetter) { + setChartValues(m_valuesGetter()); + } +} + +void SiteGroundTemperatureMonthlyWidget::setChartValues(const std::array& celsiusValues) { + m_cachedCelsius = celsiusValues; + refreshChartDisplay(); +} + +void SiteGroundTemperatureMonthlyWidget::refreshChartDisplay() { + if (!m_chartBarSet || !m_chartYAxis) { + return; + } + double minVal = m_isIP ? m_cachedCelsius[0] * 9.0 / 5.0 + 32.0 : m_cachedCelsius[0]; + double maxVal = minVal; + for (int i = 0; i < 12; ++i) { + const double val = m_isIP ? m_cachedCelsius[i] * 9.0 / 5.0 + 32.0 : m_cachedCelsius[i]; + m_chartBarSet->replace(i, val); + if (val < minVal) { + minVal = val; + } + if (val > maxVal) { + maxVal = val; + } + } + const double pad = m_isIP ? 3.6 : 2.0; // ~2°C in °F + m_chartYAxis->setRange(minVal - pad, maxVal + pad); + m_chartYAxis->setTitleText(m_isIP ? tr("Temperature [°F]") : tr("Temperature [°C]")); } // ───────────────────────────────────────────────────────── @@ -145,9 +222,16 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); } - // Default value is the January one - const double januaryCelsius = (m_obj.get_ptr()->*s_monthBinders[0].getter)(); - m_constantValueEdit->setValue(m_isIP ? januaryCelsius * 9.0 / 5.0 + 32.0 : januaryCelsius); + m_valuesGetter = [this]() { + std::array vals{}; + for (int i = 0; i < 12; ++i) { + vals[i] = (m_obj.get_ptr()->*s_monthBinders[i].getter)(); + } + return vals; + }; + const auto vals = m_valuesGetter(); + setChartValues(vals); + m_constantValueEdit->setValue(m_isIP ? vals[0] * 9.0 / 5.0 + 32.0 : vals[0]); } void SiteGroundTemperatureBuildingSurfaceWidget::applyConstantValue(double celsius) { @@ -179,8 +263,16 @@ void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); } - const double januaryCelsius = (m_obj.get_ptr()->*s_monthBinders[0].getter)(); - m_constantValueEdit->setValue(m_isIP ? januaryCelsius * 9.0 / 5.0 + 32.0 : januaryCelsius); + m_valuesGetter = [this]() { + std::array vals{}; + for (int i = 0; i < 12; ++i) { + vals[i] = (m_obj.get_ptr()->*s_monthBinders[i].getter)(); + } + return vals; + }; + const auto vals = m_valuesGetter(); + setChartValues(vals); + m_constantValueEdit->setValue(m_isIP ? vals[0] * 9.0 / 5.0 + 32.0 : vals[0]); } void SiteGroundTemperatureShallowWidget::applyConstantValue(double celsius) { @@ -211,8 +303,16 @@ void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); } - const double januaryCelsius = (m_obj.get_ptr()->*s_monthBinders[0].getter)(); - m_constantValueEdit->setValue(m_isIP ? januaryCelsius * 9.0 / 5.0 + 32.0 : januaryCelsius); + m_valuesGetter = [this]() { + std::array vals{}; + for (int i = 0; i < 12; ++i) { + vals[i] = (m_obj.get_ptr()->*s_monthBinders[i].getter)(); + } + return vals; + }; + const auto vals = m_valuesGetter(); + setChartValues(vals); + m_constantValueEdit->setValue(m_isIP ? vals[0] * 9.0 / 5.0 + 32.0 : vals[0]); } void SiteGroundTemperatureDeepWidget::applyConstantValue(double celsius) { diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp index 73247e3f8..661ba7ba1 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -14,10 +14,13 @@ #include #include +#include +class QBarSet; class QDoubleSpinBox; class QLabel; class QPushButton; +class QValueAxis; namespace openstudio { @@ -41,11 +44,22 @@ class SiteGroundTemperatureMonthlyWidget : public QWidget void toggleUnitsClicked(bool displayIP); protected: + void setChartValues(const std::array& celsiusValues); + void refreshChartFromModel(); + bool m_isIP; + std::function()> m_valuesGetter; QLabel* m_titleLabel = nullptr; QDoubleSpinBox* m_constantValueEdit = nullptr; QPushButton* m_applyConstantButton = nullptr; std::array m_edits{}; + + private: + void refreshChartDisplay(); + + QBarSet* m_chartBarSet = nullptr; + QValueAxis* m_chartYAxis = nullptr; + std::array m_cachedCelsius{}; }; /** Inspector for Site:GroundTemperature:BuildingSurface — 12 monthly fields. */ From aa713b86104c6f9e801131a3ddd915e29e981945 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 17:31:30 +0100 Subject: [PATCH 29/90] Make the MonthlyWidget inside a QScrollArea to avoid monthly edit fields get insanely too small in height when I have the App window as small as possible Min window size is there openstudio_lib/MainWindow.cpp 143: return {1024, 700} OSQuantityEdit2 and QLabel widgets have no explicit minimum height, so when vertical space is tight, they get compressed before the chart (which has setMinimumHeight(220)). The proper fix is a QScrollArea around the content so it can scroll rather than compress. --- .../GroundTemperatureMonthlyInspectorView.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 2c03ac56b..6e3907088 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -39,11 +40,22 @@ namespace openstudio { // ───────────────────────────────────────────────────────── SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP, QWidget* parent) : QWidget(parent), m_isIP(isIP) { + auto* container = new QWidget(); auto* mainLayout = new QVBoxLayout(); mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); mainLayout->setContentsMargins(10, 10, 10, 10); mainLayout->setSpacing(20); - setLayout(mainLayout); + container->setLayout(mainLayout); + + auto* scrollArea = new QScrollArea(this); + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(container); + scrollArea->setFrameShape(QFrame::NoFrame); + + auto* outerLayout = new QVBoxLayout(this); + outerLayout->setContentsMargins(0, 0, 0, 0); + outerLayout->addWidget(scrollArea); + setLayout(outerLayout); m_titleLabel = new QLabel(); m_titleLabel->setObjectName("H2"); From f111d8cc6cc0876c2c19dbbae95aed60338cc5ad Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 17:40:00 +0100 Subject: [PATCH 30/90] Implement a axhline at 0 if it crosses it, and a hover tooltip for the chart --- .../GroundTemperatureMonthlyInspectorView.cpp | 38 ++++++++++++++++++- .../GroundTemperatureMonthlyInspectorView.hpp | 2 + 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 6e3907088..44a88dc6b 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -14,11 +14,13 @@ #include #include -#include #include #include #include #include +#include +#include +#include #include #include #include @@ -28,6 +30,7 @@ #include #include #include +#include #include #include @@ -155,15 +158,44 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP m_chartYAxis = new QValueAxis; m_chartYAxis->setTitleText(isIP ? tr("Temperature [°F]") : tr("Temperature [°C]")); + // Zero line — uses a hidden numeric x axis so it can span the bar range + auto* hiddenXAxis = new QValueAxis; + hiddenXAxis->setRange(-0.5, 11.5); + hiddenXAxis->setVisible(false); + + m_zeroLine = new QLineSeries; + m_zeroLine->append(-0.5, 0.0); + m_zeroLine->append(11.5, 0.0); + QPen zeroPen(QColor(80, 80, 80)); + zeroPen.setWidth(1); + zeroPen.setStyle(Qt::DashLine); + m_zeroLine->setPen(zeroPen); + m_zeroLine->setVisible(false); + auto* chart = new QChart; chart->legend()->hide(); chart->addSeries(barSeries); + chart->addSeries(m_zeroLine); chart->addAxis(axisX, Qt::AlignBottom); + chart->addAxis(hiddenXAxis, Qt::AlignBottom); chart->addAxis(m_chartYAxis, Qt::AlignLeft); barSeries->attachAxis(axisX); barSeries->attachAxis(m_chartYAxis); + m_zeroLine->attachAxis(hiddenXAxis); + m_zeroLine->attachAxis(m_chartYAxis); chart->setAnimationOptions(QChart::SeriesAnimations); + // Hover tooltip + connect(m_chartBarSet, &QBarSet::hovered, this, [this, monthNames](bool status, int index) { + if (status && index >= 0 && index < 12) { + const double display = m_isIP ? m_cachedCelsius[index] * 9.0 / 5.0 + 32.0 : m_cachedCelsius[index]; + const QString unit = m_isIP ? tr("°F") : tr("°C"); + QToolTip::showText(QCursor::pos(), QString("%1: %2 %3").arg(monthNames[index]).arg(display, 0, 'f', 1).arg(unit)); + } else { + QToolTip::hideText(); + } + }); + auto* chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); chartView->setMinimumHeight(220); @@ -212,6 +244,10 @@ void SiteGroundTemperatureMonthlyWidget::refreshChartDisplay() { const double pad = m_isIP ? 3.6 : 2.0; // ~2°C in °F m_chartYAxis->setRange(minVal - pad, maxVal + pad); m_chartYAxis->setTitleText(m_isIP ? tr("Temperature [°F]") : tr("Temperature [°C]")); + + if (m_zeroLine) { + m_zeroLine->setVisible(minVal < 0.0 && maxVal > 0.0); + } } // ───────────────────────────────────────────────────────── diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp index 661ba7ba1..4d49ea8b0 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -19,6 +19,7 @@ class QBarSet; class QDoubleSpinBox; class QLabel; +class QLineSeries; class QPushButton; class QValueAxis; @@ -58,6 +59,7 @@ class SiteGroundTemperatureMonthlyWidget : public QWidget void refreshChartDisplay(); QBarSet* m_chartBarSet = nullptr; + QLineSeries* m_zeroLine = nullptr; QValueAxis* m_chartYAxis = nullptr; std::array m_cachedCelsius{}; }; From fc162f9b493ade3b2b361a9693a57012af6dfb91 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 17:45:13 +0100 Subject: [PATCH 31/90] Inhrit nano observer instead of manually triggering refreshChartFromModl --- .../GroundTemperatureMonthlyInspectorView.cpp | 23 +++++++++++++++---- .../GroundTemperatureMonthlyInspectorView.hpp | 8 ++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 44a88dc6b..656348fae 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -9,6 +9,7 @@ #include "../shared_gui_components/OSQuantityEdit.hpp" +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -82,9 +82,6 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP gridLayout->addWidget(new QLabel(monthNames[i]), i + 1, 0); m_edits[i] = new OSQuantityEdit2("C", "C", "F", m_isIP); connect(this, &SiteGroundTemperatureMonthlyWidget::toggleUnitsClicked, m_edits[i], &OSQuantityEdit2::onUnitSystemChange); - if (auto* lineEdit = m_edits[i]->findChild()) { - connect(lineEdit, &QLineEdit::editingFinished, this, &SiteGroundTemperatureMonthlyWidget::refreshChartFromModel); - } m_edits[i]->setFixedWidth(TEMP_EDIT_WIDTH); gridLayout->addWidget(m_edits[i], i + 1, 1, Qt::AlignLeft); } @@ -212,6 +209,7 @@ void SiteGroundTemperatureMonthlyWidget::detach() { } } m_valuesGetter = nullptr; + disconnectModelChanges(); } void SiteGroundTemperatureMonthlyWidget::refreshChartFromModel() { @@ -220,6 +218,20 @@ void SiteGroundTemperatureMonthlyWidget::refreshChartFromModel() { } } +void SiteGroundTemperatureMonthlyWidget::connectModelChanges(const model::ModelObject& obj) { + m_connectedModelObj = obj; + obj.getImpl() + ->onChange.connect(this); +} + +void SiteGroundTemperatureMonthlyWidget::disconnectModelChanges() { + if (m_connectedModelObj) { + m_connectedModelObj->getImpl() + ->onChange.disconnect(this); + m_connectedModelObj = boost::none; + } +} + void SiteGroundTemperatureMonthlyWidget::setChartValues(const std::array& celsiusValues) { m_cachedCelsius = celsiusValues; refreshChartDisplay(); @@ -280,6 +292,7 @@ void SiteGroundTemperatureBuildingSurfaceWidget::attach(const model::ModelObject const auto vals = m_valuesGetter(); setChartValues(vals); m_constantValueEdit->setValue(m_isIP ? vals[0] * 9.0 / 5.0 + 32.0 : vals[0]); + connectModelChanges(obj); } void SiteGroundTemperatureBuildingSurfaceWidget::applyConstantValue(double celsius) { @@ -321,6 +334,7 @@ void SiteGroundTemperatureShallowWidget::attach(const model::ModelObject& obj) { const auto vals = m_valuesGetter(); setChartValues(vals); m_constantValueEdit->setValue(m_isIP ? vals[0] * 9.0 / 5.0 + 32.0 : vals[0]); + connectModelChanges(obj); } void SiteGroundTemperatureShallowWidget::applyConstantValue(double celsius) { @@ -361,6 +375,7 @@ void SiteGroundTemperatureDeepWidget::attach(const model::ModelObject& obj) { const auto vals = m_valuesGetter(); setChartValues(vals); m_constantValueEdit->setValue(m_isIP ? vals[0] * 9.0 / 5.0 + 32.0 : vals[0]); + connectModelChanges(obj); } void SiteGroundTemperatureDeepWidget::applyConstantValue(double celsius) { diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp index 4d49ea8b0..e1b4a1d91 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -7,6 +7,7 @@ #define OPENSTUDIO_GROUNDTEMPERATUREMONTHLYINSPECTORVIEW_HPP #include +#include #include #include #include @@ -28,7 +29,9 @@ namespace openstudio { class OSQuantityEdit2; /** Abstract base for inspector widgets that show 12 monthly temperature fields. */ -class SiteGroundTemperatureMonthlyWidget : public QWidget +class SiteGroundTemperatureMonthlyWidget + : public QWidget + , public Nano::Observer { Q_OBJECT @@ -47,6 +50,8 @@ class SiteGroundTemperatureMonthlyWidget : public QWidget protected: void setChartValues(const std::array& celsiusValues); void refreshChartFromModel(); + void connectModelChanges(const model::ModelObject& obj); + void disconnectModelChanges(); bool m_isIP; std::function()> m_valuesGetter; @@ -58,6 +63,7 @@ class SiteGroundTemperatureMonthlyWidget : public QWidget private: void refreshChartDisplay(); + boost::optional m_connectedModelObj; QBarSet* m_chartBarSet = nullptr; QLineSeries* m_zeroLine = nullptr; QValueAxis* m_chartYAxis = nullptr; From 7e45ad7d845a10aca118537bcf9836a56915167b Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 17:56:47 +0100 Subject: [PATCH 32/90] Add Site:GroundTmeperature:FCFactorMethod with option to import from 0.5m EPW data https://bigladdersoftware.com/epx/docs/25-1/input-output-reference/group-location-climate-weather-file-access.html#sitegroundtemperaturefcfactormethod > If user does not input this object in the IDF file, it will be defaulted to the 0.5m set of monthly ground temperatures from the weather file if they are available. --- .../GroundTemperatureMonthlyInspectorView.cpp | 45 ++++++++- .../GroundTemperatureMonthlyInspectorView.hpp | 98 +++++++++++++++++++ src/openstudio_lib/GroundTemperatureView.cpp | 63 +++++++++++- src/openstudio_lib/GroundTemperatureView.hpp | 4 + 4 files changed, 207 insertions(+), 3 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 656348fae..8ae2b459e 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -11,8 +11,9 @@ #include #include -#include #include +#include +#include #include #include @@ -387,4 +388,46 @@ void SiteGroundTemperatureDeepWidget::applyConstantValue(double celsius) { } } +// ───────────────────────────────────────────────────────── +// SiteGroundTemperatureFCfactorMethodWidget +// ───────────────────────────────────────────────────────── + +SiteGroundTemperatureFCfactorMethodWidget::SiteGroundTemperatureFCfactorMethodWidget(bool isIP, QWidget* parent) + : SiteGroundTemperatureMonthlyWidget(isIP, parent) {} + +void SiteGroundTemperatureFCfactorMethodWidget::attach(const model::ModelObject& obj) { + detach(); + m_obj = obj.cast(); + m_titleLabel->setText("Site:GroundTemperature:FCfactorMethod"); + + for (int i = 0; i < 12; ++i) { + const auto& mb = s_monthBinders[i]; + m_edits[i]->bind(m_isIP, *m_obj, DoubleGetter([this, g = mb.getter]() { return (m_obj.get_ptr()->*g)(); }), + boost::optional([this, s = mb.setter](double v) { return (m_obj.get_ptr()->*s)(v); }), + boost::optional([this, r = mb.resetter]() { (m_obj.get_ptr()->*r)(); }), boost::none, boost::none, + boost::optional([this, d = mb.defaulted]() { return (m_obj.get_ptr()->*d)(); })); + } + + m_valuesGetter = [this]() { + std::array vals{}; + for (int i = 0; i < 12; ++i) { + vals[i] = (m_obj.get_ptr()->*s_monthBinders[i].getter)(); + } + return vals; + }; + const auto vals = m_valuesGetter(); + setChartValues(vals); + m_constantValueEdit->setValue(m_isIP ? vals[0] * 9.0 / 5.0 + 32.0 : vals[0]); + connectModelChanges(obj); +} + +void SiteGroundTemperatureFCfactorMethodWidget::applyConstantValue(double celsius) { + if (!m_obj) { + return; + } + for (const auto& mb : s_monthBinders) { + (m_obj.get_ptr()->*mb.setter)(celsius); + } +} + } // namespace openstudio diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp index e1b4a1d91..5026eb6d3 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -361,6 +362,103 @@ class SiteGroundTemperatureDeepWidget : public SiteGroundTemperatureMonthlyWidge boost::optional m_obj; }; +/** Inspector for Site:GroundTemperature:FCfactorMethod — 12 monthly fields. */ +class SiteGroundTemperatureFCfactorMethodWidget : public SiteGroundTemperatureMonthlyWidget +{ + Q_OBJECT + + public: + explicit SiteGroundTemperatureFCfactorMethodWidget(bool isIP, QWidget* parent = nullptr); + + void attach(const model::ModelObject& obj) override; + void applyConstantValue(double celsius) override; + + private: + using MOType = model::SiteGroundTemperatureFCfactorMethod; + struct MonthBinding + { + double (MOType::*getter)() const; // cppcheck-suppress unusedStructMember + bool (MOType::*setter)(double); // cppcheck-suppress unusedStructMember + void (MOType::*resetter)(); // cppcheck-suppress unusedStructMember + bool (MOType::*defaulted)() const; // cppcheck-suppress unusedStructMember + }; + inline static const std::array s_monthBinders{{ + { + &MOType::januaryGroundTemperature, + &MOType::setJanuaryGroundTemperature, + &MOType::resetJanuaryGroundTemperature, + &MOType::isJanuaryGroundTemperatureDefaulted, + }, + { + &MOType::februaryGroundTemperature, + &MOType::setFebruaryGroundTemperature, + &MOType::resetFebruaryGroundTemperature, + &MOType::isFebruaryGroundTemperatureDefaulted, + }, + { + &MOType::marchGroundTemperature, + &MOType::setMarchGroundTemperature, + &MOType::resetMarchGroundTemperature, + &MOType::isMarchGroundTemperatureDefaulted, + }, + { + &MOType::aprilGroundTemperature, + &MOType::setAprilGroundTemperature, + &MOType::resetAprilGroundTemperature, + &MOType::isAprilGroundTemperatureDefaulted, + }, + { + &MOType::mayGroundTemperature, + &MOType::setMayGroundTemperature, + &MOType::resetMayGroundTemperature, + &MOType::isMayGroundTemperatureDefaulted, + }, + { + &MOType::juneGroundTemperature, + &MOType::setJuneGroundTemperature, + &MOType::resetJuneGroundTemperature, + &MOType::isJuneGroundTemperatureDefaulted, + }, + { + &MOType::julyGroundTemperature, + &MOType::setJulyGroundTemperature, + &MOType::resetJulyGroundTemperature, + &MOType::isJulyGroundTemperatureDefaulted, + }, + { + &MOType::augustGroundTemperature, + &MOType::setAugustGroundTemperature, + &MOType::resetAugustGroundTemperature, + &MOType::isAugustGroundTemperatureDefaulted, + }, + { + &MOType::septemberGroundTemperature, + &MOType::setSeptemberGroundTemperature, + &MOType::resetSeptemberGroundTemperature, + &MOType::isSeptemberGroundTemperatureDefaulted, + }, + { + &MOType::octoberGroundTemperature, + &MOType::setOctoberGroundTemperature, + &MOType::resetOctoberGroundTemperature, + &MOType::isOctoberGroundTemperatureDefaulted, + }, + { + &MOType::novemberGroundTemperature, + &MOType::setNovemberGroundTemperature, + &MOType::resetNovemberGroundTemperature, + &MOType::isNovemberGroundTemperatureDefaulted, + }, + { + &MOType::decemberGroundTemperature, + &MOType::setDecemberGroundTemperature, + &MOType::resetDecemberGroundTemperature, + &MOType::isDecemberGroundTemperatureDefaulted, + }, + }}; + boost::optional m_obj; +}; + } // namespace openstudio #endif // OPENSTUDIO_GROUNDTEMPERATUREMONTHLYINSPECTORVIEW_HPP diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index e3430654e..f22a8c496 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -11,8 +11,9 @@ #include "../model_editor/Utilities.hpp" #include -#include #include +#include +#include #include #include #include @@ -92,16 +93,19 @@ GroundTemperatureListView::GroundTemperatureListView(QWidget* parent) : QWidget( m_bsEntry = new GroundTemperatureEntry(tr("Building Surface Ground Temperatures"), this); m_shEntry = new GroundTemperatureEntry(tr("Shallow Ground Temperatures"), this); m_deepEntry = new GroundTemperatureEntry(tr("Deep Ground Temperatures"), this); + m_fcEntry = new GroundTemperatureEntry(tr("FCfactorMethod Ground Temperatures"), this); m_waterMainsEntry = new GroundTemperatureEntry(tr("Water Mains Temperature"), this); connect(m_bsEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onBuildingSurfaceClicked); connect(m_shEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onShallowClicked); connect(m_deepEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onDeepClicked); + connect(m_fcEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onFCfactorMethodClicked); connect(m_waterMainsEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onWaterMainsClicked); layout->addWidget(m_bsEntry); layout->addWidget(m_shEntry); layout->addWidget(m_deepEntry); + layout->addWidget(m_fcEntry); layout->addWidget(m_waterMainsEntry); layout->addStretch(); } @@ -114,6 +118,7 @@ void GroundTemperatureListView::onBuildingSurfaceClicked() { m_bsEntry->setSelected(true); m_shEntry->setSelected(false); m_deepEntry->setSelected(false); + m_fcEntry->setSelected(false); m_waterMainsEntry->setSelected(false); emit typeSelected(GroundTempType::BuildingSurface); } @@ -122,6 +127,7 @@ void GroundTemperatureListView::onShallowClicked() { m_bsEntry->setSelected(false); m_shEntry->setSelected(true); m_deepEntry->setSelected(false); + m_fcEntry->setSelected(false); m_waterMainsEntry->setSelected(false); emit typeSelected(GroundTempType::Shallow); } @@ -130,14 +136,25 @@ void GroundTemperatureListView::onDeepClicked() { m_bsEntry->setSelected(false); m_shEntry->setSelected(false); m_deepEntry->setSelected(true); + m_fcEntry->setSelected(false); m_waterMainsEntry->setSelected(false); emit typeSelected(GroundTempType::Deep); } +void GroundTemperatureListView::onFCfactorMethodClicked() { + m_bsEntry->setSelected(false); + m_shEntry->setSelected(false); + m_deepEntry->setSelected(false); + m_fcEntry->setSelected(true); + m_waterMainsEntry->setSelected(false); + emit typeSelected(GroundTempType::FCfactorMethod); +} + void GroundTemperatureListView::onWaterMainsClicked() { m_bsEntry->setSelected(false); m_shEntry->setSelected(false); m_deepEntry->setSelected(false); + m_fcEntry->setSelected(false); m_waterMainsEntry->setSelected(true); emit typeSelected(GroundTempType::WaterMains); } @@ -186,6 +203,8 @@ QString typeNameForGroundTempType(GroundTempType type) { return "Site:GroundTemperature:Shallow"; case GroundTempType::Deep: return "Site:GroundTemperature:Deep"; + case GroundTempType::FCfactorMethod: + return "Site:GroundTemperature:FCfactorMethod"; case GroundTempType::WaterMains: return "Site:WaterMainsTemperature"; default: @@ -246,7 +265,7 @@ void GroundTemperatureNotPresentView::setType(GroundTempType type, model::Model return; } - const double target_depth = (type == GroundTempType::Shallow) ? 0.5 : 4.0; + const double target_depth = (type == GroundTempType::Deep) ? 4.0 : 0.5; // Shallow and FCfactorMethod both use 0.5m // Now try to find the target_depth in the epw data, allowing for some tolerance since the epw spec doesn't require exact depths const double tolerance = 0.1; auto it = std::find_if(ground_temps.begin(), ground_temps.end(), [target_depth, tolerance](const EpwGroundTemperatureDepth& gtd) { @@ -284,6 +303,21 @@ void GroundTemperatureNotPresentView::setType(GroundTempType type, model::Model ts.setOctoberSurfaceGroundTemperature(it->octGroundTemperature()); ts.setNovemberSurfaceGroundTemperature(it->novGroundTemperature()); ts.setDecemberSurfaceGroundTemperature(it->decGroundTemperature()); + } else if (type == GroundTempType::FCfactorMethod) { + auto tf = m_model.getUniqueModelObject(); + + tf.setJanuaryGroundTemperature(it->janGroundTemperature()); + tf.setFebruaryGroundTemperature(it->febGroundTemperature()); + tf.setMarchGroundTemperature(it->marGroundTemperature()); + tf.setAprilGroundTemperature(it->aprGroundTemperature()); + tf.setMayGroundTemperature(it->mayGroundTemperature()); + tf.setJuneGroundTemperature(it->junGroundTemperature()); + tf.setJulyGroundTemperature(it->julGroundTemperature()); + tf.setAugustGroundTemperature(it->augGroundTemperature()); + tf.setSeptemberGroundTemperature(it->sepGroundTemperature()); + tf.setOctoberGroundTemperature(it->octGroundTemperature()); + tf.setNovemberGroundTemperature(it->novGroundTemperature()); + tf.setDecemberGroundTemperature(it->decGroundTemperature()); } else { // GroundTempType::Deep auto td = m_model.getUniqueModelObject(); @@ -365,6 +399,9 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode m_waterMainsView = new SiteWaterMainsTemperatureWidget(isIP); m_rightStack->addWidget(m_waterMainsView); // index 4 + m_fcView = new SiteGroundTemperatureFCfactorMethodWidget(isIP); + m_rightStack->addWidget(m_fcView); // index 5 + connect(m_listView, &GroundTemperatureListView::typeSelected, this, &GroundTemperatureView::onTypeSelected); connect(m_notPresentView, &GroundTemperatureNotPresentView::addClicked, this, &GroundTemperatureView::onObjectCreated); connect(m_selectorButtons, &OSItemSelectorButtons::addClicked, this, [this]() { onObjectCreated(m_currentType); }); @@ -374,6 +411,7 @@ GroundTemperatureView::GroundTemperatureView(bool isIP, const model::Model& mode connect(this, &GroundTemperatureView::toggleUnitsClicked, m_bsView, &SiteGroundTemperatureBuildingSurfaceWidget::toggleUnitsClicked); connect(this, &GroundTemperatureView::toggleUnitsClicked, m_shView, &SiteGroundTemperatureShallowWidget::toggleUnitsClicked); connect(this, &GroundTemperatureView::toggleUnitsClicked, m_deepView, &SiteGroundTemperatureDeepWidget::toggleUnitsClicked); + connect(this, &GroundTemperatureView::toggleUnitsClicked, m_fcView, &SiteGroundTemperatureFCfactorMethodWidget::toggleUnitsClicked); connect(this, &GroundTemperatureView::toggleUnitsClicked, m_waterMainsView, &SiteWaterMainsTemperatureWidget::toggleUnitsClicked); // Auto-select first entry after the event loop starts @@ -414,6 +452,16 @@ void GroundTemperatureView::onTypeSelected(GroundTempType type) { m_notPresentView->setType(type, m_model); m_rightStack->setCurrentIndex(0); } + } else if (type == GroundTempType::FCfactorMethod) { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + objectExists = true; + m_fcView->attach(*opt); + m_rightStack->setCurrentIndex(5); + } else { + m_notPresentView->setType(type, m_model); + m_rightStack->setCurrentIndex(0); + } } else { auto opt = m_model.siteWaterMainsTemperature(); if (opt) { @@ -448,6 +496,10 @@ void GroundTemperatureView::onObjectCreated(GroundTempType type) { auto obj = m_model.getUniqueModelObject(); m_deepView->attach(obj); m_rightStack->setCurrentIndex(3); + } else if (type == GroundTempType::FCfactorMethod) { + auto obj = m_model.getUniqueModelObject(); + m_fcView->attach(obj); + m_rightStack->setCurrentIndex(5); } else { auto obj = m_model.getUniqueModelObject(); m_waterMainsView->attach(obj); @@ -479,6 +531,13 @@ void GroundTemperatureView::onRemoveClicked() { opt->remove(); } m_notPresentView->setType(m_currentType, m_model); + } else if (m_currentType == GroundTempType::FCfactorMethod) { + auto opt = m_model.getOptionalUniqueModelObject(); + if (opt) { + m_fcView->detach(); + opt->remove(); + } + m_notPresentView->setType(m_currentType, m_model); } else { auto opt = m_model.siteWaterMainsTemperature(); if (opt) { diff --git a/src/openstudio_lib/GroundTemperatureView.hpp b/src/openstudio_lib/GroundTemperatureView.hpp index 7235ad1a3..94c30fa25 100644 --- a/src/openstudio_lib/GroundTemperatureView.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -26,6 +26,7 @@ enum class GroundTempType BuildingSurface, Shallow, Deep, + FCfactorMethod, WaterMains }; @@ -69,12 +70,14 @@ class GroundTemperatureListView : public QWidget void onBuildingSurfaceClicked(); void onShallowClicked(); void onDeepClicked(); + void onFCfactorMethodClicked(); void onWaterMainsClicked(); private: GroundTemperatureEntry* m_bsEntry = nullptr; GroundTemperatureEntry* m_shEntry = nullptr; GroundTemperatureEntry* m_deepEntry = nullptr; + GroundTemperatureEntry* m_fcEntry = nullptr; GroundTemperatureEntry* m_waterMainsEntry = nullptr; }; @@ -126,6 +129,7 @@ class GroundTemperatureView : public QWidget SiteGroundTemperatureBuildingSurfaceWidget* m_bsView = nullptr; SiteGroundTemperatureShallowWidget* m_shView = nullptr; SiteGroundTemperatureDeepWidget* m_deepView = nullptr; + SiteGroundTemperatureFCfactorMethodWidget* m_fcView = nullptr; SiteWaterMainsTemperatureWidget* m_waterMainsView = nullptr; QStackedWidget* m_rightStack = nullptr; }; From 9a1e3316c750fb2b540a53f89d26026b25b46407 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 23:08:31 +0100 Subject: [PATCH 33/90] Fix up background appearing white on mac --- src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 8ae2b459e..6c037dd7f 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -45,6 +45,7 @@ namespace openstudio { SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP, QWidget* parent) : QWidget(parent), m_isIP(isIP) { auto* container = new QWidget(); + container->setObjectName("GrayWidget"); auto* mainLayout = new QVBoxLayout(); mainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); mainLayout->setContentsMargins(10, 10, 10, 10); From 9f50e41d4c7634e4aef2c3e6a8022611efac9079 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 23:11:20 +0100 Subject: [PATCH 34/90] On macOS I had a dangling iterator bug with EpwGroundTemperatureDepth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In GroundTemperatureView.cpp:290, the lambda captures it (an iterator into ground_temps), but ground_temps is a local variable that goes out of scope when setType returns. By the time the button is clicked, it is a dangling iterator — classic UB that happens to be harmless on Linux but corrupts on macOS. The fix: capture the EpwGroundTemperatureDepth value instead of the iterator. --- src/openstudio_lib/GroundTemperatureView.cpp | 80 ++++++++++---------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index f22a8c496..4ce5d28ba 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -279,60 +279,64 @@ void GroundTemperatureNotPresentView::setType(GroundTempType type, model::Model return; } + // Capture the EpwGroundTemperatureDepth by value — 'it' is an iterator into a local vector + // that goes out of scope when setType() returns, so dereferencing it later is UB. + EpwGroundTemperatureDepth gtd = *it; + m_epwInfoLabel->setText(tr("The weather file contains ground temperature data at a depth of " "%1 m, " "so you can choose to import those values or add the object with default values.") - .arg(QString::number(it->groundTemperatureDepth(), 'f', 1))); + .arg(QString::number(gtd.groundTemperatureDepth(), 'f', 1))); m_epwInfoLabel->show(); m_importFromEPWButton->setEnabled(true); // Connect button to a lambda that creates the object directly and fills up the value - connect(m_importFromEPWButton, &QPushButton::clicked, this, [this, type, it]() { + connect(m_importFromEPWButton, &QPushButton::clicked, this, [this, type, gtd]() { if (type == GroundTempType::Shallow) { auto ts = m_model.getUniqueModelObject(); - ts.setJanuarySurfaceGroundTemperature(it->janGroundTemperature()); - ts.setFebruarySurfaceGroundTemperature(it->febGroundTemperature()); - ts.setMarchSurfaceGroundTemperature(it->marGroundTemperature()); - ts.setAprilSurfaceGroundTemperature(it->aprGroundTemperature()); - ts.setMaySurfaceGroundTemperature(it->mayGroundTemperature()); - ts.setJuneSurfaceGroundTemperature(it->junGroundTemperature()); - ts.setJulySurfaceGroundTemperature(it->julGroundTemperature()); - ts.setAugustSurfaceGroundTemperature(it->augGroundTemperature()); - ts.setSeptemberSurfaceGroundTemperature(it->sepGroundTemperature()); - ts.setOctoberSurfaceGroundTemperature(it->octGroundTemperature()); - ts.setNovemberSurfaceGroundTemperature(it->novGroundTemperature()); - ts.setDecemberSurfaceGroundTemperature(it->decGroundTemperature()); + ts.setJanuarySurfaceGroundTemperature(gtd.janGroundTemperature()); + ts.setFebruarySurfaceGroundTemperature(gtd.febGroundTemperature()); + ts.setMarchSurfaceGroundTemperature(gtd.marGroundTemperature()); + ts.setAprilSurfaceGroundTemperature(gtd.aprGroundTemperature()); + ts.setMaySurfaceGroundTemperature(gtd.mayGroundTemperature()); + ts.setJuneSurfaceGroundTemperature(gtd.junGroundTemperature()); + ts.setJulySurfaceGroundTemperature(gtd.julGroundTemperature()); + ts.setAugustSurfaceGroundTemperature(gtd.augGroundTemperature()); + ts.setSeptemberSurfaceGroundTemperature(gtd.sepGroundTemperature()); + ts.setOctoberSurfaceGroundTemperature(gtd.octGroundTemperature()); + ts.setNovemberSurfaceGroundTemperature(gtd.novGroundTemperature()); + ts.setDecemberSurfaceGroundTemperature(gtd.decGroundTemperature()); } else if (type == GroundTempType::FCfactorMethod) { auto tf = m_model.getUniqueModelObject(); - tf.setJanuaryGroundTemperature(it->janGroundTemperature()); - tf.setFebruaryGroundTemperature(it->febGroundTemperature()); - tf.setMarchGroundTemperature(it->marGroundTemperature()); - tf.setAprilGroundTemperature(it->aprGroundTemperature()); - tf.setMayGroundTemperature(it->mayGroundTemperature()); - tf.setJuneGroundTemperature(it->junGroundTemperature()); - tf.setJulyGroundTemperature(it->julGroundTemperature()); - tf.setAugustGroundTemperature(it->augGroundTemperature()); - tf.setSeptemberGroundTemperature(it->sepGroundTemperature()); - tf.setOctoberGroundTemperature(it->octGroundTemperature()); - tf.setNovemberGroundTemperature(it->novGroundTemperature()); - tf.setDecemberGroundTemperature(it->decGroundTemperature()); + tf.setJanuaryGroundTemperature(gtd.janGroundTemperature()); + tf.setFebruaryGroundTemperature(gtd.febGroundTemperature()); + tf.setMarchGroundTemperature(gtd.marGroundTemperature()); + tf.setAprilGroundTemperature(gtd.aprGroundTemperature()); + tf.setMayGroundTemperature(gtd.mayGroundTemperature()); + tf.setJuneGroundTemperature(gtd.junGroundTemperature()); + tf.setJulyGroundTemperature(gtd.julGroundTemperature()); + tf.setAugustGroundTemperature(gtd.augGroundTemperature()); + tf.setSeptemberGroundTemperature(gtd.sepGroundTemperature()); + tf.setOctoberGroundTemperature(gtd.octGroundTemperature()); + tf.setNovemberGroundTemperature(gtd.novGroundTemperature()); + tf.setDecemberGroundTemperature(gtd.decGroundTemperature()); } else { // GroundTempType::Deep auto td = m_model.getUniqueModelObject(); - td.setJanuaryDeepGroundTemperature(it->janGroundTemperature()); - td.setFebruaryDeepGroundTemperature(it->febGroundTemperature()); - td.setMarchDeepGroundTemperature(it->marGroundTemperature()); - td.setAprilDeepGroundTemperature(it->aprGroundTemperature()); - td.setMayDeepGroundTemperature(it->mayGroundTemperature()); - td.setJuneDeepGroundTemperature(it->junGroundTemperature()); - td.setJulyDeepGroundTemperature(it->julGroundTemperature()); - td.setAugustDeepGroundTemperature(it->augGroundTemperature()); - td.setSeptemberDeepGroundTemperature(it->sepGroundTemperature()); - td.setOctoberDeepGroundTemperature(it->octGroundTemperature()); - td.setNovemberDeepGroundTemperature(it->novGroundTemperature()); - td.setDecemberDeepGroundTemperature(it->decGroundTemperature()); + td.setJanuaryDeepGroundTemperature(gtd.janGroundTemperature()); + td.setFebruaryDeepGroundTemperature(gtd.febGroundTemperature()); + td.setMarchDeepGroundTemperature(gtd.marGroundTemperature()); + td.setAprilDeepGroundTemperature(gtd.aprGroundTemperature()); + td.setMayDeepGroundTemperature(gtd.mayGroundTemperature()); + td.setJuneDeepGroundTemperature(gtd.junGroundTemperature()); + td.setJulyDeepGroundTemperature(gtd.julGroundTemperature()); + td.setAugustDeepGroundTemperature(gtd.augGroundTemperature()); + td.setSeptemberDeepGroundTemperature(gtd.sepGroundTemperature()); + td.setOctoberDeepGroundTemperature(gtd.octGroundTemperature()); + td.setNovemberDeepGroundTemperature(gtd.novGroundTemperature()); + td.setDecemberDeepGroundTemperature(gtd.decGroundTemperature()); } emit addClicked(type); }); From b40bdde25cfdefec18855b5a7607323d855deb76 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 23:19:34 +0100 Subject: [PATCH 35/90] Remove chart junk (gridlines not needed) --- src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index 6c037dd7f..fb602ec33 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -125,7 +125,7 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP if (isIP) { m_constantValueEdit->setRange(-148.0, 212.0); m_constantValueEdit->setSuffix(tr(" °F")); - m_constantValueEdit->setValue(val * 9.0 / 5.0 + 32.0); + m_constantValueEdit->setValue((val * 9.0 / 5.0) + 32.0); } else { m_constantValueEdit->setRange(-100.0, 100.0); m_constantValueEdit->setSuffix(tr(" °C")); @@ -153,9 +153,11 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP const QStringList monthAbbrevs = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; auto* axisX = new QBarCategoryAxis; axisX->append(monthAbbrevs); + axisX->setGridLineVisible(false); m_chartYAxis = new QValueAxis; m_chartYAxis->setTitleText(isIP ? tr("Temperature [°F]") : tr("Temperature [°C]")); + m_chartYAxis->setGridLineVisible(false); // Zero line — uses a hidden numeric x axis so it can span the bar range auto* hiddenXAxis = new QValueAxis; From 7905935f1a54c7c0eaa48b39fa28c37a8b9335b8 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Thu, 5 Mar 2026 23:19:53 +0100 Subject: [PATCH 36/90] Remove touch-event related warnings in console on the animated QtChart --- src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp index fb602ec33..3899dca30 100644 --- a/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp +++ b/src/openstudio_lib/GroundTemperatureMonthlyInspectorView.cpp @@ -199,6 +199,8 @@ SiteGroundTemperatureMonthlyWidget::SiteGroundTemperatureMonthlyWidget(bool isIP auto* chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); + // Suppress spurious "no target window" touch-event warnings on macOS (Qt 6 / QChartView bug) + chartView->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, false); chartView->setMinimumHeight(220); chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mainLayout->addWidget(chartView, 1); From eea7ad39648e7b9bf33c3d0fded19565df67ad92 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Fri, 6 Mar 2026 10:50:59 +0100 Subject: [PATCH 37/90] Add ability, in GeometryPreview, to flip a PlanarSurface's vertices --- src/openstudio_lib/GeometryPreviewView.cpp | 25 ++++++++ src/openstudio_lib/GeometryPreviewView.hpp | 19 ++++++ .../library/geometry_preview.html | 62 +++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/src/openstudio_lib/GeometryPreviewView.cpp b/src/openstudio_lib/GeometryPreviewView.cpp index 97771a1cf..68c779cb9 100644 --- a/src/openstudio_lib/GeometryPreviewView.cpp +++ b/src/openstudio_lib/GeometryPreviewView.cpp @@ -11,8 +11,12 @@ #include "../model_editor/Application.hpp" #include +#include +#include #include +#include + #include #include @@ -22,6 +26,7 @@ #include #include #include +#include #include #include @@ -29,6 +34,17 @@ using namespace std::placeholders; namespace openstudio { +GeometryBridge::GeometryBridge(model::Model& model, QObject* parent) : QObject(parent), m_model(model) {} + +void GeometryBridge::reverseSurfaceVertices(const QString& surfaceName) { + if (auto surface = m_model.getModelObjectByName(surfaceName.toStdString())) { + auto vertices = surface->vertices(); + std::reverse(vertices.begin(), vertices.end()); + surface->setVertices(vertices); + emit modelChanged(); + } +} + GeometryPreviewView::GeometryPreviewView(bool isIP, const openstudio::model::Model& model, QWidget* parent) : QWidget(parent) { // TODO: DLM implement units switching //connect(this, &GeometryPreviewView::toggleUnitsClicked, modelObjectInspectorView(), &ModelObjectInspectorView::toggleUnitsClicked); @@ -77,6 +93,15 @@ PreviewWebView::PreviewWebView(bool isIP, const model::Model& model, QWidget* t_ m_page = new OSWebEnginePage(m_view); m_view->setPage(m_page); // note, view does not take ownership of page + auto* channel = new QWebChannel(m_page); + m_bridge = new GeometryBridge(m_model, this); + channel->registerObject(QStringLiteral("bridge"), m_bridge); + m_page->setWebChannel(channel); + connect(m_bridge, &GeometryBridge::modelChanged, this, [this]() { + m_json = QString(); + refreshClicked(); + }); + auto* mainWindow = OSAppBase::instance()->currentDocument()->mainWindow(); const bool verboseOutput = mainWindow->geometryDiagnostics(); m_geometryDiagnosticsBox = new QCheckBox(); diff --git a/src/openstudio_lib/GeometryPreviewView.hpp b/src/openstudio_lib/GeometryPreviewView.hpp index 2b7eaeff8..44babcea6 100644 --- a/src/openstudio_lib/GeometryPreviewView.hpp +++ b/src/openstudio_lib/GeometryPreviewView.hpp @@ -14,6 +14,7 @@ #include "../shared_gui_components/ProgressBarWithError.hpp" +#include #include #include @@ -25,6 +26,23 @@ namespace openstudio { class OSDocument; +class GeometryBridge : public QObject +{ + Q_OBJECT + + public: + explicit GeometryBridge(model::Model& model, QObject* parent = nullptr); + + public slots: + Q_INVOKABLE void reverseSurfaceVertices(const QString& surfaceName); + + signals: + void modelChanged(); + + private: + model::Model& m_model; +}; + class GeometryPreviewView : public QWidget { Q_OBJECT @@ -75,6 +93,7 @@ class PreviewWebView : public QWidget QWebEngineView* m_view; OSWebEnginePage* m_page; std::shared_ptr m_document; + GeometryBridge* m_bridge; QString m_json; }; diff --git a/src/openstudio_lib/library/geometry_preview.html b/src/openstudio_lib/library/geometry_preview.html index e5ace398c..0c6eb5fda 100644 --- a/src/openstudio_lib/library/geometry_preview.html +++ b/src/openstudio_lib/library/geometry_preview.html @@ -13,6 +13,8 @@ + +