Repository: Karpman-Consulting/eQUEST-229RPDGenerator
Pull request #229: Added baseline/proposed model toggle functionality
Author: login: bradcline-kc
Created: 2025-03-04T11:50:28Z | Merged: 2025-03-11T22:00:51Z



Diff:
diff --git a/interface/CTkMessagebox/__init__.py b/interface/CTkMessagebox/__init__.py
new file mode 100644
index 00000000..b1c711a3
--- /dev/null
+++ b/interface/CTkMessagebox/__init__.py
@@ -0,0 +1,10 @@
+"""
+CustomTkinter Messagebox
+Author: Akash Bora
+This is a modern messagebox made with customtkinter.
+Homepage: https://github.com/Akascape/CTkMessagebox
+"""
+
+__version__ = "2.7"
+
+from .ctkmessagebox import CTkMessagebox
diff --git a/interface/CTkMessagebox/ctkmessagebox.py b/interface/CTkMessagebox/ctkmessagebox.py
new file mode 100644
index 00000000..a495b7ca
--- /dev/null
+++ b/interface/CTkMessagebox/ctkmessagebox.py
@@ -0,0 +1,700 @@
+"""
+CustomTkinter Messagebox
+Author: Akash Bora
+Version: 2.7
+"""
+
+import customtkinter
+from PIL import Image, ImageTk
+import os
+import sys
+import time
+from typing import Literal
+
+
+class CTkMessagebox(customtkinter.CTkToplevel):
+    ICONS = {
+        "check": None,
+        "cancel": None,
+        "info": None,
+        "question": None,
+        "warning": None,
+    }
+    ICON_BITMAP = {}
+
+    def __init__(
+        self,
+        master: any = None,
+        width: int = 400,
+        height: int = 200,
+        title: str = "CTkMessagebox",
+        message: str = "This is a CTkMessagebox!",
+        option_1: str = "OK",
+        option_2: str = None,
+        option_3: str = None,
+        options: list = [],
+        border_width: int = 1,
+        border_color: str = "default",
+        button_color: str = "default",
+        bg_color: str = "default",
+        fg_color: str = "default",
+        text_color: str = "default",
+        title_color: str = "default",
+        button_text_color: str = "default",
+        button_width: int = None,
+        button_height: int = None,
+        cancel_button_color: str = None,
+        cancel_button: str = None,  # types: circle, cross or none
+        button_hover_color: str = "default",
+        icon: str = "info",
+        icon_size: tuple = None,
+        corner_radius: int = 15,
+        justify: str = "right",
+        font: tuple = None,
+        header: bool = False,
+        topmost: bool = True,
+        fade_in_duration: int = 0,
+        sound: bool = False,
+        wraplength: int = 0,
+        option_focus: Literal[1, 2, 3] = None,
+    ):
+
+        super().__init__()
+
+        self.master_window = master
+
+        self.width = 250 if width < 250 else width
+        self.height = 150 if height < 150 else height
+
+        if self.master_window is None:
+            self.spawn_x = int((self.winfo_screenwidth() - self.width) / 2)
+            self.spawn_y = int((self.winfo_screenheight() - self.height) / 2)
+        else:
+            self.spawn_x = int(
+                self.master_window.winfo_width() * 0.5
+                + self.master_window.winfo_x()
+                - 0.5 * self.width
+                + 7
+            )
+            self.spawn_y = int(
+                self.master_window.winfo_height() * 0.5
+                + self.master_window.winfo_y()
+                - 0.5 * self.height
+                + 20
+            )
+
+        self.after(10)
+        self.geometry(f"{self.width}x{self.height}+{self.spawn_x}+{self.spawn_y}")
+        self.title(title)
+        self.resizable(width=False, height=False)
+        self.fade = fade_in_duration
+        self.oldx = 0
+        self.oldy = 0
+
+        if self.fade:
+            self.fade = 20 if self.fade < 20 else self.fade
+            self.attributes("-alpha", 0)
+
+        if not header:
+            self.overrideredirect(1)
+
+        if topmost:
+            self.attributes("-topmost", True)
+        else:
+            self.transient(self.master_window)
+
+        if sys.platform.startswith("win"):
+            self.transparent_color = self._apply_appearance_mode(self.cget("fg_color"))
+            self.attributes("-transparentcolor", self.transparent_color)
+            default_cancel_button = "cross"
+        elif sys.platform.startswith("darwin"):
+            self.transparent_color = "systemTransparent"
+            self.attributes("-transparent", True)
+            default_cancel_button = "circle"
+        else:
+            self.transparent_color = "#000001"
+            corner_radius = 0
+            default_cancel_button = "cross"
+
+        self.lift()
+
+        self.config(background=self.transparent_color)
+        self.protocol("WM_DELETE_WINDOW", self.button_event)
+        self.grid_columnconfigure(0, weight=1)
+        self.grid_rowconfigure(0, weight=1)
+        self.x = self.winfo_x()
+        self.y = self.winfo_y()
+        self._title = title
+        self.message = message
+        self.font = font
+        self.justify = justify
+        self.sound = sound
+        self.cancel_button = cancel_button if cancel_button else default_cancel_button
+        self.round_corners = corner_radius if corner_radius <= 30 else 30
+        self.button_width = button_width if button_width else self.width / 4
+        self.button_height = button_height if button_height else 28
+
+        if self.fade:
+            self.attributes("-alpha", 0)
+
+        if self.button_height > self.height / 4:
+            self.button_height = self.height / 4 - 20
+        self.dot_color = cancel_button_color
+
+        self.border_width = border_width if border_width < 6 else 5
+
+        if type(options) is list and len(options) > 0:
+            try:
+                option_1 = options[-1]
+                option_2 = options[-2]
+                option_3 = options[-3]
+            except IndexError:
+                None
+
+        if bg_color == "default":
+            self.bg_color = self._apply_appearance_mode(
+                customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"]
+            )
+        else:
+            self.bg_color = bg_color
+
+        if self.dot_color == "transparent":
+            self.dot_color = self.bg_color
+
+        if fg_color == "default":
+            self.fg_color = self._apply_appearance_mode(
+                customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"]
+            )
+        else:
+            self.fg_color = fg_color
+
+        default_button_color = self._apply_appearance_mode(
+            customtkinter.ThemeManager.theme["CTkButton"]["fg_color"]
+        )
+
+        if sys.platform.startswith("win"):
+            if (
+                self.bg_color == self.transparent_color
+                or self.fg_color == self.transparent_color
+            ):
+                self.configure(fg_color="#000001")
+                self.transparent_color = "#000001"
+                self.attributes("-transparentcolor", self.transparent_color)
+
+        if button_color == "default":
+            self.button_color = (
+                default_button_color,
+                default_button_color,
+                default_button_color,
+            )
+        else:
+            if type(button_color) is tuple:
+                if len(button_color) == 2:
+                    self.button_color = (
+                        button_color[0],
+                        button_color[1],
+                        default_button_color,
+                    )
+                elif len(button_color) == 1:
+                    self.button_color = (
+                        button_color[0],
+                        default_button_color,
+                        default_button_color,
+                    )
+                else:
+                    self.button_color = button_color
+            else:
+                self.button_color = (button_color, button_color, button_color)
+
+        if text_color == "default":
+            self.text_color = self._apply_appearance_mode(
+                customtkinter.ThemeManager.theme["CTkLabel"]["text_color"]
+            )
+        else:
+            self.text_color = text_color
+
+        if title_color == "default":
+            self.title_color = self._apply_appearance_mode(
+                customtkinter.ThemeManager.theme["CTkLabel"]["text_color"]
+            )
+        else:
+            self.title_color = title_color
+
+        if button_text_color == "default":
+            self.bt_text_color = self._apply_appearance_mode(
+                customtkinter.ThemeManager.theme["CTkButton"]["text_color"]
+            )
+        else:
+            self.bt_text_color = button_text_color
+
+        if button_hover_color == "default":
+            self.bt_hv_color = self._apply_appearance_mode(
+                customtkinter.ThemeManager.theme["CTkButton"]["hover_color"]
+            )
+        else:
+            self.bt_hv_color = button_hover_color
+
+        if border_color == "default":
+            self.border_color = self._apply_appearance_mode(
+                customtkinter.ThemeManager.theme["CTkFrame"]["border_color"]
+            )
+        else:
+            self.border_color = border_color
+
+        if icon_size:
+            self.size_height = (
+                icon_size[1] if icon_size[1] <= self.height - 100 else self.height - 100
+            )
+            self.size = (icon_size[0], self.size_height)
+        else:
+            self.size = (self.height / 4, self.height / 4)
+
+        self.icon = self.load_icon(icon, icon_size) if icon else None
+
+        self.frame_top = customtkinter.CTkFrame(
+            self,
+            corner_radius=self.round_corners,
+            width=self.width,
+            border_width=self.border_width,
+            bg_color=self.transparent_color,
+            fg_color=self.bg_color,
+            border_color=self.border_color,
+        )
+        self.frame_top.grid(sticky="nswe")
+
+        if button_width:
+            self.frame_top.grid_columnconfigure(0, weight=1)
+        else:
+            self.frame_top.grid_columnconfigure((1, 2, 3), weight=1)
+
+        if button_height:
+            self.frame_top.grid_rowconfigure((0, 1, 3), weight=1)
+        else:
+            self.frame_top.grid_rowconfigure((0, 1, 2), weight=1)
+
+        self.frame_top.bind("<B1-Motion>", self.move_window)
+        self.frame_top.bind("<ButtonPress-1>", self.oldxyset)
+
+        if self.cancel_button == "cross":
+            self.button_close = customtkinter.CTkButton(
+                self.frame_top,
+                corner_radius=10,
+                width=0,
+                height=0,
+                hover=False,
+                border_width=0,
+                text_color=self.dot_color if self.dot_color else self.title_color,
+                text="✕",
+                fg_color="transparent",
+                command=self.button_event,
+            )
+            self.button_close.grid(
+                row=0,
+                column=5,
+                sticky="ne",
+                padx=5 + self.border_width,
+                pady=5 + self.border_width,
+            )
+        elif self.cancel_button == "circle":
+            self.button_close = customtkinter.CTkButton(
+                self.frame_top,
+                corner_radius=10,
+                width=10,
+                height=10,
+                hover=False,
+                border_width=0,
+                text="",
+                fg_color=self.dot_color if self.dot_color else "#c42b1c",
+                command=self.button_event,
+            )
+            self.button_close.grid(row=0, column=5, sticky="ne", padx=10, pady=10)
+
+        self.title_label = customtkinter.CTkLabel(
+            self.frame_top,
+            width=1,
+            text=self._title,
+            text_color=self.title_color,
+            font=self.font,
+        )
+        self.title_label.grid(
+            row=0, column=0, columnspan=6, sticky="nw", padx=(15, 30), pady=5
+        )
+        self.title_label.bind("<B1-Motion>", self.move_window)
+        self.title_label.bind("<ButtonPress-1>", self.oldxyset)
+
+        self.info = customtkinter.CTkButton(
+            self.frame_top,
+            width=1,
+            height=self.height / 2,
+            corner_radius=0,
+            text=self.message,
+            font=self.font,
+            fg_color=self.fg_color,
+            hover=False,
+            text_color=self.text_color,
+            image=self.icon,
+        )
+        self.info._text_label.configure(wraplength=self.width / 2, justify="left")
+        self.info.grid(
+            row=1, column=0, columnspan=6, sticky="nwes", padx=self.border_width
+        )
+
+        if wraplength > 0:
+            self.info._text_label.configure(wraplength=wraplength)
+
+        if self.info._text_label.winfo_reqheight() > self.height / 2:
+            height_offset = int(
+                (self.info._text_label.winfo_reqheight())
+                - (self.height / 2)
+                + self.height
+            )
+            self.geometry(f"{self.width}x{height_offset}")
+
+        self.option_text_1 = option_1
+
+        self.button_1 = customtkinter.CTkButton(
+            self.frame_top,
+            text=self.option_text_1,
+            fg_color=self.button_color[0],
+            width=self.button_width,
+            font=self.font,
+            text_color=self.bt_text_color,
+            hover_color=self.bt_hv_color,
+            height=self.button_height,
+            command=lambda: self.button_event(self.option_text_1),
+        )
+
+        self.option_text_2 = option_2
+        if option_2:
+            self.button_2 = customtkinter.CTkButton(
+                self.frame_top,
+                text=self.option_text_2,
+                fg_color=self.button_color[1],
+                width=self.button_width,
+                font=self.font,
+                text_color=self.bt_text_color,
+                hover_color=self.bt_hv_color,
+                height=self.button_height,
+                command=lambda: self.button_event(self.option_text_2),
+            )
+
+        self.option_text_3 = option_3
+        if option_3:
+            self.button_3 = customtkinter.CTkButton(
+                self.frame_top,
+                text=self.option_text_3,
+                fg_color=self.button_color[2],
+                width=self.button_width,
+                font=self.font,
+                text_color=self.bt_text_color,
+                hover_color=self.bt_hv_color,
+                height=self.button_height,
+                command=lambda: self.button_event(self.option_text_3),
+            )
+
+        if self.justify == "center":
+            if button_width:
+                columns = [4, 3, 2]
+                span = 1
+            else:
+                columns = [4, 2, 0]
+                span = 2
+            if option_3:
+                self.frame_top.columnconfigure((0, 1, 2, 3, 4, 5), weight=1)
+                self.button_1.grid(
+                    row=2,
+                    column=columns[0],
+                    columnspan=span,
+                    sticky="news",
+                    padx=(0, 10),
+                    pady=10,
+                )
+                self.button_2.grid(
+                    row=2,
+                    column=columns[1],
+                    columnspan=span,
+                    sticky="news",
+                    padx=10,
+                    pady=10,
+                )
+                self.button_3.grid(
+                    row=2,
+                    column=columns[2],
+                    columnspan=span,
+                    sticky="news",
+                    padx=(10, 0),
+                    pady=10,
+                )
+            elif option_2:
+                self.frame_top.columnconfigure((0, 5), weight=1)
+                columns = [2, 3]
+                self.button_1.grid(
+                    row=2, column=columns[0], sticky="news", padx=(0, 5), pady=10
+                )
+                self.button_2.grid(
+                    row=2, column=columns[1], sticky="news", padx=(5, 0), pady=10
+                )
+            else:
+                if button_width:
+                    self.frame_top.columnconfigure((0, 1, 2, 3, 4, 5), weight=1)
+                else:
+                    self.frame_top.columnconfigure((0, 2, 4), weight=2)
+                self.button_1.grid(
+                    row=2,
+                    column=columns[1],
+                    columnspan=span,
+                    sticky="news",
+                    padx=(0, 10),
+                    pady=10,
+                )
+        elif self.justify == "left":
+            self.frame_top.columnconfigure((0, 1, 2, 3, 4, 5), weight=1)
+            if button_width:
+                columns = [0, 1, 2]
+                span = 1
+            else:
+                columns = [0, 2, 4]
+                span = 2
+            if option_3:
+                self.button_1.grid(
+                    row=2,
+                    column=columns[2],
+                    columnspan=span,
+                    sticky="news",
+                    padx=(0, 10),
+                    pady=10,
+                )
+                self.button_2.grid(
+                    row=2,
+                    column=columns[1],
+                    columnspan=span,
+                    sticky="news",
+                    padx=10,
+                    pady=10,
+                )
+                self.button_3.grid(
+                    row=2,
+                    column=columns[0],
+                    columnspan=span,
+                    sticky="news",
+                    padx=(10, 0),
+                    pady=10,
+                )
+            elif option_2:
+                self.button_1.grid(
+                    row=2,
+                    column=columns[1],
+                    columnspan=span,
+                    sticky="news",
+                    padx=10,
+                    pady=10,
+                )
+                self.button_2.grid(
+                    row=2,
+                    column=columns[0],
+                    columnspan=span,
+                    sticky="news",
+                    padx=(10, 0),
+                    pady=10,
+                )
+            else:
+                self.button_1.grid(
+                    row=2,
+                    column=columns[0],
+                    columnspan=span,
+                    sticky="news",
+                    padx=(10, 0),
+                    pady=10,
+                )
+        else:
+            self.frame_top.columnconfigure((0, 1, 2, 3, 4, 5), weight=1)
+            if button_width:
+                columns = [5, 4, 3]
+                span = 1
+            else:
+                columns = [4, 2, 0]
+                span = 2
+            self.button_1.grid(
+                row=2,
+                column=columns[0],
+                columnspan=span,
+                sticky="news",
+                padx=(0, 10),
+                pady=10,
+            )
+            if option_2:
+                self.button_2.grid(
+                    row=2,
+                    column=columns[1],
+                    columnspan=span,
+                    sticky="news",
+                    padx=10,
+                    pady=10,
+                )
+            if option_3:
+                self.button_3.grid(
+                    row=2,
+                    column=columns[2],
+                    columnspan=span,
+                    sticky="news",
+                    padx=(10, 0),
+                    pady=10,
+                )
+
+        if header:
+            self.title_label.configure(text="")
+            self.title_label.grid_configure(pady=0)
+            self.button_close.configure(text_color=self.bg_color)
+            self.frame_top.configure(corner_radius=0)
+
+        if self.winfo_exists():
+            self.grab_set()
+
+        if self.sound:
+            self.bell()
+
+        if self.fade:
+            self.fade_in()
+
+        if option_focus:
+            self.option_focus = option_focus
+            self.focus_button(self.option_focus)
+        else:
+            if not self.option_text_2 and not self.option_text_3:
+                self.button_1.focus()
+                self.button_1.bind(
+                    "<Return>", lambda event: self.button_event(self.option_text_1)
+                )
+
+        self.bind("<Escape>", lambda e: self.button_event())
+
+    def place_widget(self, widget, x=10, y=10, **args):
+        if "master" in args:
+            del args["master"]
+
+        new_widget = widget(master=self.frame_top, **args)
+        new_widget.place(x=x, y=y)
+        return new_widget
+
+    def focus_button(self, option_focus):
+        try:
+            self.selected_button = getattr(self, "button_" + str(option_focus))
+            self.selected_button.focus()
+            self.selected_button.configure(
+                border_color=self.bt_hv_color, border_width=3
+            )
+            self.selected_option = getattr(self, "option_text_" + str(option_focus))
+            self.selected_button.bind(
+                "<Return>", lambda event: self.button_event(self.selected_option)
+            )
+        except AttributeError:
+            return
+
+        self.bind("<Left>", lambda e: self.change_left())
+        self.bind("<Right>", lambda e: self.change_right())
+
+    def change_left(self):
+        if self.option_focus == 3:
+            return
+
+        self.selected_button.unbind("<Return>")
+        self.selected_button.configure(border_width=0)
+
+        if self.option_focus == 1:
+            if self.option_text_2:
+                self.option_focus = 2
+
+        elif self.option_focus == 2:
+            if self.option_text_3:
+                self.option_focus = 3
+
+        self.focus_button(self.option_focus)
+
+    def change_right(self):
+        if self.option_focus == 1:
+            return
+
+        self.selected_button.unbind("<Return>")
+        self.selected_button.configure(border_width=0)
+
+        if self.option_focus == 2:
+            self.option_focus = 1
+
+        elif self.option_focus == 3:
+            self.option_focus = 2
+
+        self.focus_button(self.option_focus)
+
+    def load_icon(self, icon, icon_size):
+        if icon not in self.ICONS or self.ICONS[icon] is None:
+            if icon in ["check", "cancel", "info", "question", "warning"]:
+                image_path = os.path.join(
+                    os.path.dirname(os.path.realpath(__file__)), "icons", icon + ".png"
+                )
+            else:
+                image_path = icon
+            if icon_size:
+                size_height = (
+                    icon_size[1]
+                    if icon_size[1] <= self.height - 100
+                    else self.height - 100
+                )
+                size = (icon_size[0], size_height)
+            else:
+                size = (self.height / 4, self.height / 4)
+            self.ICONS[icon] = customtkinter.CTkImage(Image.open(image_path), size=size)
+            self.ICON_BITMAP[icon] = ImageTk.PhotoImage(file=image_path)
+        if not sys.platform.startswith("darwin"):
+            self.after(200, lambda: self.iconphoto(False, self.ICON_BITMAP[icon]))
+        return self.ICONS[icon]
+
+    def fade_in(self):
+        for i in range(0, 110, 10):
+            if not self.winfo_exists():
+                break
+            self.attributes("-alpha", i / 100)
+            self.update()
+            time.sleep(1 / self.fade)
+
+    def fade_out(self):
+        for i in range(100, 0, -10):
+            if not self.winfo_exists():
+                break
+            self.attributes("-alpha", i / 100)
+            self.update()
+            time.sleep(1 / self.fade)
+
+    def get(self):
+        if self.winfo_exists():
+            self.master.wait_window(self)
+        return self.event
+
+    def oldxyset(self, event):
+        self.oldx = event.x
+        self.oldy = event.y
+
+    def move_window(self, event):
+        self.y = event.y_root - self.oldy
+        self.x = event.x_root - self.oldx
+        self.geometry(f"+{self.x}+{self.y}")
+
+    def button_event(self, event=None):
+        try:
+            self.button_1.configure(state="disabled")
+            self.button_2.configure(state="disabled")
+            self.button_3.configure(state="disabled")
+        except AttributeError:
+            pass
+
+        if self.fade:
+            self.fade_out()
+        self.grab_release()
+        self.destroy()
+        self.event = event
+        if self.master_window:
+            self.master_window.focus_force()
+
+
+if __name__ == "__main__":
+    app = CTkMessagebox()
+    app.mainloop()
diff --git a/interface/CTkMessagebox/icons/check.png b/interface/CTkMessagebox/icons/check.png
new file mode 100644
index 00000000..dc0892a2
Binary files /dev/null and b/interface/CTkMessagebox/icons/check.png differ
diff --git a/interface/CTkMessagebox/icons/info.png b/interface/CTkMessagebox/icons/info.png
new file mode 100644
index 00000000..4b382883
Binary files /dev/null and b/interface/CTkMessagebox/icons/info.png differ
diff --git a/interface/CTkMessagebox/icons/question.png b/interface/CTkMessagebox/icons/question.png
new file mode 100644
index 00000000..24f77c3e
Binary files /dev/null and b/interface/CTkMessagebox/icons/question.png differ
diff --git a/interface/CTkMessagebox/icons/warning.png b/interface/CTkMessagebox/icons/warning.png
new file mode 100644
index 00000000..bc6aea67
Binary files /dev/null and b/interface/CTkMessagebox/icons/warning.png differ
diff --git a/interface/compliance_parameter_window.py b/interface/compliance_parameter_window.py
index 14d3510b..51e1c88f 100644
--- a/interface/compliance_parameter_window.py
+++ b/interface/compliance_parameter_window.py
@@ -5,7 +5,6 @@
 from interface.views.spaces import SpacesView
 from interface.views.test import TestView
 from interface.views.project_info import ProjectInfoView
-from interface.views.buildings import BuildingsView
 from interface.views.building_areas import BuildingAreasView
 from interface.views.zones import ZonesView
 from interface.views.surfaces import SurfacesView
@@ -45,10 +44,26 @@ def __init__(self, main_app, test_mode=False):
         # for i in range(self.grid_size()[0]):
         #     self.grid_columnconfigure(i, weight=1)
 
+        # Setup the baseline/proposed toggle
+        self.baseline_label = ctk.CTkLabel(self, text="Baseline")
+        self.baseline_proposed_switch = ctk.CTkSwitch(
+            self,
+            variable=self.main_app.data.baseline_or_proposed,
+            text="",
+            height=20,
+            width=50,
+            switch_height=20,
+            switch_width=50,
+            command=self.toggle_baseline_proposed,
+            onvalue="Proposed",
+            offvalue="Baseline",
+        )
+        self.baseline_proposed_switch.deselect()
+        self.proposed_label = ctk.CTkLabel(self, text="Proposed")
+
         self.views = {
             "Test": TestView(self),
             "Project Info": ProjectInfoView(self),
-            "Buildings": BuildingsView(self),
             "Building Areas": BuildingAreasView(self),
             "Zones": ZonesView(self),
             "Spaces": SpacesView(self),
@@ -113,6 +128,16 @@ def __init__(self, main_app, test_mode=False):
         else:
             self.show_view("Project Info")
 
+    def toggle_baseline_proposed(self):
+        new_state = self.main_app.data.baseline_or_proposed.get()
+        old_state = "Baseline" if new_state == "Proposed" else "Proposed"
+
+        if old_state in self.current_view.current_subview_name:
+            self.current_view.current_subview_name = (
+                self.current_view.current_subview_name.replace(old_state, new_state)
+            )
+        self.current_view.show_subview(self.current_view.current_subview_name)
+
     def create_menu_bar(self):
         menubar = Menu(self)
         file_menu = Menu(menubar, tearoff=0)
@@ -212,8 +237,21 @@ def create_nav_bar(self):
         self.warnings_button.grid(row=2, column=0, pady=5)
         self.errors_button.grid(row=2, column=1, pady=5)
         self.continue_button.grid(row=2, column=3, columnspan=3, pady=5)
+        self.baseline_label.grid(row=2, column=5, pady=5, sticky="e")
+        self.baseline_proposed_switch.grid(row=2, column=6, pady=5)
+        self.proposed_label.grid(row=2, column=7, pady=5, sticky="w")
         self.generate_RPD_button.grid(row=2, column=8, pady=5)
 
+    def show_baseline_proposed_toggle(self, show_toggle):
+        if show_toggle:
+            self.baseline_label.grid()
+            self.baseline_proposed_switch.grid()
+            self.proposed_label.grid()
+        else:
+            self.baseline_label.grid_remove()
+            self.baseline_proposed_switch.grid_remove()
+            self.proposed_label.grid_remove()
+
     def show_view(self, view_name):
         # Clear previous view
         if self.current_view is not None:
diff --git a/interface/main_app_data.py b/interface/main_app_data.py
index e6ce9107..c2be03c0 100644
--- a/interface/main_app_data.py
+++ b/interface/main_app_data.py
@@ -43,6 +43,7 @@ def __init__(self):
         self.selected_ruleset.set("ASHRAE 90.1-2019")
         self.has_rotation_exception = ctk.BooleanVar()
         self.is_all_new_construction = ctk.BooleanVar()
+        self.baseline_or_proposed = ctk.StringVar()
         self.ruleset_model_file_paths = {}
         self.output_directory = ctk.StringVar()
         self.climate_zone = ctk.StringVar()
@@ -178,6 +179,7 @@ def insert_to_rpd(self, mapping, obj_u_name: str = None):
                 obj = rmd.get_obj(obj_u_name)
                 if not obj:
                     print(f"Object {obj_u_name} not found in {rmd.type} RMD")
+                    continue
                 obj.lighting_space_type = enumerations_map.get(
                     self.lighting_space_type_vars[obj_u_name].get()
                 )
@@ -202,6 +204,12 @@ def insert_to_rpd(self, mapping, obj_u_name: str = None):
                     enumerations_map.get(self.cooling_design_day.get()),
                 )
 
+    def get_rmd(self, rmd_type):
+        for rmd in self.rmds:
+            if rmd.type == rmd_type:
+                return rmd
+        return None
+
     @staticmethod
     def validate_int_entry(entry):
         if str.isdigit(entry) or entry == "":
diff --git a/interface/views/building_areas.py b/interface/views/building_areas.py
index ae681cc7..7999de99 100644
--- a/interface/views/building_areas.py
+++ b/interface/views/building_areas.py
@@ -6,6 +6,7 @@
 class BuildingAreasView(BaseView):
     def __init__(self, window):
         super().__init__(window)
+        self.main_window = window
 
     def __repr__(self):
         return "BuildingAreasView"
@@ -13,3 +14,4 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Building Areas")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(False)
diff --git a/interface/views/buildings.py b/interface/views/buildings.py
index faeb0791..470641ea 100644
--- a/interface/views/buildings.py
+++ b/interface/views/buildings.py
@@ -6,6 +6,7 @@
 class BuildingsView(BaseView):
     def __init__(self, window):
         super().__init__(window)
+        self.main_window = window
 
     def __repr__(self):
         return "BuildingsView"
@@ -13,3 +14,4 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Buildings")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(False)
diff --git a/interface/views/ext_lighting.py b/interface/views/ext_lighting.py
index f379de86..6d05f7a4 100644
--- a/interface/views/ext_lighting.py
+++ b/interface/views/ext_lighting.py
@@ -6,6 +6,7 @@
 class ExteriorLightingView(BaseView):
     def __init__(self, window):
         super().__init__(window)
+        self.main_window = window
 
     def __repr__(self):
         return "ExteriorLightingView"
@@ -13,3 +14,4 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Ext. Lighting")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(False)
diff --git a/interface/views/miscellaneous.py b/interface/views/miscellaneous.py
index bf2f9437..6f028733 100644
--- a/interface/views/miscellaneous.py
+++ b/interface/views/miscellaneous.py
@@ -6,6 +6,7 @@
 class MiscellaneousView(BaseView):
     def __init__(self, window):
         super().__init__(window)
+        self.main_window = window
 
     def __repr__(self):
         return "MiscellaneousView"
@@ -13,3 +14,4 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Misc.")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(False)
diff --git a/interface/views/project_info.py b/interface/views/project_info.py
index 3a2fd723..40947dfb 100644
--- a/interface/views/project_info.py
+++ b/interface/views/project_info.py
@@ -22,6 +22,7 @@
 class ProjectInfoView(BaseView):
     def __init__(self, window):
         super().__init__(window)
+        self.main_window = window
 
         # All subviews will be placed inside this frame. Single row/column allows formatting of subview to be handled by the subview itself
         self.subview_frame = ctk.CTkFrame(self)
@@ -47,6 +48,8 @@ def open_view(self):
         self.window.continue_button.configure(command=self.view_continue)
         # Update the errors and warnings button formatting
         self.update_warnings_errors()
+        # Hide baseline/proposed toggle
+        self.main_window.show_baseline_proposed_toggle(False)
 
         self.toggle_active_button("Project Info")
         self.grid_propagate(False)
diff --git a/interface/views/results.py b/interface/views/results.py
index 7d923851..3165db68 100644
--- a/interface/views/results.py
+++ b/interface/views/results.py
@@ -6,6 +6,7 @@
 class ResultsView(BaseView):
     def __init__(self, window):
         super().__init__(window)
+        self.main_window = window
 
     def __repr__(self):
         return "ResultsView"
@@ -13,3 +14,4 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Results")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(False)
diff --git a/interface/views/spaces.py b/interface/views/spaces.py
index 9e223e8c..51f6cdca 100644
--- a/interface/views/spaces.py
+++ b/interface/views/spaces.py
@@ -1,8 +1,9 @@
 import customtkinter as ctk
 
+from interface.CTkMessagebox import CTkMessagebox
 from interface.ctk_xyframe import CTkXYFrame
 from interface.base_view import BaseView
-
+from interface.main_app_data import ASHRAE9012019ModelOptions
 
 LABEL_FONT = ("Arial", 14, "bold")
 READONLY = "readonly"
@@ -16,8 +17,10 @@
 class SpacesView(BaseView):
     def __init__(self, window):
         super().__init__(window)
-
+        self.main_window = window
         self.view_frame = ctk.CTkFrame(self)
+        self.current_subview = None
+        self.current_subview_name = None
 
         # Directions frame holds all directions info and will get 'gridded' within the surfaces view grid
         self.directions_frame = ctk.CTkFrame(self, fg_color="transparent")
@@ -32,7 +35,8 @@ def __init__(self, window):
             font=LABEL_FONT,
         )
         self.subviews = {
-            "Spaces": SpacesSubview(self.view_frame),
+            "Baseline SpacesSubview": SpacesSubview(self.view_frame),
+            "Proposed SpacesSubview": SpacesSubview(self.view_frame),
         }
 
     def __repr__(self):
@@ -41,6 +45,7 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Spaces")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(True)
 
         # 2 rows in the main surface view structure.
         # View frame (row 2, index 1) has a weight to make it fill up the empty space in the window
@@ -57,9 +62,37 @@ def open_view(self):
         self.view_frame.grid_rowconfigure(0, weight=1)
         self.view_frame.grid_columnconfigure(0, weight=1)
 
-        spaces_view = self.subviews["Spaces"]
-        spaces_view.grid(row=0, column=0, sticky=FILL)
-        spaces_view.open_view()
+        current_state = self.app_data.baseline_or_proposed.get()
+        self.current_subview_name = current_state + " SpacesSubview"
+        self.show_subview(current_state + " SpacesSubview")
+
+    def show_subview(self, subview_name):
+        # Clear previous subview
+        if self.current_subview is not None:
+            self.current_subview.grid_forget()
+
+        subview = self.subviews.get(subview_name)
+        if subview:
+            self.current_subview_name = subview_name
+            self.current_subview = subview
+        else:
+            current_state = self.app_data.baseline_or_proposed.get()
+            filtered_subviews = [
+                value for key, value in self.subviews.items() if current_state in key
+            ]
+
+            # Set current_subview to the first matching subview, if found
+            if filtered_subviews:
+                self.current_subview = filtered_subviews[0]
+                self.current_subview_name = (
+                    self.app_data.baseline_or_proposed.get()
+                    + " "
+                    + self.current_subview.__repr__()
+                )
+
+        self.current_subview.grid(row=0, column=0, sticky=FILL)
+        self.current_subview.focus_set()
+        self.current_subview.open_subview()
 
 
 class SpacesSubview(CTkXYFrame):
@@ -72,13 +105,24 @@ def __init__(self, view_frame):
     def __repr__(self):
         return "SpacesSubview"
 
-    def open_view(self):
+    def open_subview(self):
         self.populate_subview() if not self.is_view_populated else None
 
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, space_name in enumerate(self.app_data.rmds[0].space_map.keys()):
+        #  Get spaces from relevant rmd. Throw error if none found
+        space_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            space_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).space_map.keys()
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            space_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).space_map.keys()
+
+        for i, space_name in enumerate(space_names):
             # Add data vars for each space
             self.app_data.lighting_space_type_vars[space_name] = ctk.StringVar()
             # Add widget row for each space
@@ -89,7 +133,7 @@ def populate_subview(self):
     def add_column_headers(self):
         name_label = ctk.CTkLabel(self, text="Name", font=LABEL_FONT)
         name_label.grid(row=0, column=0, padx=PAD20END, pady=5)
-        if not self.app_data.is_all_new_construction:
+        if not self.app_data.is_all_new_construction.get():
             status_label = ctk.CTkLabel(self, text="Status", font=LABEL_FONT)
             status_label.grid(row=0, column=1, padx=PAD20END, pady=5)
         lighting_space_type_label = ctk.CTkLabel(
@@ -132,7 +176,7 @@ def add_column_headers(self):
     def add_row(self, i, space_name):
         name_label = ctk.CTkLabel(self, text=f"{space_name}")
         name_label.grid(row=(i + 1), column=0, padx=PAD20END, pady=PAD20END, sticky=W)
-        if not self.app_data.is_all_new_construction:
+        if not self.app_data.is_all_new_construction.get():
             status_combo = ctk.CTkComboBox(
                 self,
                 values=self.app_data.StatusDescriptions,
diff --git a/interface/views/surfaces.py b/interface/views/surfaces.py
index 92c0739b..ea6ed9a7 100644
--- a/interface/views/surfaces.py
+++ b/interface/views/surfaces.py
@@ -2,6 +2,7 @@
 
 from interface.ctk_xyframe import CTkXYFrame
 from interface.base_view import BaseView
+from interface.main_app_data import ASHRAE9012019ModelOptions
 
 
 LABEL_FONT = ("Arial", 14, "bold")
@@ -19,19 +20,43 @@
 class SurfacesView(BaseView):
     def __init__(self, window):
         super().__init__(window)
+        self.main_window = window
 
         # All subviews will be placed inside this frame.
         # Single row/column allows formatting of subview to be handled by the subview itself
         self.subview_frame = ctk.CTkFrame(self)
         self.current_subview = None
+        self.current_subview_name = None
 
         self.subviews = {
-            "Exterior": ExteriorSurfaceView(self.subview_frame),
-            "Interior": InteriorSurfaceView(self.subview_frame),
-            "Underground": UndergroundSurfaceView(self.subview_frame),
-            "Windows": WindowSurfaceView(self.subview_frame),
-            "Skylights": SkylightSurfaceView(self.subview_frame),
-            "Doors": DoorSurfaceView(self.subview_frame),
+            "Baseline ExteriorSurfaceSubview": ExteriorSurfaceSubview(
+                self.subview_frame
+            ),
+            "Proposed ExteriorSurfaceSubview": ExteriorSurfaceSubview(
+                self.subview_frame
+            ),
+            "Baseline InteriorSurfaceSubview": InteriorSurfaceSubview(
+                self.subview_frame
+            ),
+            "Proposed InteriorSurfaceSubview": InteriorSurfaceSubview(
+                self.subview_frame
+            ),
+            "Baseline UndergroundSurfaceSubview": UndergroundSurfaceSubview(
+                self.subview_frame
+            ),
+            "Proposed UndergroundSurfaceSubview": UndergroundSurfaceSubview(
+                self.subview_frame
+            ),
+            "Baseline WindowSurfaceSubview": WindowSurfaceSubview(self.subview_frame),
+            "Proposed WindowSurfaceSubview": WindowSurfaceSubview(self.subview_frame),
+            "Baseline SkylightSurfaceSubview": SkylightSurfaceSubview(
+                self.subview_frame
+            ),
+            "Proposed SkylightSurfaceSubview": SkylightSurfaceSubview(
+                self.subview_frame
+            ),
+            "Baseline DoorSurfaceSubview": DoorSurfaceSubview(self.subview_frame),
+            "Proposed DoorSurfaceSubview": DoorSurfaceSubview(self.subview_frame),
         }
         self.subview_buttons = {}
 
@@ -61,6 +86,7 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Surfaces")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(True)
 
         # 3 rows in the main surface view structure.
         # Subview frame (row 4, index 3) has a weight to make it fill up the empty space in the window
@@ -86,27 +112,41 @@ def open_view(self):
         self.subview_frame.grid_rowconfigure(0, weight=1)
         self.subview_frame.grid_columnconfigure(0, weight=1)
 
-        if self.subview_buttons:
-            # Open the first subview available
-            self.show_subview(next(iter(self.subview_buttons)))
+        self.current_subview_name = (
+            f"{self.app_data.baseline_or_proposed.get()} ExteriorSurfaceSubview"
+        )
+        self.show_subview(
+            f"{self.app_data.baseline_or_proposed.get()} ExteriorSurfaceSubview"
+        )
 
     def create_subbutton_bar(self):
         callback_methods = {}
-        if not self.app_data.is_all_new_construction:
+        current_state = self.app_data.baseline_or_proposed.get()
+        if not self.app_data.is_all_new_construction.get():
             if len(self.app_data.rmds[0].ext_wall_names) > 0:
-                callback_methods["Exterior"] = lambda: self.show_subview("Exterior")
+                callback_methods["Exterior"] = lambda: self.show_subview(
+                    f"{current_state} ExteriorSurfaceSubview"
+                )
             if len(self.app_data.rmds[0].int_wall_names) > 0:
-                callback_methods["Interior"] = lambda: self.show_subview("Interior")
+                callback_methods["Interior"] = lambda: self.show_subview(
+                    f"{current_state} InteriorSurfaceSubview"
+                )
             if len(self.app_data.rmds[0].undg_wall_names) > 0:
                 callback_methods["Underground"] = lambda: self.show_subview(
-                    "Underground"
+                    f"{current_state} UndergroundSurfaceSubview"
                 )
         if len(self.app_data.rmds[0].door_names) > 0:
-            callback_methods["Doors"] = lambda: self.show_subview("Doors")
+            callback_methods["Doors"] = lambda: self.show_subview(
+                f"{current_state} DoorSurfaceSubview"
+            )
         if len(self.app_data.rmds[0].window_names) > 0:
-            callback_methods["Windows"] = lambda: self.show_subview("Windows")
+            callback_methods["Windows"] = lambda: self.show_subview(
+                f"{current_state} WindowSurfaceSubview"
+            )
         if len(self.app_data.rmds[0].skylight_names) > 0:
-            callback_methods["Skylights"] = lambda: self.show_subview("Skylights")
+            callback_methods["Skylights"] = lambda: self.show_subview(
+                f"{current_state} SkylightSurfaceSubview"
+            )
 
         for index, name in enumerate(callback_methods):
             # Create the button to go inside this button frame
@@ -129,13 +169,28 @@ def show_subview(self, subview_name):
         if self.current_subview is not None:
             self.current_subview.grid_forget()
 
-        # Show new subview
         subview = self.subviews.get(subview_name)
         if subview:
+            self.current_subview_name = subview_name
             self.current_subview = subview
-            self.current_subview.grid(row=0, column=0, sticky=FILL)
-            self.current_subview.focus_set()
-            self.current_subview.open_subview()
+        else:
+            current_state = self.app_data.baseline_or_proposed.get()
+            filtered_subviews = [
+                value for key, value in self.subviews.items() if current_state in key
+            ]
+
+            # Set current_subview to the first matching subview, if found
+            if filtered_subviews:
+                self.current_subview = filtered_subviews[0]
+                self.current_subview_name = (
+                    self.app_data.baseline_or_proposed.get()
+                    + " "
+                    + self.current_subview.__repr__()
+                )
+
+        self.current_subview.grid(row=0, column=0, sticky=FILL)
+        self.current_subview.focus_set()
+        self.current_subview.open_subview()
 
     def toggle_active_subbutton(self, active_subbutton_name):
         for name, button in self.subview_buttons.items():
@@ -153,7 +208,7 @@ def toggle_active_subbutton(self, active_subbutton_name):
                 )
 
 
-class ExteriorSurfaceView(CTkXYFrame):
+class ExteriorSurfaceSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.surfaces_view = subview_frame.master
@@ -161,7 +216,7 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "ExteriorSurfaceView"
+        return "ExteriorSurfaceSubview"
 
     def open_subview(self):
         self.surfaces_view.toggle_active_subbutton("Exterior")
@@ -170,7 +225,18 @@ def open_subview(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, ext_wall_name in enumerate(self.app_data.rmds[0].ext_wall_names):
+        #  Get exterior walls from relevant rmd. Throw error if none found
+        ext_wall_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            ext_wall_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).ext_wall_names
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            ext_wall_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).ext_wall_names
+
+        for i, ext_wall_name in enumerate(ext_wall_names):
             self.add_row(i, ext_wall_name)
 
         self.is_subview_populated = True
@@ -195,7 +261,7 @@ def add_row(self, i, ext_wall_name):
         status_combo.grid(row=(i + 1), column=1, padx=PAD20END, pady=PAD20END)
 
 
-class InteriorSurfaceView(CTkXYFrame):
+class InteriorSurfaceSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.surfaces_view = subview_frame.master
@@ -203,7 +269,7 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "InteriorSurfaceView"
+        return "InteriorSurfaceSubview"
 
     def open_subview(self):
         self.surfaces_view.toggle_active_subbutton("Interior")
@@ -212,7 +278,18 @@ def open_subview(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, int_wall_name in enumerate(self.app_data.rmds[0].int_wall_names):
+        #  Get interior walls from relevant rmd. Throw error if none found
+        int_wall_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            int_wall_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).int_wall_names
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            int_wall_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).int_wall_names
+
+        for i, int_wall_name in enumerate(int_wall_names):
             self.add_row(i, int_wall_name)
 
         self.is_subview_populated = True
@@ -237,7 +314,7 @@ def add_row(self, i, int_wall_name):
         status_combo.grid(row=(i + 1), column=1, padx=PAD20END, pady=PAD20END)
 
 
-class UndergroundSurfaceView(CTkXYFrame):
+class UndergroundSurfaceSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.surfaces_view = subview_frame.master
@@ -245,7 +322,7 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "UndergroundSurfaceView"
+        return "UndergroundSurfaceSubview"
 
     def open_subview(self):
         self.surfaces_view.toggle_active_subbutton("Underground")
@@ -254,7 +331,18 @@ def open_subview(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, undg_wall_name in enumerate(self.app_data.rmds[0].undg_wall_names):
+        #  Get underground walls from relevant rmd. Throw error if none found
+        undg_wall_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            undg_wall_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).undg_wall_names
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            undg_wall_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).undg_wall_names
+
+        for i, undg_wall_name in enumerate(undg_wall_names):
             self.add_row(i, undg_wall_name)
 
         self.is_subview_populated = True
@@ -279,7 +367,7 @@ def add_row(self, i, undg_wall_name):
         status_combo.grid(row=(i + 1), column=1, padx=PAD20END, pady=PAD20END)
 
 
-class WindowSurfaceView(CTkXYFrame):
+class WindowSurfaceSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.surfaces_view = subview_frame.master
@@ -287,7 +375,7 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "WindowSurfaceView"
+        return "WindowSurfaceSubview"
 
     def open_subview(self):
         self.surfaces_view.toggle_active_subbutton("Windows")
@@ -296,7 +384,18 @@ def open_subview(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, window_name in enumerate(self.app_data.rmds[0].window_names):
+        #  Get windows from relevant rmd. Throw error if none found
+        window_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            window_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).window_names
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            window_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).window_names
+
+        for i, window_name in enumerate(window_names):
             self.add_row(i, window_name)
 
         self.is_subview_populated = True
@@ -304,7 +403,7 @@ def populate_subview(self):
     def add_column_headers(self):
         name_label = ctk.CTkLabel(self, text="Name", font=LABEL_FONT)
         name_label.grid(row=0, column=0, padx=PAD20END, pady=5)
-        if not self.app_data.is_all_new_construction:
+        if not self.app_data.is_all_new_construction.get():
             status_label = ctk.CTkLabel(self, text="Status", font=LABEL_FONT)
             status_label.grid(row=0, column=1, padx=PAD20END, pady=5)
         classification_label = ctk.CTkLabel(
@@ -323,7 +422,7 @@ def add_row(self, i, window_name):
         surface_label.grid(
             row=(i + 1), column=0, padx=PAD20END, pady=PAD20END, sticky=W
         )
-        if not self.app_data.is_all_new_construction:
+        if not self.app_data.is_all_new_construction.get():
             status_combo = ctk.CTkComboBox(
                 self,
                 values=self.app_data.StatusDescriptions,
@@ -351,7 +450,7 @@ def add_row(self, i, window_name):
         open_sensor_checkbox.grid(row=(i + 1), column=5, padx=PAD20END, pady=PAD20END)
 
 
-class SkylightSurfaceView(CTkXYFrame):
+class SkylightSurfaceSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.surfaces_view = subview_frame.master
@@ -359,7 +458,7 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "SkylightSurfaceView"
+        return "SkylightSurfaceSubview"
 
     def open_subview(self):
         self.surfaces_view.toggle_active_subbutton("Skylights")
@@ -368,7 +467,18 @@ def open_subview(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, skylight_name in enumerate(self.app_data.rmds[0].skylight_names):
+        #  Get skylights from relevant rmd. Throw error if none found
+        skylight_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            skylight_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).skylight_names
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            skylight_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).skylight_names
+
+        for i, skylight_name in enumerate(skylight_names):
             self.add_row(i, skylight_name)
 
         self.is_subview_populated = True
@@ -376,7 +486,7 @@ def populate_subview(self):
     def add_column_headers(self):
         name_label = ctk.CTkLabel(self, text="Name", font=LABEL_FONT)
         name_label.grid(row=0, column=0, padx=PAD20END, pady=5)
-        if not self.app_data.is_all_new_construction:
+        if not self.app_data.is_all_new_construction.get():
             status_label = ctk.CTkLabel(self, text="Status", font=LABEL_FONT)
             status_label.grid(row=0, column=1, padx=PAD20END, pady=5)
         classification_label = ctk.CTkLabel(
@@ -395,7 +505,7 @@ def add_row(self, i, skylight_name):
         surface_label.grid(
             row=(i + 1), column=0, padx=PAD20END, pady=PAD20END, sticky=W
         )
-        if not self.app_data.is_all_new_construction:
+        if not self.app_data.is_all_new_construction.get():
             status_combo = ctk.CTkComboBox(
                 self,
                 values=self.app_data.StatusDescriptions,
@@ -423,7 +533,7 @@ def add_row(self, i, skylight_name):
         open_sensor_checkbox.grid(row=(i + 1), column=5, padx=PAD20END, pady=PAD20END)
 
 
-class DoorSurfaceView(CTkXYFrame):
+class DoorSurfaceSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.surfaces_view = subview_frame.master
@@ -431,7 +541,7 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "DoorSurfaceView"
+        return "DoorSurfaceSubview"
 
     def open_subview(self):
         self.surfaces_view.toggle_active_subbutton("Doors")
@@ -440,7 +550,18 @@ def open_subview(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, door_name in enumerate(self.app_data.rmds[0].door_names):
+        #  Get doors from relevant rmd. Throw error if none found
+        door_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            door_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).door_names
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            door_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).door_names
+
+        for i, door_name in enumerate(door_names):
             self.add_row(i, door_name)
 
         self.is_subview_populated = True
diff --git a/interface/views/systems.py b/interface/views/systems.py
index 8cf99b09..97e8fa98 100644
--- a/interface/views/systems.py
+++ b/interface/views/systems.py
@@ -1,8 +1,9 @@
 import customtkinter as ctk
-import interface.custom_widgets as cw
 
+import interface.custom_widgets as cw
 from interface.ctk_xyframe import CTkXYFrame
 from interface.base_view import BaseView
+from interface.main_app_data import ASHRAE9012019ModelOptions
 
 
 LABEL_FONT = ("Arial", 14, "bold")
@@ -20,15 +21,20 @@
 class SystemsView(BaseView):
     def __init__(self, window):
         super().__init__(window)
+        self.main_window = window
 
         # All subviews will be placed inside this frame. Single row/column allows formatting of subview to be handled by the subview itself
         self.subview_frame = ctk.CTkFrame(self)
         self.current_subview = None
+        self.current_subview_name = None
 
         self.subviews = {
-            "HVAC Systems": HVACSystemView(self.subview_frame),
-            "Heat Rejection": HeatRejectionView(self.subview_frame),
-            "Zonal Exhaust": ZonalExhaustView(self.subview_frame),
+            "Baseline HVACSystemSubview": HVACSystemSubview(self.subview_frame),
+            "Proposed HVACSystemSubview": HVACSystemSubview(self.subview_frame),
+            "Baseline HeatRejectionSubview": HeatRejectionSubview(self.subview_frame),
+            "Proposed HeatRejectionSubview": HeatRejectionSubview(self.subview_frame),
+            "Baseline ZonalExhaustSubview": ZonalExhaustSubview(self.subview_frame),
+            "Proposed ZonalExhaustSubview": ZonalExhaustSubview(self.subview_frame),
         }
 
         # Directions frame holds all directions info and will get 'gridded' within the surfaces view grid
@@ -57,6 +63,7 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Systems")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(True)
 
         # 3 rows in the main surface view structure. Subview frame (row 4, index 3) has a weight to make it fill up the empty space in the window
         self.grid_rowconfigure(3, weight=1)
@@ -81,24 +88,29 @@ def open_view(self):
         self.subview_frame.grid_rowconfigure(0, weight=1)
         self.subview_frame.grid_columnconfigure(0, weight=1)
 
-        if self.subview_buttons:
-            # Open the first subview available
-            self.show_subview(next(iter(self.subview_buttons)))
+        self.current_subview_name = (
+            f"{self.app_data.baseline_or_proposed.get()} HVACSystemSubview"
+        )
+        self.show_subview(
+            f"{self.app_data.baseline_or_proposed.get()} HVACSystemSubview"
+        )
 
     def create_subbutton_bar(self):
         callback_methods = {}
         if len(self.app_data.rmds[0].heat_rejection_names) > 0:
             callback_methods["Heat Rejection"] = lambda: self.show_subview(
-                "Heat Rejection"
+                f"{self.app_data.baseline_or_proposed.get()} Heat Rejection"
             )
         if len(self.app_data.rmds[0].system_names) > 0:
-            callback_methods["HVAC Systems"] = lambda: self.show_subview("HVAC Systems")
+            callback_methods["HVAC Systems"] = lambda: self.show_subview(
+                f"{self.app_data.baseline_or_proposed.get()} HVAC Systems"
+            )
         if (
             len(self.app_data.rmds[0].zonal_exh_fan_names) > 0
-            and not self.app_data.is_all_new_construction
+            and not self.app_data.is_all_new_construction.get()
         ):
             callback_methods["Zonal Exhaust"] = lambda: self.show_subview(
-                "Zonal Exhaust"
+                f"{self.app_data.baseline_or_proposed.get()} Zonal Exhaust"
             )
 
         for name in callback_methods:
@@ -123,13 +135,28 @@ def show_subview(self, subview_name):
         if self.current_subview is not None:
             self.current_subview.grid_forget()
 
-        # Show new subview
         subview = self.subviews.get(subview_name)
         if subview:
+            self.current_subview_name = subview_name
             self.current_subview = subview
-            self.current_subview.grid(row=0, column=0, sticky=FILL)
-            self.current_subview.focus_set()
-            self.current_subview.open_subview()
+        else:
+            current_state = self.app_data.baseline_or_proposed.get()
+            filtered_subviews = [
+                value for key, value in self.subviews.items() if current_state in key
+            ]
+
+            # Set current_subview to the first matching subview, if found
+            if filtered_subviews:
+                self.current_subview = filtered_subviews[0]
+                self.current_subview_name = (
+                    self.app_data.baseline_or_proposed.get()
+                    + " "
+                    + self.current_subview.__repr__()
+                )
+
+        self.current_subview.grid(row=0, column=0, sticky=FILL)
+        self.current_subview.focus_set()
+        self.current_subview.open_subview()
 
     def toggle_active_subbutton(self, active_subbutton_name):
         for name, button in self.subview_buttons.items():
@@ -149,7 +176,7 @@ def toggle_active_subbutton(self, active_subbutton_name):
                 )
 
 
-class HeatRejectionView(CTkXYFrame):
+class HeatRejectionSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.systems_view = subview_frame.master
@@ -157,16 +184,27 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "HeatRejectionView"
+        return "HeatRejectionSubview"
 
     def open_subview(self):
         self.systems_view.toggle_active_subbutton("Heat Rejection")
         self.populate_subview() if not self.is_subview_populated else None
 
     def populate_subview(self):
-        for i, heat_rejection_name in enumerate(
-            self.app_data.rmds[0].heat_rejection_names
-        ):
+        self.add_column_headers()
+
+        #  Get heat rejections from relevant rmd. Throw error if none found
+        heat_rejection_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            heat_rejection_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).heat_rejection_names
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            heat_rejection_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).heat_rejection_names
+
+        for i, heat_rejection_name in enumerate(heat_rejection_names):
             self.add_row(i, heat_rejection_name)
 
         self.is_subview_populated = True
@@ -189,7 +227,7 @@ def add_row(self, i, heat_rejection_name):
         fan_type_combo.grid(row=(i + 1), column=1, padx=PAD20END, pady=PAD20END)
 
 
-class HVACSystemView(CTkXYFrame):
+class HVACSystemSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.systems_view = subview_frame.master
@@ -197,7 +235,7 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "HVACSystemView"
+        return "HVACSystemSubview"
 
     def open_subview(self):
         self.systems_view.toggle_active_subbutton("HVAC Systems")
@@ -206,7 +244,18 @@ def open_subview(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, hvac_system_name in enumerate(self.app_data.rmds[0].system_names):
+        #  Get hvac systems from relevant rmd. Throw error if none found
+        hvac_system_names = []
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            hvac_system_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).system_names
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            hvac_system_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).system_names
+
+        for i, hvac_system_name in enumerate(hvac_system_names):
             self.add_row(i, hvac_system_name)
 
         self.is_subview_populated = True
@@ -214,7 +263,7 @@ def populate_subview(self):
     def add_column_headers(self):
         name_label = ctk.CTkLabel(self, text="Name", font=LABEL_FONT)
         name_label.grid(row=0, column=0, padx=PAD20END, pady=5)
-        if not self.app_data.is_all_new_construction:
+        if not self.app_data.is_all_new_construction.get():
             status_label = ctk.CTkLabel(self, text="Status", font=LABEL_FONT)
             status_label.grid(row=0, column=1, padx=PAD20END, pady=5)
         dehumidification_type_label = ctk.CTkLabel(
@@ -231,7 +280,7 @@ def add_column_headers(self):
     def add_row(self, i, hvac_system_name):
         system_label = ctk.CTkLabel(self, text=f"{hvac_system_name}")
         system_label.grid(row=(i + 1), column=0, padx=PAD20END, pady=PAD20END, sticky=W)
-        if not self.app_data.is_all_new_construction:
+        if not self.app_data.is_all_new_construction.get():
             status_combo = ctk.CTkComboBox(
                 self,
                 values=self.app_data.StatusDescriptions,
@@ -258,7 +307,7 @@ def add_row(self, i, hvac_system_name):
         )
 
 
-class ZonalExhaustView(CTkXYFrame):
+class ZonalExhaustSubview(CTkXYFrame):
     def __init__(self, subview_frame):
         super().__init__(subview_frame)
         self.systems_view = subview_frame.master
@@ -266,7 +315,7 @@ def __init__(self, subview_frame):
         self.is_subview_populated = False
 
     def __repr__(self):
-        return "ZonalExhaustView"
+        return "ZonalExhaustSubview"
 
     def open_subview(self):
         self.systems_view.toggle_active_subbutton("Zonal Exhaust")
@@ -285,8 +334,18 @@ def populate_subview(self):
     def get_zonal_exhaust_fans(self):
         # TODO: Review this approach..may be tough once we are trying to set data back to the rmds
         zonal_exhaust_fans = []
-        for zone_name in self.app_data.rmds[0].zone_names:
-            zone_obj = self.app_data.rmds[0].get_obj(zone_name)
+
+        #  Get relevant rmd
+        rmd = None
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            rmd = self.app_data.get_rmd(ASHRAE9012019ModelOptions.PROPOSED)
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            rmd = self.app_data.get_rmd(ASHRAE9012019ModelOptions.BASELINE_0)
+        if not rmd:
+            return
+
+        for zone_name in rmd.zone_names:
+            zone_obj = rmd.get_obj(zone_name)
             if zone_obj.zonal_exhaust_fan:
                 zonal_exhaust_fans.append(zone_obj.zonal_exhaust_fan)
         return zonal_exhaust_fans
diff --git a/interface/views/zones.py b/interface/views/zones.py
index 051786d4..1778aa2e 100644
--- a/interface/views/zones.py
+++ b/interface/views/zones.py
@@ -24,7 +24,7 @@
 class ZonesView(BaseView):
     def __init__(self, window):
         super().__init__(window)
-
+        self.main_window = window
         self.view_frame = ctk.CTkFrame(self)
 
         # Directions frame holds all directions info and will get 'gridded' within the surfaces view grid
@@ -51,6 +51,7 @@ def __repr__(self):
     def open_view(self):
         self.toggle_active_button("Zones")
         self.grid_propagate(False)
+        self.main_window.show_baseline_proposed_toggle(False)
 
         # 2 rows in the main surface view structure.
         # View frame (row 2, index 1) has a weight to make it fill up the empty space in the window


Reviews:
user: login: JacksonJ-KC
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T14:50:47Z
user: login: JacksonJ-KC
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T14:52:56Z
user: login: JacksonJ-KC
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T16:41:25Z

Review comments:
diff_hunk: @@ -78,7 +87,27 @@ def open_view(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, space_name in enumerate(self.app_data.rmds[0].space_map.keys()):
+        #  Get spaces from relevant rmd. Throw error if none found
+        space_names = None
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            space_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).space_map.keys()
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            space_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).space_map.keys()
+        if not space_names:
+            msg = CTkMessagebox(
path: interface/views/spaces.py
user: login: JacksonJ-KC
body: Seems like this messagebox would be a replacement for the Errors/Warnings CTk TopLevelWindows that we're using?
created_at: 2025-03-10T14:50:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -202,6 +204,12 @@ def insert_to_rpd(self, mapping, obj_u_name: str = None):
                     enumerations_map.get(self.cooling_design_day.get()),
                 )
 
+    def get_rmd(self, rmd_type):
path: interface/main_app_data.py
user: login: JacksonJ-KC
body: also FYI (don't need to update) you could have done this in a one-liner
`return next((rmd for rmd in self.rmds if rmd.type == rmd_type), None)`

created_at: 2025-03-10T14:52:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -78,7 +87,27 @@ def open_view(self):
     def populate_subview(self):
         self.add_column_headers()
 
-        for i, space_name in enumerate(self.app_data.rmds[0].space_map.keys()):
+        #  Get spaces from relevant rmd. Throw error if none found
+        space_names = None
+        if self.app_data.baseline_or_proposed.get() == "Proposed":
+            space_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.PROPOSED
+            ).space_map.keys()
+        elif self.app_data.baseline_or_proposed.get() == "Baseline":
+            space_names = self.app_data.get_rmd(
+                ASHRAE9012019ModelOptions.BASELINE_0
+            ).space_map.keys()
+        if not space_names:
+            msg = CTkMessagebox(
path: interface/views/spaces.py
user: login: JacksonJ-KC
body: I'm going to remove the use of this here because this scenario of no spaces could never be simulated - so could not have the required files to reach this point of the interface.
created_at: 2025-03-10T16:41:24Z
author_association: CONTRIBUTOR

PR comments:
user: login: bradcline-kc
created_at: 2025-03-07T19:17:05Z
body: Need to address situation where there may be a different set of subviews between baseline and proposed models. For example, if the proposed has skylights and the baseline does not. Is that a valid scenario? Seems like it would be uncommon. Even less so if we stick to the simplified views.

 Using the example above, one of two things happens:

1. The proposed has skylights and the baseline does not. The skylight tab appears, regardless of the toggle state and when you are in baseline mode (no skylights) there is a dialog that pops up that says "There aren't any skylights in this model"
2. The baseline has skylights and the proposed does not. The skylight tab does not appear because we are only checking the proposed for which tabs to populate (or something like that). 

Keeping with this example, my thought it to hide the skylight tab if you're on the model that doesn't have skylights and show it when you do. @JacksonJ-KC  What do you think here? Is it disorienting to have tabs appear and disappear when swapping between models?
author_association: CONTRIBUTOR
user: login: JacksonJ-KC
created_at: 2025-03-10T13:25:08Z
body: During testing, ran into a bug that crashed the program.

>   File "C:\Users\JacksonJarboe\Documents\Development\DOE2-229RPDGenerator\interface\views\spaces.py", line 194, in add_row
>     ventilation_space_type_combo = ctk.CTkComboBox(
>                                    ^^^^^^^^^^^^^^^^
>   File "C:\Users\JacksonJarboe\.virtualenvs\DOE2-229RPDGenerator-P0TNiW4w\Lib\site-packages\customtkinter\windows\widgets\ctk_combobox.py", line 79, in __init__
>     self._dropdown_menu = DropdownMenu(master=self,
>                           ^^^^^^^^^^^^^^^^^^^^^^^^^
>   File "C:\Users\JacksonJarboe\.virtualenvs\DOE2-229RPDGenerator-P0TNiW4w\Lib\site-packages\customtkinter\windows\widgets\core_widget_classes\dropdown_menu.py", line 44, in __init__
>     self._add_menu_commands()
>   File "C:\Users\JacksonJarboe\.virtualenvs\DOE2-229RPDGenerator-P0TNiW4w\Lib\site-packages\customtkinter\windows\widgets\core_widget_classes\dropdown_menu.py", line 100, in _add_menu_commands
>     self.add_command(label=value.ljust(self._min_character_width),
>   File "c:\users\jacksonjarboe\appdata\local\programs\python\python311-32\Lib\tkinter\__init__.py", line 3368, in add_command
>     self.add('command', cnf or kw)
>   File "c:\users\jacksonjarboe\appdata\local\programs\python\python311-32\Lib\tkinter\__init__.py", line 3355, in add
>     self.tk.call((self._w, 'add', itemType) +
> _tkinter.TclError
author_association: CONTRIBUTOR
user: login: JacksonJ-KC
created_at: 2025-03-10T13:27:08Z
body: > Need to address situation where there may be a different set of subviews between baseline and proposed models. For example, if the proposed has skylights and the baseline does not. Is that a valid scenario? Seems like it would be uncommon. Even less so if we stick to the simplified views.
> 
> Using the example above, one of two things happens:
> 
> 1. The proposed has skylights and the baseline does not. The skylight tab appears, regardless of the toggle state and when you are in baseline mode (no skylights) there is a dialog that pops up that says "There aren't any skylights in this model"
> 2. The baseline has skylights and the proposed does not. The skylight tab does not appear because we are only checking the proposed for which tabs to populate (or something like that).
> 
> Keeping with this example, my thought it to hide the skylight tab if you're on the model that doesn't have skylights and show it when you do. @JacksonJ-KC What do you think here? Is it disorienting to have tabs appear and disappear when swapping between models?

Agreed, let's always hide tabs that are not applicable to a given model - even if that is triggered on the baseline/proposed toggle.
author_association: CONTRIBUTOR