From 60e6110ed7274d6ae4701c7c9da5d12bb5b8f7d0 Mon Sep 17 00:00:00 2001 From: ajmalbabums Date: Fri, 21 Mar 2025 17:16:47 +0530 Subject: [PATCH 01/12] unit test: test_cost_components --- README.md | 6 +- src/__init__.py | 0 src/osbridgelcca/backend/__init__.py | 0 src/osbridgelcca/core/__init__.py | 0 src/osbridgelcca/core/bridge_lcc.py | 3 +- .../{cost_component.py => cost_components.py} | 78 +++++-------------- .../{src => src_web}/components/InputForm.jsx | 0 .../components/ResultsView.jsx | 0 .../web_app/{src => src_web}/services/api.js | 0 tests/unit_tests/conftest.py | 15 +--- tests/unit_tests/test_cost_components.py | 58 ++++++++++++++ 11 files changed, 84 insertions(+), 76 deletions(-) create mode 100644 src/__init__.py create mode 100644 src/osbridgelcca/backend/__init__.py create mode 100644 src/osbridgelcca/core/__init__.py rename src/osbridgelcca/core/{cost_component.py => cost_components.py} (60%) rename src/osbridgelcca/web_app/{src => src_web}/components/InputForm.jsx (100%) rename src/osbridgelcca/web_app/{src => src_web}/components/ResultsView.jsx (100%) rename src/osbridgelcca/web_app/{src => src_web}/services/api.js (100%) create mode 100644 tests/unit_tests/test_cost_components.py diff --git a/README.md b/README.md index 6ffa57a..75e3589 100644 --- a/README.md +++ b/README.md @@ -59,17 +59,17 @@ python scripts/verify_installation.py ### 🖥️ **Running the Desktop Application** ```sh -python src/osbridgelcca/desktop/app.py +python src_web/osbridgelcca/desktop/app.py ``` ### 🌐 **Running the Web Application** #### **Start the Backend Server** ```sh -python src/osbridgelcca/backend/manage.py runserver +python src_web/osbridgelcca/backend/manage.py runserver ``` #### **Start the Frontend** ```sh -cd src/osbridgelcca/web +cd src_web/osbridgelcca/web npm install npm start ``` diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/osbridgelcca/backend/__init__.py b/src/osbridgelcca/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/osbridgelcca/core/__init__.py b/src/osbridgelcca/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/osbridgelcca/core/bridge_lcc.py b/src/osbridgelcca/core/bridge_lcc.py index 4cb3716..938ff31 100644 --- a/src/osbridgelcca/core/bridge_lcc.py +++ b/src/osbridgelcca/core/bridge_lcc.py @@ -3,8 +3,7 @@ class BridgeLCC: def __init__(self, project_name, inputs): self.project_name = project_name - self.inputs = inputs # Dictionary of user inputs - self.outputs = {} + self.inputs = inputs # input object def calculate_lcc(self): """Calculate the total life cycle cost based on input parameters.""" diff --git a/src/osbridgelcca/core/cost_component.py b/src/osbridgelcca/core/cost_components.py similarity index 60% rename from src/osbridgelcca/core/cost_component.py rename to src/osbridgelcca/core/cost_components.py index c839b93..a5f5791 100644 --- a/src/osbridgelcca/core/cost_component.py +++ b/src/osbridgelcca/core/cost_components.py @@ -11,13 +11,13 @@ def __init__(self, amount, category, is_initial, is_recurring, present_worth_fac :param category: Economic, Environmental, or Social :param is_initial: True if an initial cost, False if future cost :param is_recurring: True if recurring, False if one-time - :param present_worth_factor: Discounting factor for future costs (present_worth_factor) + :param present_worth_factor: Discounting factor for future costs """ self.amount = amount self.category = category self.is_initial = is_initial self.is_recurring = is_recurring - self.present_worth_factor = present_worth_factor + self.pwf = present_worth_factor @abstractmethod def calculate_cost(self): @@ -25,126 +25,88 @@ def calculate_cost(self): pass -class InitialConstructionCost(CostComponent): - """Covers material, labor, and equipment costs for bridge construction.""" +def calculate_pwf(discount_rate, period, design_life): + return sum(1 / ((1 + discount_rate) ** (i * period)) for i in range(1, int(design_life / period) + 1)) + +class InitialConstructionCost(CostComponent): def __init__(self, quantity, rate): super().__init__(amount=quantity * rate, category="Economic", is_initial=True, is_recurring=False, present_worth_factor=1.00) self.quantity = quantity self.rate = rate def calculate_cost(self): - return self.quantity * self.rate * self.present_worth_factor + return self.quantity * self.rate * self.pwf class InitialCarbonEmissionCost(CostComponent): - """Calculates initial carbon emissions from material production and transport.""" - def __init__(self, material_quantity, carbon_emission_factor, carbon_cost): super().__init__(amount=(material_quantity * carbon_emission_factor) * carbon_cost, category="Environmental", is_initial=True, is_recurring=False, present_worth_factor=1.00) - self.material_quantity = material_quantity - self.carbon_emission_factor = carbon_emission_factor - self.carbon_cost = carbon_cost def calculate_cost(self): - return (self.material_quantity * self.carbon_emission_factor) * self.carbon_cost * self.present_worth_factor + return self.amount class TimeCost(CostComponent): - """Calculates economic losses due to construction delays.""" - def __init__(self, construction_cost, interest_rate, time, investment_ratio): cost = construction_cost * interest_rate * time * investment_ratio super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, present_worth_factor=1.00) - self.construction_cost = construction_cost - self.interest_rate = interest_rate - self.time = time - self.investment_ratio = investment_ratio def calculate_cost(self): - return self.construction_cost * self.interest_rate * self.time * self.investment_ratio * self.present_worth_factor + return self.amount class RoadUserCost(CostComponent): - """Evaluates economic impact on road users due to delays and detours.""" - def __init__(self, vehicles_affected, vehicle_operation_cost, construction_time): cost = vehicles_affected * vehicle_operation_cost * construction_time super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, present_worth_factor=1.00) - self.vehicles_affected = vehicles_affected - self.vehicle_operation_cost = vehicle_operation_cost - self.construction_time = construction_time def calculate_cost(self): - return self.vehicles_affected * self.vehicle_operation_cost * self.construction_time * self.present_worth_factor + return self.amount class AdditionalCarbonEmissionCost(CostComponent): - """Accounts for increased emissions from detoured traffic during bridge work.""" - def __init__(self, vehicles_affected, reroute_distance, co2_emission_per_km, carbon_cost): cost = vehicles_affected * reroute_distance * co2_emission_per_km * carbon_cost super().__init__(amount=cost, category="Environmental", is_initial=True, is_recurring=False, present_worth_factor=1.00) - self.vehicles_affected = vehicles_affected - self.reroute_distance = reroute_distance - self.co2_emission_per_km = co2_emission_per_km - self.carbon_cost = carbon_cost def calculate_cost(self): - return self.vehicles_affected * self.reroute_distance * self.co2_emission_per_km * self.carbon_cost * self.present_worth_factor + return self.amount class PeriodicMaintenanceCost(CostComponent): - """Includes expenses for routine maintenance activities.""" - def __init__(self, maintenance_cost_rate, construction_cost, discount_rate, period, design_life): - pwf = sum(1 / ((1 + discount_rate) ** (i * period)) for i in range(1, int(design_life / period) + 1)) + pwf = calculate_pwf(discount_rate, period, design_life) cost = maintenance_cost_rate * construction_cost * pwf super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) - self.maintenance_cost_rate = maintenance_cost_rate - self.construction_cost = construction_cost - self.discount_rate = discount_rate - self.period = period - self.design_life = design_life def calculate_cost(self): - return self.maintenance_cost_rate * self.construction_cost * self.present_worth_factor + return self.amount class PeriodicMaintenanceCarbonCost(CostComponent): - """Calculates emissions from maintenance activities.""" - def __init__(self, material_quantity, carbon_emission_factor, carbon_cost, discount_rate, period, design_life): - pwf = sum(1 / ((1 + discount_rate) ** (i * period)) for i in range(1, int(design_life / period) + 1)) + pwf = calculate_pwf(discount_rate, period, design_life) cost = material_quantity * carbon_emission_factor * carbon_cost * pwf super().__init__(amount=cost, category="Environmental", is_initial=False, is_recurring=True, present_worth_factor=pwf) - self.material_quantity = material_quantity - self.carbon_emission_factor = carbon_emission_factor - self.carbon_cost = carbon_cost def calculate_cost(self): - return self.material_quantity * self.carbon_emission_factor * self.carbon_cost * self.present_worth_factor + return self.amount class RoutineInspectionCost(CostComponent): - """Annual cost of inspections for structural integrity.""" - def __init__(self, quantity, rate, discount_rate, design_life): - pwf = sum(1 / ((1 + discount_rate) ** i) for i in range(1, design_life + 1)) + pwf = calculate_pwf(discount_rate, 1, design_life) cost = quantity * rate * pwf super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) - self.quantity = quantity - self.rate = rate def calculate_cost(self): - return self.quantity * self.rate * self.present_worth_factor + return self.amount class RepairAndRehabilitationCost(CostComponent): - """Covers major structural repairs and retrofitting.""" - def __init__(self, repair_cost_rate, construction_cost, discount_rate, period, design_life): - pwf = sum(1 / ((1 + discount_rate) ** (i * period)) for i in range(1, int(design_life / period) + 1)) + pwf = calculate_pwf(discount_rate, period, design_life) cost = repair_cost_rate * construction_cost * pwf super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) @@ -153,8 +115,6 @@ def calculate_cost(self): class DemolitionCost(CostComponent): - """Costs incurred at the end of bridge life for demolition and disposal.""" - def __init__(self, demolition_rate, construction_cost, discount_rate, design_life): pwf = 1 / ((1 + discount_rate) ** design_life) cost = demolition_rate * construction_cost * pwf @@ -165,8 +125,6 @@ def calculate_cost(self): class RecyclingCost(CostComponent): - """Accounts for material salvage and repurposing costs.""" - def __init__(self, scrap_value, quantity, discount_rate, design_life): pwf = 1 / ((1 + discount_rate) ** design_life) cost = scrap_value * quantity * pwf diff --git a/src/osbridgelcca/web_app/src/components/InputForm.jsx b/src/osbridgelcca/web_app/src_web/components/InputForm.jsx similarity index 100% rename from src/osbridgelcca/web_app/src/components/InputForm.jsx rename to src/osbridgelcca/web_app/src_web/components/InputForm.jsx diff --git a/src/osbridgelcca/web_app/src/components/ResultsView.jsx b/src/osbridgelcca/web_app/src_web/components/ResultsView.jsx similarity index 100% rename from src/osbridgelcca/web_app/src/components/ResultsView.jsx rename to src/osbridgelcca/web_app/src_web/components/ResultsView.jsx diff --git a/src/osbridgelcca/web_app/src/services/api.js b/src/osbridgelcca/web_app/src_web/services/api.js similarity index 100% rename from src/osbridgelcca/web_app/src/services/api.js rename to src/osbridgelcca/web_app/src_web/services/api.js diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index a0993b8..afb922a 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -1,12 +1,5 @@ -import pytest -from backend.main import app as flask_app +import sys +import os -@pytest.fixture -def client(): - """Flask test client""" - return flask_app.test_client() - -@pytest.fixture -def sample_cost_data(): - """Sample cost data for testing""" - return {"quantity": 100, "rate": 500} +# Add the src_web directory to sys.path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../src_web'))) diff --git a/tests/unit_tests/test_cost_components.py b/tests/unit_tests/test_cost_components.py new file mode 100644 index 0000000..cb235e3 --- /dev/null +++ b/tests/unit_tests/test_cost_components.py @@ -0,0 +1,58 @@ +import pytest +from src.osbridgelcca.core.cost_components import ( + InitialConstructionCost, + InitialCarbonEmissionCost, + TimeCost, + RoadUserCost, + AdditionalCarbonEmissionCost, + PeriodicMaintenanceCost, + PeriodicMaintenanceCarbonCost, + RoutineInspectionCost, + RepairAndRehabilitationCost, + DemolitionCost, + RecyclingCost, +) + +def test_initial_construction_cost(): + cost = InitialConstructionCost(quantity=100, rate=50) + assert cost.calculate_cost() == 5000 + +def test_initial_carbon_emission_cost(): + cost = InitialCarbonEmissionCost(material_quantity=100, carbon_emission_factor=2, carbon_cost=10) + assert cost.calculate_cost() == 2000 + +def test_time_cost(): + cost = TimeCost(construction_cost=10000, interest_rate=0.05, time=2, investment_ratio=0.8) + assert cost.calculate_cost() == 800.0 + +def test_road_user_cost(): + cost = RoadUserCost(vehicles_affected=1000, vehicle_operation_cost=2, construction_time=5) + assert cost.calculate_cost() == 10000 + +def test_additional_carbon_emission_cost(): + cost = AdditionalCarbonEmissionCost(vehicles_affected=1000, reroute_distance=10, co2_emission_per_km=0.5, carbon_cost=20) + assert cost.calculate_cost() == 100000.0 + +def test_periodic_maintenance_cost(): + cost = PeriodicMaintenanceCost(maintenance_cost_rate=0.02, construction_cost=100000, discount_rate=0.03, period=5, design_life=50) + assert cost.calculate_cost() > 0 # Ensure cost is calculated + +def test_periodic_maintenance_carbon_cost(): + cost = PeriodicMaintenanceCarbonCost(material_quantity=500, carbon_emission_factor=1.5, carbon_cost=20, discount_rate=0.03, period=5, design_life=50) + assert cost.calculate_cost() > 0 + +def test_routine_inspection_cost(): + cost = RoutineInspectionCost(quantity=10, rate=1000, discount_rate=0.03, design_life=50) + assert cost.calculate_cost() > 0 + +def test_repair_and_rehabilitation_cost(): + cost = RepairAndRehabilitationCost(repair_cost_rate=0.1, construction_cost=100000, discount_rate=0.03, period=10, design_life=50) + assert cost.calculate_cost() > 0 + +def test_demolition_cost(): + cost = DemolitionCost(demolition_rate=0.05, construction_cost=100000, discount_rate=0.03, design_life=50) + assert cost.calculate_cost() > 0 + +def test_recycling_cost(): + cost = RecyclingCost(scrap_value=1000, quantity=10, discount_rate=0.03, design_life=50) + assert cost.calculate_cost() > 0 From 23173c820cb4663762a37506b59ea5db84fff018 Mon Sep 17 00:00:00 2001 From: ajmalbabums Date: Fri, 21 Mar 2025 17:43:12 +0530 Subject: [PATCH 02/12] calc: PWF eqn --- src/osbridgelcca/core/cost_components.py | 26 ++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/osbridgelcca/core/cost_components.py b/src/osbridgelcca/core/cost_components.py index a5f5791..4a6de34 100644 --- a/src/osbridgelcca/core/cost_components.py +++ b/src/osbridgelcca/core/cost_components.py @@ -25,8 +25,30 @@ def calculate_cost(self): pass -def calculate_pwf(discount_rate, period, design_life): - return sum(1 / ((1 + discount_rate) ** (i * period)) for i in range(1, int(design_life / period) + 1)) +def calculate_pwf(discount_rate, design_life, is_initial=False, is_recurring=True, interval=1, future_year=None): + """ + Calculates the Present Worth Factor (PWF) based on cost type. + + :param discount_rate: Discount rate (decimal form, e.g., 0.05 for 5%). + :param design_life: Total design life in years. + :param is_initial: True for initial cost (PWF = 1). + :param is_recurring: True for recurring costs. + :param interval: Interval in years for recurring costs (default is 1). + :param future_year: Year when a non-recurring future cost occurs. + :return: Present Worth Factor (PWF). + """ + if is_initial: + return 1.0 # Initial cost has no discounting + + elif not is_recurring: + if future_year is None: + raise ValueError("future_year must be provided for non-recurring future costs") + return 1 / ((1 + discount_rate) ** future_year) + + else: + if interval is None: + raise ValueError("interval must be provided for recurring costs") + return sum(1 / ((1 + discount_rate) ** (i * interval)) for i in range(1, int(design_life / interval) + 1)) class InitialConstructionCost(CostComponent): From b561eb600ff2f5b7392f1f5d5b1475080755eb3b Mon Sep 17 00:00:00 2001 From: ajmalbabums Date: Fri, 21 Mar 2025 17:44:44 +0530 Subject: [PATCH 03/12] calc: PWF eqn --- src/osbridgelcca/core/cost_components.py | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/osbridgelcca/core/cost_components.py b/src/osbridgelcca/core/cost_components.py index 4a6de34..43a275c 100644 --- a/src/osbridgelcca/core/cost_components.py +++ b/src/osbridgelcca/core/cost_components.py @@ -3,7 +3,7 @@ class CostComponent(ABC): """Abstract Base Class for different cost components in Life Cycle Cost Analysis.""" - def __init__(self, amount, category, is_initial, is_recurring, present_worth_factor): + def __init__(self, amount, category, is_initial, is_recurring, pwf): """ Initialize a generic cost component. @@ -11,13 +11,13 @@ def __init__(self, amount, category, is_initial, is_recurring, present_worth_fac :param category: Economic, Environmental, or Social :param is_initial: True if an initial cost, False if future cost :param is_recurring: True if recurring, False if one-time - :param present_worth_factor: Discounting factor for future costs + :param pwf: Discounting factor for future costs """ self.amount = amount self.category = category self.is_initial = is_initial self.is_recurring = is_recurring - self.pwf = present_worth_factor + self.pwf = pwf @abstractmethod def calculate_cost(self): @@ -53,7 +53,7 @@ def calculate_pwf(discount_rate, design_life, is_initial=False, is_recurring=Tru class InitialConstructionCost(CostComponent): def __init__(self, quantity, rate): - super().__init__(amount=quantity * rate, category="Economic", is_initial=True, is_recurring=False, present_worth_factor=1.00) + super().__init__(amount=quantity * rate, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) self.quantity = quantity self.rate = rate @@ -63,7 +63,7 @@ def calculate_cost(self): class InitialCarbonEmissionCost(CostComponent): def __init__(self, material_quantity, carbon_emission_factor, carbon_cost): - super().__init__(amount=(material_quantity * carbon_emission_factor) * carbon_cost, category="Environmental", is_initial=True, is_recurring=False, present_worth_factor=1.00) + super().__init__(amount=(material_quantity * carbon_emission_factor) * carbon_cost, category="Environmental", is_initial=True, is_recurring=False, pwf=1.00) def calculate_cost(self): return self.amount @@ -72,7 +72,7 @@ def calculate_cost(self): class TimeCost(CostComponent): def __init__(self, construction_cost, interest_rate, time, investment_ratio): cost = construction_cost * interest_rate * time * investment_ratio - super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, present_worth_factor=1.00) + super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) def calculate_cost(self): return self.amount @@ -81,7 +81,7 @@ def calculate_cost(self): class RoadUserCost(CostComponent): def __init__(self, vehicles_affected, vehicle_operation_cost, construction_time): cost = vehicles_affected * vehicle_operation_cost * construction_time - super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, present_worth_factor=1.00) + super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) def calculate_cost(self): return self.amount @@ -90,7 +90,7 @@ def calculate_cost(self): class AdditionalCarbonEmissionCost(CostComponent): def __init__(self, vehicles_affected, reroute_distance, co2_emission_per_km, carbon_cost): cost = vehicles_affected * reroute_distance * co2_emission_per_km * carbon_cost - super().__init__(amount=cost, category="Environmental", is_initial=True, is_recurring=False, present_worth_factor=1.00) + super().__init__(amount=cost, category="Environmental", is_initial=True, is_recurring=False, pwf=1.00) def calculate_cost(self): return self.amount @@ -100,7 +100,7 @@ class PeriodicMaintenanceCost(CostComponent): def __init__(self, maintenance_cost_rate, construction_cost, discount_rate, period, design_life): pwf = calculate_pwf(discount_rate, period, design_life) cost = maintenance_cost_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) def calculate_cost(self): return self.amount @@ -110,7 +110,7 @@ class PeriodicMaintenanceCarbonCost(CostComponent): def __init__(self, material_quantity, carbon_emission_factor, carbon_cost, discount_rate, period, design_life): pwf = calculate_pwf(discount_rate, period, design_life) cost = material_quantity * carbon_emission_factor * carbon_cost * pwf - super().__init__(amount=cost, category="Environmental", is_initial=False, is_recurring=True, present_worth_factor=pwf) + super().__init__(amount=cost, category="Environmental", is_initial=False, is_recurring=True, pwf=pwf) def calculate_cost(self): return self.amount @@ -120,7 +120,7 @@ class RoutineInspectionCost(CostComponent): def __init__(self, quantity, rate, discount_rate, design_life): pwf = calculate_pwf(discount_rate, 1, design_life) cost = quantity * rate * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) def calculate_cost(self): return self.amount @@ -130,7 +130,7 @@ class RepairAndRehabilitationCost(CostComponent): def __init__(self, repair_cost_rate, construction_cost, discount_rate, period, design_life): pwf = calculate_pwf(discount_rate, period, design_life) cost = repair_cost_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) def calculate_cost(self): return self.amount @@ -140,7 +140,7 @@ class DemolitionCost(CostComponent): def __init__(self, demolition_rate, construction_cost, discount_rate, design_life): pwf = 1 / ((1 + discount_rate) ** design_life) cost = demolition_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, present_worth_factor=pwf) + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, pwf=pwf) def calculate_cost(self): return self.amount @@ -150,7 +150,7 @@ class RecyclingCost(CostComponent): def __init__(self, scrap_value, quantity, discount_rate, design_life): pwf = 1 / ((1 + discount_rate) ** design_life) cost = scrap_value * quantity * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, present_worth_factor=pwf) + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, pwf=pwf) def calculate_cost(self): return self.amount From 6920e1dfcf937b2c4c35990c13861d77e5adf116 Mon Sep 17 00:00:00 2001 From: ajmalbabums Date: Fri, 21 Mar 2025 17:48:30 +0530 Subject: [PATCH 04/12] calc: abstract class --- src/osbridgelcca/core/cost_components.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/osbridgelcca/core/cost_components.py b/src/osbridgelcca/core/cost_components.py index 43a275c..b08a90c 100644 --- a/src/osbridgelcca/core/cost_components.py +++ b/src/osbridgelcca/core/cost_components.py @@ -13,11 +13,11 @@ def __init__(self, amount, category, is_initial, is_recurring, pwf): :param is_recurring: True if recurring, False if one-time :param pwf: Discounting factor for future costs """ - self.amount = amount + self.amount = None self.category = category - self.is_initial = is_initial - self.is_recurring = is_recurring - self.pwf = pwf + self.is_initial = None + self.is_recurring = None + self.pwf = None @abstractmethod def calculate_cost(self): From bc2f26cef57a7ad06c7f58153cc9f6bb13a3d9c4 Mon Sep 17 00:00:00 2001 From: AjmalBabuMS Date: Sun, 23 Mar 2025 16:07:18 +0530 Subject: [PATCH 05/12] installation setup --- install.bat | 9 ++++++++- scripts/verify_installation.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/install.bat b/install.bat index 5a1a326..312ff7a 100644 --- a/install.bat +++ b/install.bat @@ -1,6 +1,13 @@ @echo off echo Creating and activating Conda environment... -call conda activate osbridgelcca || conda create -n osbridgelcca python=3.9 -y && conda activate osbridgelcca + +REM Try activating the environment +call conda activate osbridgelcca 2>nul +if errorlevel 1 ( + echo Environment not found. Creating it now... + call conda create -n osbridgelcca python=3.9 -y + call conda activate osbridgelcca +) echo Installing dependencies from pyproject.toml... pip install . diff --git a/scripts/verify_installation.py b/scripts/verify_installation.py index 9e6afe4..8ceaa8c 100644 --- a/scripts/verify_installation.py +++ b/scripts/verify_installation.py @@ -1,5 +1,6 @@ import importlib import sys +sys.stdout.reconfigure(encoding='utf-8') # List of required packages REQUIRED_PACKAGES = [ From b3734fcda6e3e0c5b0612c5b3f6fa167dfceeada Mon Sep 17 00:00:00 2001 From: AjmalBabuMS Date: Sun, 23 Mar 2025 16:08:48 +0530 Subject: [PATCH 06/12] lcc cost components --- src/osbridgelcca/core/bridge_lcc.py | 75 +++++++++-- src/osbridgelcca/core/cost_components.py | 163 +++++++++++------------ 2 files changed, 142 insertions(+), 96 deletions(-) diff --git a/src/osbridgelcca/core/bridge_lcc.py b/src/osbridgelcca/core/bridge_lcc.py index 938ff31..1b9138c 100644 --- a/src/osbridgelcca/core/bridge_lcc.py +++ b/src/osbridgelcca/core/bridge_lcc.py @@ -1,25 +1,84 @@ +import pandas as pd +from input_data import Input +from cost_components import CostComponent +from src.osbridgelcca.core.cost_components import InitialConstructionCost, InitialCarbonEmissionCost, TimeCost, \ + RoadUserCost + + class BridgeLCC: """Main class for handling Life Cycle Cost Analysis of bridges.""" - - def __init__(self, project_name, inputs): + + def __init__(self, project_name: str, inputs: Input): self.project_name = project_name - self.inputs = inputs # input object + self.inputs = inputs # Expecting an instance of Input class + self.outputs = {} # Initialize output storage def calculate_lcc(self): """Calculate the total life cycle cost based on input parameters.""" try: - material_cost = sum(self.inputs["bill_of_quantity"].values()) - maintenance_cost = self.inputs["maintenance_cost"] - operation_cost = self.inputs["vehicle_operating_cost"] - discount_rate = self.inputs["discount_rate"] + material_cost = self.inputs.material_data.quantity * self.inputs.material_data.unit_rate + maintenance_cost = self.inputs.maintenance_data.periodic_maintenance_rate + operation_cost = self.inputs.traffic_data.annual_traffic_increase * 5000 # Example formula + discount_rate = self.inputs.finance_data.discount_rate / 100 # Convert percentage to decimal # Simple NPV calculation total_cost = (material_cost + maintenance_cost + operation_cost) / (1 + discount_rate) self.outputs["total_lcc"] = total_cost return total_cost - except KeyError as e: + except AttributeError as e: raise ValueError(f"Missing input parameter: {e}") def get_outputs(self): """Return computed outputs.""" return self.outputs + + def tabulate_cost_components(self): + """Create a Pandas DataFrame for cost components.""" + data = { + "Sl. No.": list(range(1, 12)) + ["Total"], + "Cost Component": [ + "Initial costs", "Initial carbon emissions costs", "Time costs", + "Road user costs due to re-routing", "Carbon emission cost due to re-routing", + "Periodic maintenance costs", "Periodic maintenance carbon emissions costs", + "Annual routine inspection costs", "Repair and rehabilitation costs", + "Demolition and disposal costs", "Recycling costs", "Total life cycle cost" + ], + "Cost Amount": [InitialConstructionCost.amount, + InitialCarbonEmissionCost.amount, + TimeCost.amount, + RoadUserCost.amount, + 82.62, 19.36, 1.5, 17.68, 13.8, 0, 0.84, -2.67, 212.81], + "Life Cycle Stage": [ + "Initial", "Initial", "Initial", "Initial", "Initial", + "Use", "Use", "Use", "Use", + "End", "End", "Total" + ], + "Cost Type": [ + "Economic", "Environmental", "Social", "Social", "Environmental", + "Economic", "Environmental", "Economic", "Economic", + "Economic", "Economic", "Total" + ] + } + + df = pd.DataFrame(data) + return df + +# Example Usage +if __name__ == "__main__": + from input_data import MaterialData, FinanceData, TrafficData, MaintenanceData, RepairData, DemolitionData, RecycleData, CarbonEmissionData + + material = MaterialData("Steel", "Reinforcement bars", 5000, "kg", 60, "Govt Schedule", 32, 2.5, "EPD Source", 5, 90) + finance = FinanceData(5.0, 7.5, 1.2) + carbon = CarbonEmissionData("SSP2", "RCP6", 1200) + traffic = TrafficData(15, 4000, "Plain", "Urban", 10, {"Car": 50, "Truck": 30, "Bus": 20}) + maintenance = MaintenanceData(0.55, 1.0, 10, 5, 1) + repair = RepairData("Component-wise repair details") + demolition = DemolitionData(10) + recycle = RecycleData(80, 10000, 5000) + + project_input = Input(material, finance, carbon, traffic, maintenance, repair, demolition, recycle) + bridge = BridgeLCC("Bridge A", project_input) + + print("Total LCC:", bridge.calculate_lcc()) + print("\nCost Components Table:") + print(bridge.tabulate_cost_components()) diff --git a/src/osbridgelcca/core/cost_components.py b/src/osbridgelcca/core/cost_components.py index b08a90c..155cba9 100644 --- a/src/osbridgelcca/core/cost_components.py +++ b/src/osbridgelcca/core/cost_components.py @@ -1,31 +1,27 @@ from abc import ABC, abstractmethod + class CostComponent(ABC): """Abstract Base Class for different cost components in Life Cycle Cost Analysis.""" - def __init__(self, amount, category, is_initial, is_recurring, pwf): - """ - Initialize a generic cost component. - - :param amount: Cost amount in INR - :param category: Economic, Environmental, or Social - :param is_initial: True if an initial cost, False if future cost - :param is_recurring: True if recurring, False if one-time - :param pwf: Discounting factor for future costs - """ - self.amount = None - self.category = category - self.is_initial = None - self.is_recurring = None - self.pwf = None + def __init__(self): + self.amount = 0 # To be calculated in subclasses + self.category = None # Defined in subclass + self.is_initial = None # Defined in subclass + self.is_recurring = None # Defined in subclass + self.pwf = None # To be calculated in subclasses @abstractmethod def calculate_cost(self): """Abstract method to be implemented by subclasses for cost calculation.""" pass + def __str__(self): + return f"{self.__class__.__name__}: Amount = {self.amount:.2f}, Category = {self.category}" + -def calculate_pwf(discount_rate, design_life, is_initial=False, is_recurring=True, interval=1, future_year=None): +def calculate_pwf(discount_rate=None, design_life=None, is_initial=False, is_recurring=True, interval=1, + future_year=None): """ Calculates the Present Worth Factor (PWF) based on cost type. @@ -39,118 +35,109 @@ def calculate_pwf(discount_rate, design_life, is_initial=False, is_recurring=Tru """ if is_initial: return 1.0 # Initial cost has no discounting - elif not is_recurring: if future_year is None: raise ValueError("future_year must be provided for non-recurring future costs") return 1 / ((1 + discount_rate) ** future_year) - else: - if interval is None: - raise ValueError("interval must be provided for recurring costs") return sum(1 / ((1 + discount_rate) ** (i * interval)) for i in range(1, int(design_life / interval) + 1)) class InitialConstructionCost(CostComponent): def __init__(self, quantity, rate): - super().__init__(amount=quantity * rate, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) + super().__init__() self.quantity = quantity self.rate = rate + self.category = "Economic" + self.is_initial = True + self.is_recurring = False + self.pwf = calculate_pwf(is_initial=True) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.quantity * self.rate * self.pwf + return self.quantity * self.rate class InitialCarbonEmissionCost(CostComponent): def __init__(self, material_quantity, carbon_emission_factor, carbon_cost): - super().__init__(amount=(material_quantity * carbon_emission_factor) * carbon_cost, category="Environmental", is_initial=True, is_recurring=False, pwf=1.00) + super().__init__() + self.material_quantity = material_quantity + self.carbon_emission_factor = carbon_emission_factor + self.carbon_cost = carbon_cost + self.category = "Environmental" + self.is_initial = True + self.is_recurring = False + self.pwf = calculate_pwf(is_initial=True) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount - - -class TimeCost(CostComponent): - def __init__(self, construction_cost, interest_rate, time, investment_ratio): - cost = construction_cost * interest_rate * time * investment_ratio - super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) - - def calculate_cost(self): - return self.amount - - -class RoadUserCost(CostComponent): - def __init__(self, vehicles_affected, vehicle_operation_cost, construction_time): - cost = vehicles_affected * vehicle_operation_cost * construction_time - super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) - - def calculate_cost(self): - return self.amount - - -class AdditionalCarbonEmissionCost(CostComponent): - def __init__(self, vehicles_affected, reroute_distance, co2_emission_per_km, carbon_cost): - cost = vehicles_affected * reroute_distance * co2_emission_per_km * carbon_cost - super().__init__(amount=cost, category="Environmental", is_initial=True, is_recurring=False, pwf=1.00) - - def calculate_cost(self): - return self.amount + return (self.material_quantity * self.carbon_emission_factor) * self.carbon_cost class PeriodicMaintenanceCost(CostComponent): - def __init__(self, maintenance_cost_rate, construction_cost, discount_rate, period, design_life): - pwf = calculate_pwf(discount_rate, period, design_life) - cost = maintenance_cost_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) + def __init__(self, maintenance_cost_rate, construction_cost, discount_rate, interval, design_life): + super().__init__() + self.maintenance_cost_rate = maintenance_cost_rate + self.construction_cost = construction_cost + self.discount_rate = discount_rate + self.interval = interval + self.design_life = design_life + self.category = "Economic" + self.is_initial = False + self.is_recurring = True + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=True, interval=interval) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount - - -class PeriodicMaintenanceCarbonCost(CostComponent): - def __init__(self, material_quantity, carbon_emission_factor, carbon_cost, discount_rate, period, design_life): - pwf = calculate_pwf(discount_rate, period, design_life) - cost = material_quantity * carbon_emission_factor * carbon_cost * pwf - super().__init__(amount=cost, category="Environmental", is_initial=False, is_recurring=True, pwf=pwf) - - def calculate_cost(self): - return self.amount + return self.maintenance_cost_rate * self.construction_cost * self.pwf class RoutineInspectionCost(CostComponent): def __init__(self, quantity, rate, discount_rate, design_life): - pwf = calculate_pwf(discount_rate, 1, design_life) - cost = quantity * rate * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) - - def calculate_cost(self): - return self.amount - - -class RepairAndRehabilitationCost(CostComponent): - def __init__(self, repair_cost_rate, construction_cost, discount_rate, period, design_life): - pwf = calculate_pwf(discount_rate, period, design_life) - cost = repair_cost_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) + super().__init__() + self.quantity = quantity + self.rate = rate + self.discount_rate = discount_rate + self.design_life = design_life + self.category = "Economic" + self.is_initial = False + self.is_recurring = True + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=True) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount + return self.quantity * self.rate * self.pwf class DemolitionCost(CostComponent): def __init__(self, demolition_rate, construction_cost, discount_rate, design_life): - pwf = 1 / ((1 + discount_rate) ** design_life) - cost = demolition_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, pwf=pwf) + super().__init__() + self.demolition_rate = demolition_rate + self.construction_cost = construction_cost + self.discount_rate = discount_rate + self.design_life = design_life + self.category = "Economic" + self.is_initial = False + self.is_recurring = False + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=False, future_year=design_life) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount + return self.demolition_rate * self.construction_cost * self.pwf class RecyclingCost(CostComponent): def __init__(self, scrap_value, quantity, discount_rate, design_life): - pwf = 1 / ((1 + discount_rate) ** design_life) - cost = scrap_value * quantity * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, pwf=pwf) + super().__init__() + self.scrap_value = scrap_value + self.quantity = quantity + self.discount_rate = discount_rate + self.design_life = design_life + self.category = "Economic" + self.is_initial = False + self.is_recurring = False + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=False, future_year=design_life) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount + return self.scrap_value * self.quantity * self.pwf From debcb9fe2adcfe86eb9158175f63d607f22473d6 Mon Sep 17 00:00:00 2001 From: ajmalbabums Date: Fri, 28 Mar 2025 12:00:37 +0530 Subject: [PATCH 07/12] installation setup --- install.bat | 9 ++++++++- scripts/verify_installation.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/install.bat b/install.bat index 5a1a326..312ff7a 100644 --- a/install.bat +++ b/install.bat @@ -1,6 +1,13 @@ @echo off echo Creating and activating Conda environment... -call conda activate osbridgelcca || conda create -n osbridgelcca python=3.9 -y && conda activate osbridgelcca + +REM Try activating the environment +call conda activate osbridgelcca 2>nul +if errorlevel 1 ( + echo Environment not found. Creating it now... + call conda create -n osbridgelcca python=3.9 -y + call conda activate osbridgelcca +) echo Installing dependencies from pyproject.toml... pip install . diff --git a/scripts/verify_installation.py b/scripts/verify_installation.py index 9e6afe4..8ceaa8c 100644 --- a/scripts/verify_installation.py +++ b/scripts/verify_installation.py @@ -1,5 +1,6 @@ import importlib import sys +sys.stdout.reconfigure(encoding='utf-8') # List of required packages REQUIRED_PACKAGES = [ From 41c839feedb9e1be63948f4da5ddb280d4cf512d Mon Sep 17 00:00:00 2001 From: ajmalbabums Date: Fri, 28 Mar 2025 12:01:30 +0530 Subject: [PATCH 08/12] lcc cost components --- src/osbridgelcca/core/bridge_lcc.py | 75 +++++++++-- src/osbridgelcca/core/cost_components.py | 163 +++++++++++------------ 2 files changed, 142 insertions(+), 96 deletions(-) diff --git a/src/osbridgelcca/core/bridge_lcc.py b/src/osbridgelcca/core/bridge_lcc.py index 938ff31..1b9138c 100644 --- a/src/osbridgelcca/core/bridge_lcc.py +++ b/src/osbridgelcca/core/bridge_lcc.py @@ -1,25 +1,84 @@ +import pandas as pd +from input_data import Input +from cost_components import CostComponent +from src.osbridgelcca.core.cost_components import InitialConstructionCost, InitialCarbonEmissionCost, TimeCost, \ + RoadUserCost + + class BridgeLCC: """Main class for handling Life Cycle Cost Analysis of bridges.""" - - def __init__(self, project_name, inputs): + + def __init__(self, project_name: str, inputs: Input): self.project_name = project_name - self.inputs = inputs # input object + self.inputs = inputs # Expecting an instance of Input class + self.outputs = {} # Initialize output storage def calculate_lcc(self): """Calculate the total life cycle cost based on input parameters.""" try: - material_cost = sum(self.inputs["bill_of_quantity"].values()) - maintenance_cost = self.inputs["maintenance_cost"] - operation_cost = self.inputs["vehicle_operating_cost"] - discount_rate = self.inputs["discount_rate"] + material_cost = self.inputs.material_data.quantity * self.inputs.material_data.unit_rate + maintenance_cost = self.inputs.maintenance_data.periodic_maintenance_rate + operation_cost = self.inputs.traffic_data.annual_traffic_increase * 5000 # Example formula + discount_rate = self.inputs.finance_data.discount_rate / 100 # Convert percentage to decimal # Simple NPV calculation total_cost = (material_cost + maintenance_cost + operation_cost) / (1 + discount_rate) self.outputs["total_lcc"] = total_cost return total_cost - except KeyError as e: + except AttributeError as e: raise ValueError(f"Missing input parameter: {e}") def get_outputs(self): """Return computed outputs.""" return self.outputs + + def tabulate_cost_components(self): + """Create a Pandas DataFrame for cost components.""" + data = { + "Sl. No.": list(range(1, 12)) + ["Total"], + "Cost Component": [ + "Initial costs", "Initial carbon emissions costs", "Time costs", + "Road user costs due to re-routing", "Carbon emission cost due to re-routing", + "Periodic maintenance costs", "Periodic maintenance carbon emissions costs", + "Annual routine inspection costs", "Repair and rehabilitation costs", + "Demolition and disposal costs", "Recycling costs", "Total life cycle cost" + ], + "Cost Amount": [InitialConstructionCost.amount, + InitialCarbonEmissionCost.amount, + TimeCost.amount, + RoadUserCost.amount, + 82.62, 19.36, 1.5, 17.68, 13.8, 0, 0.84, -2.67, 212.81], + "Life Cycle Stage": [ + "Initial", "Initial", "Initial", "Initial", "Initial", + "Use", "Use", "Use", "Use", + "End", "End", "Total" + ], + "Cost Type": [ + "Economic", "Environmental", "Social", "Social", "Environmental", + "Economic", "Environmental", "Economic", "Economic", + "Economic", "Economic", "Total" + ] + } + + df = pd.DataFrame(data) + return df + +# Example Usage +if __name__ == "__main__": + from input_data import MaterialData, FinanceData, TrafficData, MaintenanceData, RepairData, DemolitionData, RecycleData, CarbonEmissionData + + material = MaterialData("Steel", "Reinforcement bars", 5000, "kg", 60, "Govt Schedule", 32, 2.5, "EPD Source", 5, 90) + finance = FinanceData(5.0, 7.5, 1.2) + carbon = CarbonEmissionData("SSP2", "RCP6", 1200) + traffic = TrafficData(15, 4000, "Plain", "Urban", 10, {"Car": 50, "Truck": 30, "Bus": 20}) + maintenance = MaintenanceData(0.55, 1.0, 10, 5, 1) + repair = RepairData("Component-wise repair details") + demolition = DemolitionData(10) + recycle = RecycleData(80, 10000, 5000) + + project_input = Input(material, finance, carbon, traffic, maintenance, repair, demolition, recycle) + bridge = BridgeLCC("Bridge A", project_input) + + print("Total LCC:", bridge.calculate_lcc()) + print("\nCost Components Table:") + print(bridge.tabulate_cost_components()) diff --git a/src/osbridgelcca/core/cost_components.py b/src/osbridgelcca/core/cost_components.py index b08a90c..155cba9 100644 --- a/src/osbridgelcca/core/cost_components.py +++ b/src/osbridgelcca/core/cost_components.py @@ -1,31 +1,27 @@ from abc import ABC, abstractmethod + class CostComponent(ABC): """Abstract Base Class for different cost components in Life Cycle Cost Analysis.""" - def __init__(self, amount, category, is_initial, is_recurring, pwf): - """ - Initialize a generic cost component. - - :param amount: Cost amount in INR - :param category: Economic, Environmental, or Social - :param is_initial: True if an initial cost, False if future cost - :param is_recurring: True if recurring, False if one-time - :param pwf: Discounting factor for future costs - """ - self.amount = None - self.category = category - self.is_initial = None - self.is_recurring = None - self.pwf = None + def __init__(self): + self.amount = 0 # To be calculated in subclasses + self.category = None # Defined in subclass + self.is_initial = None # Defined in subclass + self.is_recurring = None # Defined in subclass + self.pwf = None # To be calculated in subclasses @abstractmethod def calculate_cost(self): """Abstract method to be implemented by subclasses for cost calculation.""" pass + def __str__(self): + return f"{self.__class__.__name__}: Amount = {self.amount:.2f}, Category = {self.category}" + -def calculate_pwf(discount_rate, design_life, is_initial=False, is_recurring=True, interval=1, future_year=None): +def calculate_pwf(discount_rate=None, design_life=None, is_initial=False, is_recurring=True, interval=1, + future_year=None): """ Calculates the Present Worth Factor (PWF) based on cost type. @@ -39,118 +35,109 @@ def calculate_pwf(discount_rate, design_life, is_initial=False, is_recurring=Tru """ if is_initial: return 1.0 # Initial cost has no discounting - elif not is_recurring: if future_year is None: raise ValueError("future_year must be provided for non-recurring future costs") return 1 / ((1 + discount_rate) ** future_year) - else: - if interval is None: - raise ValueError("interval must be provided for recurring costs") return sum(1 / ((1 + discount_rate) ** (i * interval)) for i in range(1, int(design_life / interval) + 1)) class InitialConstructionCost(CostComponent): def __init__(self, quantity, rate): - super().__init__(amount=quantity * rate, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) + super().__init__() self.quantity = quantity self.rate = rate + self.category = "Economic" + self.is_initial = True + self.is_recurring = False + self.pwf = calculate_pwf(is_initial=True) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.quantity * self.rate * self.pwf + return self.quantity * self.rate class InitialCarbonEmissionCost(CostComponent): def __init__(self, material_quantity, carbon_emission_factor, carbon_cost): - super().__init__(amount=(material_quantity * carbon_emission_factor) * carbon_cost, category="Environmental", is_initial=True, is_recurring=False, pwf=1.00) + super().__init__() + self.material_quantity = material_quantity + self.carbon_emission_factor = carbon_emission_factor + self.carbon_cost = carbon_cost + self.category = "Environmental" + self.is_initial = True + self.is_recurring = False + self.pwf = calculate_pwf(is_initial=True) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount - - -class TimeCost(CostComponent): - def __init__(self, construction_cost, interest_rate, time, investment_ratio): - cost = construction_cost * interest_rate * time * investment_ratio - super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) - - def calculate_cost(self): - return self.amount - - -class RoadUserCost(CostComponent): - def __init__(self, vehicles_affected, vehicle_operation_cost, construction_time): - cost = vehicles_affected * vehicle_operation_cost * construction_time - super().__init__(amount=cost, category="Economic", is_initial=True, is_recurring=False, pwf=1.00) - - def calculate_cost(self): - return self.amount - - -class AdditionalCarbonEmissionCost(CostComponent): - def __init__(self, vehicles_affected, reroute_distance, co2_emission_per_km, carbon_cost): - cost = vehicles_affected * reroute_distance * co2_emission_per_km * carbon_cost - super().__init__(amount=cost, category="Environmental", is_initial=True, is_recurring=False, pwf=1.00) - - def calculate_cost(self): - return self.amount + return (self.material_quantity * self.carbon_emission_factor) * self.carbon_cost class PeriodicMaintenanceCost(CostComponent): - def __init__(self, maintenance_cost_rate, construction_cost, discount_rate, period, design_life): - pwf = calculate_pwf(discount_rate, period, design_life) - cost = maintenance_cost_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) + def __init__(self, maintenance_cost_rate, construction_cost, discount_rate, interval, design_life): + super().__init__() + self.maintenance_cost_rate = maintenance_cost_rate + self.construction_cost = construction_cost + self.discount_rate = discount_rate + self.interval = interval + self.design_life = design_life + self.category = "Economic" + self.is_initial = False + self.is_recurring = True + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=True, interval=interval) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount - - -class PeriodicMaintenanceCarbonCost(CostComponent): - def __init__(self, material_quantity, carbon_emission_factor, carbon_cost, discount_rate, period, design_life): - pwf = calculate_pwf(discount_rate, period, design_life) - cost = material_quantity * carbon_emission_factor * carbon_cost * pwf - super().__init__(amount=cost, category="Environmental", is_initial=False, is_recurring=True, pwf=pwf) - - def calculate_cost(self): - return self.amount + return self.maintenance_cost_rate * self.construction_cost * self.pwf class RoutineInspectionCost(CostComponent): def __init__(self, quantity, rate, discount_rate, design_life): - pwf = calculate_pwf(discount_rate, 1, design_life) - cost = quantity * rate * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) - - def calculate_cost(self): - return self.amount - - -class RepairAndRehabilitationCost(CostComponent): - def __init__(self, repair_cost_rate, construction_cost, discount_rate, period, design_life): - pwf = calculate_pwf(discount_rate, period, design_life) - cost = repair_cost_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, pwf=pwf) + super().__init__() + self.quantity = quantity + self.rate = rate + self.discount_rate = discount_rate + self.design_life = design_life + self.category = "Economic" + self.is_initial = False + self.is_recurring = True + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=True) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount + return self.quantity * self.rate * self.pwf class DemolitionCost(CostComponent): def __init__(self, demolition_rate, construction_cost, discount_rate, design_life): - pwf = 1 / ((1 + discount_rate) ** design_life) - cost = demolition_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, pwf=pwf) + super().__init__() + self.demolition_rate = demolition_rate + self.construction_cost = construction_cost + self.discount_rate = discount_rate + self.design_life = design_life + self.category = "Economic" + self.is_initial = False + self.is_recurring = False + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=False, future_year=design_life) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount + return self.demolition_rate * self.construction_cost * self.pwf class RecyclingCost(CostComponent): def __init__(self, scrap_value, quantity, discount_rate, design_life): - pwf = 1 / ((1 + discount_rate) ** design_life) - cost = scrap_value * quantity * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, pwf=pwf) + super().__init__() + self.scrap_value = scrap_value + self.quantity = quantity + self.discount_rate = discount_rate + self.design_life = design_life + self.category = "Economic" + self.is_initial = False + self.is_recurring = False + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=False, future_year=design_life) + self.amount = self.calculate_cost() def calculate_cost(self): - return self.amount + return self.scrap_value * self.quantity * self.pwf From 30b787f6804d44a66a71c54855a0759df1f6b7c5 Mon Sep 17 00:00:00 2001 From: ajmalbabums Date: Fri, 28 Mar 2025 14:26:21 +0530 Subject: [PATCH 09/12] construction cost and emission cost equations with panda table --- src/osbridgelcca/core/cost_components.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/osbridgelcca/core/cost_components.py b/src/osbridgelcca/core/cost_components.py index 155cba9..9e54c22 100644 --- a/src/osbridgelcca/core/cost_components.py +++ b/src/osbridgelcca/core/cost_components.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +import pandas as pd class CostComponent(ABC): @@ -44,10 +45,9 @@ def calculate_pwf(discount_rate=None, design_life=None, is_initial=False, is_rec class InitialConstructionCost(CostComponent): - def __init__(self, quantity, rate): + def __init__(self, material_boq: pd.DataFrame): super().__init__() - self.quantity = quantity - self.rate = rate + self.material_boq = material_boq self.category = "Economic" self.is_initial = True self.is_recurring = False @@ -55,14 +55,13 @@ def __init__(self, quantity, rate): self.amount = self.calculate_cost() def calculate_cost(self): - return self.quantity * self.rate + return (self.material_boq.iloc[:, 2] * self.material_boq.iloc[:, 3]).sum() class InitialCarbonEmissionCost(CostComponent): - def __init__(self, material_quantity, carbon_emission_factor, carbon_cost): + def __init__(self, material_boq: pd.DataFrame, carbon_cost): super().__init__() - self.material_quantity = material_quantity - self.carbon_emission_factor = carbon_emission_factor + self.material_boq = material_boq self.carbon_cost = carbon_cost self.category = "Environmental" self.is_initial = True @@ -71,7 +70,7 @@ def __init__(self, material_quantity, carbon_emission_factor, carbon_cost): self.amount = self.calculate_cost() def calculate_cost(self): - return (self.material_quantity * self.carbon_emission_factor) * self.carbon_cost + return self.carbon_cost * (self.material_boq.iloc[:, 2] * self.material_boq.iloc[:, 4]).sum() class PeriodicMaintenanceCost(CostComponent): From 840aa6190c29b1b542d8bd7c3103f4c9b622ab3c Mon Sep 17 00:00:00 2001 From: ajmalbabums Date: Fri, 28 Mar 2025 17:10:00 +0530 Subject: [PATCH 10/12] more cost components --- src/osbridgelcca/core/cost_components.py | 73 +++++++++++++++++++++++- tests/unit_tests/test_cost_components.py | 6 +- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/osbridgelcca/core/cost_components.py b/src/osbridgelcca/core/cost_components.py index 9e54c22..cdf9c78 100644 --- a/src/osbridgelcca/core/cost_components.py +++ b/src/osbridgelcca/core/cost_components.py @@ -73,6 +73,57 @@ def calculate_cost(self): return self.carbon_cost * (self.material_boq.iloc[:, 2] * self.material_boq.iloc[:, 4]).sum() +class TimeCost(CostComponent): + def __init__(self, construction_cost, interest_rate, construction_time, investment_ratio): + super().__init__() + self.construction_cost = construction_cost + self.interest_rate = interest_rate + self.construction_time = construction_time + self.investment_ratio = investment_ratio + self.category = "Economic" + self.is_initial = True + self.is_recurring = False + self.pwf = calculate_pwf(is_initial=True) + self.amount = self.calculate_cost() + + def calculate_cost(self): + return self.construction_cost * self.interest_rate * self.construction_time * self.investment_ratio + + +class RoadUserCost(CostComponent): + def __init__(self, vehicles_affected, vehicle_operation_cost, construction_time): + super().__init__() + self.vehicles_affected = vehicles_affected + self.vehicle_operation_cost = vehicle_operation_cost + self.construction_time = construction_time + self.delay_cost = 0.00 + self.category = "Economic" + self.is_initial = True + self.is_recurring = False + self.pwf = 1.0 + self.amount = self.calculate_cost() + + def calculate_cost(self): + return self.vehicles_affected * (self.vehicle_operation_cost + self.delay_cost) * self.construction_time + + +class ReroutingCarbonEmissionCost(CostComponent): + def __init__(self, vehicles_affected, reroute_distance, co2_emission_per_km, carbon_cost): + super().__init__() + self.vehicles_affected = vehicles_affected + self.reroute_distance = reroute_distance + self.co2_emission_per_km = co2_emission_per_km + self.carbon_cost = carbon_cost + self.category = "Environmental" + self.is_initial = True + self.is_recurring = False + self.pwf = 1.0 + self.amount = self.calculate_cost() + + def calculate_cost(self): + return self.vehicles_affected * self.reroute_distance * self.co2_emission_per_km * self.carbon_cost + + class PeriodicMaintenanceCost(CostComponent): def __init__(self, maintenance_cost_rate, construction_cost, discount_rate, interval, design_life): super().__init__() @@ -84,13 +135,33 @@ def __init__(self, maintenance_cost_rate, construction_cost, discount_rate, inte self.category = "Economic" self.is_initial = False self.is_recurring = True - self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=True, interval=interval) + self.pwf = calculate_pwf(discount_rate, design_life=self.design_life, is_recurring=self.is_recurring, + interval=interval) self.amount = self.calculate_cost() def calculate_cost(self): return self.maintenance_cost_rate * self.construction_cost * self.pwf +class PeriodicMaintenanceCarbonEmissionCost(CostComponent): + def __init__(self, material_quantity, carbon_emission_factor, carbon_cost, discount_rate, interval, design_life): + super().__init__() + self.material_quantity = material_quantity + self.carbon_emission_factor = carbon_emission_factor + self.carbon_cost = carbon_cost + self.discount_rate = discount_rate + self.interval = interval + self.design_life = design_life + self.category = "Environmental" + self.is_initial = False + self.is_recurring = True + self.pwf = calculate_pwf(discount_rate, design_life, is_recurring=True, interval=interval) + self.amount = self.calculate_cost() + + def calculate_cost(self): + return self.material_quantity * self.carbon_emission_factor * self.carbon_cost * self.pwf + + class RoutineInspectionCost(CostComponent): def __init__(self, quantity, rate, discount_rate, design_life): super().__init__() diff --git a/tests/unit_tests/test_cost_components.py b/tests/unit_tests/test_cost_components.py index cb235e3..9f0fc0b 100644 --- a/tests/unit_tests/test_cost_components.py +++ b/tests/unit_tests/test_cost_components.py @@ -4,7 +4,7 @@ InitialCarbonEmissionCost, TimeCost, RoadUserCost, - AdditionalCarbonEmissionCost, + ReroutingCarbonEmissionCost, PeriodicMaintenanceCost, PeriodicMaintenanceCarbonCost, RoutineInspectionCost, @@ -22,7 +22,7 @@ def test_initial_carbon_emission_cost(): assert cost.calculate_cost() == 2000 def test_time_cost(): - cost = TimeCost(construction_cost=10000, interest_rate=0.05, time=2, investment_ratio=0.8) + cost = TimeCost(construction_cost=10000, interest_rate=0.05, construction_time=2, investment_ratio=0.8) assert cost.calculate_cost() == 800.0 def test_road_user_cost(): @@ -30,7 +30,7 @@ def test_road_user_cost(): assert cost.calculate_cost() == 10000 def test_additional_carbon_emission_cost(): - cost = AdditionalCarbonEmissionCost(vehicles_affected=1000, reroute_distance=10, co2_emission_per_km=0.5, carbon_cost=20) + cost = ReroutingCarbonEmissionCost(vehicles_affected=1000, reroute_distance=10, co2_emission_per_km=0.5, carbon_cost=20) assert cost.calculate_cost() == 100000.0 def test_periodic_maintenance_cost(): From afa2076179bb1ca873fe6ccfaddb028f6927d1ab Mon Sep 17 00:00:00 2001 From: Souhridya Patra Date: Fri, 6 Jun 2025 20:51:50 +0530 Subject: [PATCH 11/12] Databse structure created --- src/osbridgelcca/backend/config.py | 21 +++++++ src/osbridgelcca/backend/database.py | 28 +++++++++ src/osbridgelcca/backend/main.py | 4 +- src/osbridgelcca/backend/models.py | 92 ++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/osbridgelcca/backend/database.py create mode 100644 src/osbridgelcca/backend/models.py diff --git a/src/osbridgelcca/backend/config.py b/src/osbridgelcca/backend/config.py index 5aea750..3d78f4d 100644 --- a/src/osbridgelcca/backend/config.py +++ b/src/osbridgelcca/backend/config.py @@ -1 +1,22 @@ # Placeholder for app configuration + +import os +from pathlib import Path + +# Base directory of the project +BASE_DIR = Path(__file__).resolve().parent.parent.parent + +# Database configuration +DATABASE_URL = os.getenv('DATABASE_URL', f'sqlite:///{BASE_DIR}/data/lcca.db') + +# Create data directory if it doesn't exist +DATA_DIR = BASE_DIR / 'data' +DATA_DIR.mkdir(exist_ok=True) + +# Database configuration options +DB_CONFIG = { + 'pool_size': 5, + 'max_overflow': 10, + 'pool_timeout': 30, + 'pool_recycle': 1800 +} diff --git a/src/osbridgelcca/backend/database.py b/src/osbridgelcca/backend/database.py new file mode 100644 index 0000000..b239887 --- /dev/null +++ b/src/osbridgelcca/backend/database.py @@ -0,0 +1,28 @@ +import os +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import QueuePool +from .models import Base +from .config import DATABASE_URL, DB_CONFIG + +# Create engine with connection pooling +engine = create_engine( + DATABASE_URL, + poolclass=QueuePool, + **DB_CONFIG +) + +# Create session factory +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +def get_db(): + """Get database session""" + db = SessionLocal() + try: + yield db + finally: + db.close() + +def init_database(): + """Initialize database tables""" + Base.metadata.create_all(bind=engine) \ No newline at end of file diff --git a/src/osbridgelcca/backend/main.py b/src/osbridgelcca/backend/main.py index daa4a22..d768a4c 100644 --- a/src/osbridgelcca/backend/main.py +++ b/src/osbridgelcca/backend/main.py @@ -1 +1,3 @@ -# Placeholder for Flask API entry point +from osbridgelcca.backend.database import init_database + +init_database() diff --git a/src/osbridgelcca/backend/models.py b/src/osbridgelcca/backend/models.py new file mode 100644 index 0000000..c99a675 --- /dev/null +++ b/src/osbridgelcca/backend/models.py @@ -0,0 +1,92 @@ +from sqlalchemy import create_engine, Column, Integer, Float, String, ForeignKey, DateTime, JSON +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship +from datetime import datetime, UTC + +Base = declarative_base() + +class MaterialRate(Base): + """Regional schedule of rates for materials""" + __tablename__ = 'material_rates' + + id = Column(Integer, primary_key=True) + material_name = Column(String(100), nullable=False) + region = Column(String(50), nullable=False) + unit = Column(String(20), nullable=False) + rate = Column(Float, nullable=False) + currency = Column(String, nullable=False) + last_updated = Column(DateTime, default=lambda: datetime.now(UTC)) + +class ScrapValue(Base): + """Scrap values and recyclability percentages""" + __tablename__ = 'scrap_values' + + id = Column(Integer, primary_key=True) + material_name = Column(String(100), nullable=False) + scrap_value = Column(Float, nullable=False) + recyclability_percentage = Column(Float, nullable=False) + currency = Column(String, nullable=False) + last_updated = Column(DateTime, default=lambda: datetime.now(UTC)) + +class EnvironmentalImpact(Base): + """Environmental impact data from EPDs""" + __tablename__ = 'environmental_impacts' + + id = Column(Integer, primary_key=True) + material_name = Column(String(100), nullable=False) + epd_id = Column(String(50), nullable=False) + global_warming_potential = Column(Float, nullable=False) # kg CO2 eq + water_consumption = Column(Float) # m³ + energy_consumption = Column(Float) # MJ + waste_generation = Column(Float) # kg + last_updated = Column(DateTime, default=lambda: datetime.now(UTC)) + +class SocialCostOfCarbon(Base): + """Country-specific social cost of carbon""" + __tablename__ = 'social_cost_of_carbon' + + id = Column(Integer, primary_key=True) + country = Column(String(50), nullable=False) + year = Column(Integer, nullable=False) + cost_per_ton = Column(Float, nullable=False) # USD per ton CO2 + currency = Column(String, nullable=False) + last_updated = Column(DateTime, default=lambda: datetime.now(UTC)) + +class TransportEmissionFactor(Base): + """Road transport carbon emission factors""" + __tablename__ = 'transport_emission_factors' + + id = Column(Integer, primary_key=True) + vehicle_type = Column(String(50), nullable=False) + emission_factor = Column(Float, nullable=False) # kg CO2 per km + load_factor = Column(Float) # Average load factor + last_updated = Column(DateTime, default=lambda: datetime.now(UTC)) + +class VehicleOperationCost(Base): + """Vehicle operation costs""" + __tablename__ = 'vehicle_operation_costs' + + id = Column(Integer, primary_key=True) + vehicle_type = Column(String(50), nullable=False) + fuel_cost_per_km = Column(Float, nullable=False) + maintenance_cost_per_km = Column(Float, nullable=False) + insurance_cost_per_year = Column(Float, nullable=False) + currency = Column(String(3), nullable=False) + last_updated = Column(DateTime, default=lambda: datetime.now(UTC)) + +class UserAnalysis(Base): + """User analyses storage""" + __tablename__ = 'user_analyses' + + id = Column(Integer, primary_key=True) + user_id = Column(String(50), nullable=False) + analysis_name = Column(String(100), nullable=False) + analysis_data = Column(JSON, nullable=False) + created_at = Column(DateTime, default=lambda: datetime.now(UTC)) + updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC)) + +def init_db(db_url): + """Initialize the database""" + engine = create_engine(db_url) + Base.metadata.create_all(engine) + return engine \ No newline at end of file From a3e64358acbaa8a50f015dfa226260f83e85c86f Mon Sep 17 00:00:00 2001 From: Souhridya Patra Date: Sun, 8 Jun 2025 23:41:11 +0530 Subject: [PATCH 12/12] GUI Version-1.0 --- src/osbridgelcca/desktop_app/app.py | 897 ++++++++++++++++ ...b7798a84ffe0528bdec7dc8efe8fb1c89f77ae.png | Bin 0 -> 65874 bytes ...ff94ca74c504343b797700f5d515a40ab72143.png | Bin 0 -> 72221 bytes .../assets/MainWindow_icons/Alert Circle.png | Bin 0 -> 205 bytes .../assets/MainWindow_icons/Contact.png | Bin 0 -> 283 bytes .../assets/MainWindow_icons/Dismiss.png | Bin 0 -> 287 bytes .../assets/MainWindow_icons/Export.png | Bin 0 -> 290 bytes .../assets/MainWindow_icons/Icon.png | Bin 0 -> 1093 bytes .../assets/MainWindow_icons/Vector (1).png | Bin 0 -> 223 bytes .../assets/MainWindow_icons/Vector (2).png | Bin 0 -> 212 bytes .../assets/MainWindow_icons/Vector (3).png | Bin 0 -> 206 bytes .../assets/MainWindow_icons/Vector (4).png | Bin 0 -> 202 bytes .../assets/MainWindow_icons/Vector.png | Bin 0 -> 214 bytes .../play_arrow_filled (1).png | Bin 0 -> 262 bytes .../MainWindow_icons/play_arrow_filled.png | Bin 0 -> 257 bytes ...7\246\206 icon _People Community_ (1).png" | Bin 0 -> 301 bytes ...0\237\246\206 icon _People Community_.png" | Bin 0 -> 301 bytes ...60\237\246\206 icon _Person Feedback_.png" | Bin 0 -> 273 bytes ...\206 icon _document save as template_.png" | Bin 0 -> 318 bytes .../\360\237\246\206 icon _file copy_.png" | Bin 0 -> 230 bytes ...237\246\206 icon _save action floppy_.png" | Bin 0 -> 241 bytes .../\360\237\246\206 icon _youtube_.png" | Bin 0 -> 202 bytes .../assets/country_abbreviations.csv | 171 +++ .../desktop_app/assets/general_info.json | 1 + .../desktop_app/assets/years_2000_to_2025.csv | 27 + src/osbridgelcca/desktop_app/ui/input_form.py | 6 + ...jectDetails_BridgeANDTrafficData_Window.py | 675 ++++++++++++ ...rojectDetails_CarbonEmissionData_Window.py | 987 ++++++++++++++++++ ...tails_DemolitionANDRecyclingData_Window.py | 486 +++++++++ .../ProjectDetails_FinancialData_Window.py | 520 +++++++++ .../ProjectDetails_Foundation_Window.py | 764 ++++++++++++++ ...Details_MaintenanceANDRepairData_Window.py | 571 ++++++++++ .../ProjectDetails_Miscellaneous_Window.py | 743 +++++++++++++ .../ProjectDetails_SubStructure_Window.py | 723 +++++++++++++ .../ProjectDetails_SuperStructure_Window.py | 742 +++++++++++++ 35 files changed, 7313 insertions(+) create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/08b7798a84ffe0528bdec7dc8efe8fb1c89f77ae.png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/78ff94ca74c504343b797700f5d515a40ab72143.png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Alert Circle.png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Contact.png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Dismiss.png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Export.png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Icon.png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (1).png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (2).png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (3).png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (4).png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector.png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled (1).png create mode 100644 src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled.png create mode 100644 "src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _People Community_ (1).png" create mode 100644 "src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _People Community_.png" create mode 100644 "src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _Person Feedback_.png" create mode 100644 "src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _document save as template_.png" create mode 100644 "src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _file copy_.png" create mode 100644 "src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _save action floppy_.png" create mode 100644 "src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _youtube_.png" create mode 100644 src/osbridgelcca/desktop_app/assets/country_abbreviations.csv create mode 100644 src/osbridgelcca/desktop_app/assets/general_info.json create mode 100644 src/osbridgelcca/desktop_app/assets/years_2000_to_2025.csv create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_BridgeANDTrafficData_Window.py create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_CarbonEmissionData_Window.py create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_DemolitionANDRecyclingData_Window.py create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_FinancialData_Window.py create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_Foundation_Window.py create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_MaintenanceANDRepairData_Window.py create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_Miscellaneous_Window.py create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_SubStructure_Window.py create mode 100644 src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_SuperStructure_Window.py diff --git a/src/osbridgelcca/desktop_app/app.py b/src/osbridgelcca/desktop_app/app.py index 62e7ee9..2257fd7 100644 --- a/src/osbridgelcca/desktop_app/app.py +++ b/src/osbridgelcca/desktop_app/app.py @@ -1,3 +1,899 @@ +import sys +import csv +import json +from PyQt5 import QtWidgets +from ui.input_form import save_general_info +from ui.project_details_windows.ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog +from ui.project_details_windows.ProjectDetails_Foundation_Window import Ui_Foundation_Dialog +from ui.project_details_windows.ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog +from ui.project_details_windows.ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog +from ui.project_details_windows.ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog +from ui.project_details_windows.ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog +from ui.project_details_windows.ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog +from ui.project_details_windows.ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog +from ui.project_details_windows.ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog +from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, + QHBoxLayout, QPushButton, QLabel, QTreeWidget, + QTreeWidgetItem, QTabBar, QFrame, QSplitter, + QToolBar, QAction, QGroupBox, QMenu, QLineEdit, + QComboBox, QMessageBox, QGridLayout) +from PyQt5.QtGui import QIcon, QFont +from PyQt5.QtCore import Qt, QSize + +class BICCAStudio(QMainWindow): + def openBridgeTrafficWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def __init__(self): + super().__init__() + self.setWindowTitle(" - BICCA Studio 1.0.0") + self.setGeometry(100, 40, 1440, 900) + self.setStyleSheet("background-color: #f5f5f5;") + + # Initialize tutorial page counter + self.current_tutorial_page = 1 + self.total_tutorial_pages = 4 + + # Tutorial content + self.tutorial_pages = [ + { + "page_number": "1/4", + "title": "Welcome to\nBICCA Studio", + "content": """ + BICCA Studio has a lot of features to offer. In the next few minutes, you'll learn how to use BICCA Studio efficiently, from setting up and managing projects, to navigating the user interface. This tutorial will guide you through essential features, including customization options, shortcuts, and export capabilities, ensuring a seamless workflow. Whether you're a beginner or an advanced user, this guide will help you unlock the full potential of BICCA Studio and enhance your productivity. + """ + }, + { + "page_number": "2/4", + "title": "Welcome to\nBICCA Studio", + "content": """ + The Project General Information page is the foundation of your project setup, allowing you to input essential details for accurate documentation and streamlined management. Here, you will provide key information starting with the Company Name, which represents the organization behind the project. Next is the Project Title, a concise name that defines the scope of work. The Project Description further elaborates on the objectives and purpose of the project. Additionally, you will need to enter the Name of the Valuer responsible for the valuation, along with the Job Number for easy reference. The Client field identifies the primary stakeholder of the project, while the Country specifies the project's geographical location. Finally, the Base Year establishes a reference period for analysis and reports. + """ + }, + { + "page_number": "3/4", + "title": "Understanding\nInput Parameters", + "content": """ + Input Parameters are crucial for accurate analysis and results. This section allows you to define various technical specifications, economic factors, and operational variables that will influence your project outcomes. You can specify factors such as time periods, growth rates, discount rates, and other numerical inputs that the software will use for calculations. Each parameter can be customized according to your specific requirements, ensuring that the analysis reflects real-world conditions accurately. The intuitive interface makes it easy to adjust these parameters as needed, and you can save different parameter sets for future use or comparisons. + """ + }, + { + "page_number": "4/4", + "title": "Working with\nOutputs", + "content": """ + The Outputs section displays the results of your analysis based on the information and parameters you've entered. Here you can view comprehensive reports, charts, and visualizations that present your data in meaningful ways. You can customize the output format according to your preferences or your client's requirements. BICCA Studio allows you to export these outputs in various formats including PDF, Excel, or as image files for easy sharing and presentation. Additionally, you can compare different scenarios by adjusting your inputs and generating new outputs, providing valuable insights for decision-making processes. + """ + } + ] + + # Create the main layout + self.central_widget = QWidget() + self.setCentralWidget(self.central_widget) + self.main_layout = QVBoxLayout(self.central_widget) + self.main_layout.setContentsMargins(0, 0, 0, 0) + self.main_layout.setSpacing(0) + + # Create menu bar with dropdown menus + self.create_menu_bar() + + # Create toolbar + self.create_toolbar() + + # Create window tabs + self.create_window_tabs() + + # Create main content area + self.create_content_area() + self.show_tutorials_only() # Show only tutorials at launch + + # Add status bar with Data button + self.create_status_bar() + + # Initially hide dropdown menus + self.file_menu_widget = QWidget(self) + self.file_menu_widget.hide() + self.help_menu_widget = QWidget(self) + self.help_menu_widget.hide() + + # Update tutorial content to first page + self.update_tutorial_content() + + + def create_menu_bar(self): + menubar = self.menuBar() + menubar.setObjectName("menubar") + menubar.setStyleSheet(""" + QMenuBar { + background-color: #4C9141; + color: white; + } + QMenuBar::item { + background-color: white; + color: black; + padding: 5px 25px; + } + QMenuBar::item:selected { + background-color: #e0e0e0; + } + """) + + # File menu + self.menuFile = QMenu("File", self) + self.menuFile.setObjectName("menuFile") + menubar.addMenu(self.menuFile) + + # Home menu + self.menuHome = QMenu("Home", self) + self.menuHome.setObjectName("menuHome") + menubar.addMenu(self.menuHome) + + # Reports menu + self.menuReports = QMenu("Reports", self) + self.menuReports.setObjectName("menuReports") + menubar.addMenu(self.menuReports) + + # Help menu + self.menuHelp = QMenu("Help", self) + self.menuHelp.setObjectName("menuHelp") + menubar.addMenu(self.menuHelp) + + # Actions with icons (update icon paths as needed) + from PyQt5.QtGui import QIcon, QPixmap + + def icon(path): + return QIcon(QPixmap(path)) + + self.actionNew = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector.png"), "New", self) + self.actionOpen = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (1).png"), "Open", self) + self.actionSave = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/🦆 icon _save action floppy_.png"), "Save", self) + self.actionSave_As = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/🦆 icon _document save as template_.png"), "Save As...", self) + self.actionCreate_a_Copy = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/🦆 icon _file copy_.png"), "Create a Copy", self) + self.actionPrint = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (2).png"), "Print", self) + self.actionRename = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (3).png"), "Rename", self) + self.actionExport = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Export.png"), "Export", self) + self.actionVersion_History = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (4).png"), "Version History", self) + self.actionInfo = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Alert Circle.png"), "Info", self) + self.actionContact_Us = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Contact.png"), "Contact Us", self) + self.actionFeedback = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/🦆 icon _Person Feedback_.png"), "Feedback", self) + self.actionVideo_Tutorials = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/🦆 icon _youtube_.png"), "Video Tutorials", self) + self.actionJoin_our_Community = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/🦆 icon _People Community_.png"), "Join our Community", self) + self.actionEdit = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/08b7798a84ffe0528bdec7dc8efe8fb1c89f77ae.png"), "Edit", self) + self.actionFile = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Icon.png"), "File", self) + self.actionOpen_File = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/78ff94ca74c504343b797700f5d515a40ab72143.png"), "Open File", self) + + # Add actions to File menu + self.menuFile.addAction(self.actionNew) + self.menuFile.addAction(self.actionOpen) + self.menuFile.addAction(self.actionSave) + self.menuFile.addAction(self.actionSave_As) + self.menuFile.addAction(self.actionCreate_a_Copy) + self.menuFile.addAction(self.actionPrint) + self.menuFile.addAction(self.actionRename) + self.menuFile.addAction(self.actionExport) + self.menuFile.addAction(self.actionVersion_History) + self.menuFile.addAction(self.actionInfo) + + # Add actions to Help menu + self.menuHelp.addAction(self.actionContact_Us) + self.menuHelp.addAction(self.actionFeedback) + self.menuHelp.addAction(self.actionVideo_Tutorials) + self.menuHelp.addAction(self.actionJoin_our_Community) + + # Optionally, add actions to toolbar if you want + # self.addToolBar(Qt.LeftToolBarArea, QToolBar(self)).addAction(self.actionEdit) + # self.addToolBar(Qt.LeftToolBarArea, QToolBar(self)).addAction(self.actionFile) + # self.addToolBar(Qt.LeftToolBarArea, QToolBar(self)).addAction(self.actionOpen_File) + + def toggle_file_menu(self): + # Close help menu if it's open + self.help_menu_widget.hide() + + # Toggle file menu + if self.file_menu_widget.isVisible(): + self.file_menu_widget.hide() + else: + self.file_menu_widget.show() + self.file_menu_widget.raise_() + + def toggle_help_menu(self): + # Close file menu if it's open + self.file_menu_widget.hide() + + # Toggle help menu + if self.help_menu_widget.isVisible(): + self.help_menu_widget.hide() + else: + self.help_menu_widget.show() + self.help_menu_widget.raise_() + + def create_toolbar(self): + toolbar = QToolBar("Main Toolbar") + toolbar.setMovable(False) + toolbar.setIconSize(QSize(20, 20)) + self.addToolBar(Qt.LeftToolBarArea, toolbar) + + # Use the same icons as in MainWindow.py (update paths if needed) + from PyQt5.QtGui import QIcon, QPixmap + + def icon(path): + return QIcon(QPixmap(path)) + + self.actionEdit = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/08b7798a84ffe0528bdec7dc8efe8fb1c89f77ae.png"), "Edit", self) + self.actionFile = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/Icon.png"), "File", self) + self.actionOpen_File = QAction(icon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/78ff94ca74c504343b797700f5d515a40ab72143.png"), "Open File", self) + + toolbar.addAction(self.actionEdit) + toolbar.addAction(self.actionFile) + toolbar.addAction(self.actionOpen_File) + toolbar.addSeparator() + + def create_window_tabs(self): + # Create a container widget for better alignment + window_tabs_container = QWidget() + container_layout = QHBoxLayout(window_tabs_container) + container_layout.setContentsMargins(0, 0, 0, 0) + + # Left spacer to push content to center + container_layout.addStretch(1) + + # Windows label with better spacing + windows_label = QLabel("Windows:") + windows_label.setStyleSheet("padding: 5px 10px 5px 0px;") + container_layout.addWidget(windows_label) + + # Store tab buttons for later use + self.tab_buttons = {} + + # Create tab buttons with better spacing + tabs = ["Tutorials", "Project Details", "Results", "Compare"] + for i, tab_name in enumerate(tabs): + tab_btn = QPushButton(tab_name) + tab_btn.setStyleSheet(""" + QPushButton { + background-color: #EEEEEE; + border: 1px solid #CCCCCC; + padding: 5px 10px; + margin: 0px 2px; + } + QPushButton:pressed { + background-color: #DDDDDD; + } + """) + container_layout.addWidget(tab_btn) + self.tab_buttons[tab_name] = tab_btn + # Connect each tab to its handler + if tab_name == "Tutorials": + tab_btn.clicked.connect(self.show_tutorials_only) + elif tab_name == "Project Details": + tab_btn.clicked.connect(self.show_project_details) + # Results and Compare can be connected later + + # Right spacer to push content to center + container_layout.addStretch(1) + + window_tabs_container.setFixedHeight(40) + self.main_layout.addWidget(window_tabs_container) + + def show_tutorials_only(self): + # Hide project details panel + self.project_panel.setVisible(False) + # Set tutorials panel max width to 771 + self.tutorials_panel.setMaximumWidth(771) + # Center the tutorials panel in the main window + self.splitter.setSizes([1, 0]) + # Optionally, expand tutorials to fill available space + self.tutorials_panel.setMinimumWidth(0) + self.tutorials_panel.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + + def show_project_details(self): + # Show both panels + self.project_panel.setVisible(True) + # Restore tutorials panel max width to 244 + self.tutorials_panel.setMaximumWidth(244) + self.tutorials_panel.setMinimumWidth(0) + self.tutorials_panel.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + # Reset splitter sizes for normal layout + self.splitter.setSizes([200, 750]) + + def create_content_area(self): + def get_country_name(filename = "src//osbridgelcca//desktop_app//assets//country_abbreviations.csv"): + country_names_lst = [] + + with open(filename, newline='', encoding='utf-8') as csvfile: + reader = csv.reader(csvfile) + next(reader) # Skiping header row + for row in reader: + country_names_lst.append(row[1]) + return country_names_lst + + def get_base_year(filename = "src//osbridgelcca//desktop_app//assets//years_2000_to_2025.csv"): + base_years_lst = [] + + with open(filename, newline='', encoding='utf-8') as csvfile: + reader = csv.reader(csvfile) + next(reader) # Skiping header row + for row in reader: + base_years_lst.append(row[0]) + return base_years_lst + + + self.splitter = QSplitter(Qt.Horizontal) + + # Left side - Tutorials panel + self.tutorials_panel = QWidget() + self.tutorials_panel.setMaximumWidth(771) # Start with max 771 for launch + tutorials_layout = QVBoxLayout(self.tutorials_panel) + tutorials_layout.setContentsMargins(0, 0, 0, 0) + + # Tutorial header + tutorials_header = QWidget() + tutorials_header.setStyleSheet("background-color: #f0e6e6;") + tutorials_header_layout = QHBoxLayout(tutorials_header) + tutorials_header_layout.setContentsMargins(5, 5, 5, 5) + + tutorials_label = QLabel("Tutorials") + close_btn = QPushButton("×") + close_btn.setFixedSize(20, 20) + close_btn.setStyleSheet("border: none;") + close_btn.clicked.connect(self.close_tutorials) + + tutorials_header_layout.addWidget(tutorials_label) + tutorials_header_layout.addStretch() + tutorials_header_layout.addWidget(close_btn) + + # Tutorial content with light pink background + self.tutorials_content = QWidget() + self.tutorials_content.setStyleSheet("background-color: #f9f0f0;") + self.tutorials_content_layout = QVBoxLayout(self.tutorials_content) + + # Create labels that will be updated dynamically + self.page_label = QLabel() + self.page_label.setAlignment(Qt.AlignCenter) + self.page_label.setStyleSheet("font-weight: bold; padding: 5px; border-bottom: 1px solid #ddd;") + + self.welcome_label = QLabel() + self.welcome_label.setAlignment(Qt.AlignCenter) + self.welcome_label.setStyleSheet("font-weight: bold; padding: 10px; border-bottom: 1px solid #ddd;") + + self.description_label = QLabel() + self.description_label.setWordWrap(True) + self.description_label.setStyleSheet("padding: 10px;") + + self.tutorials_content_layout.addWidget(self.page_label) + self.tutorials_content_layout.addWidget(self.welcome_label) + self.tutorials_content_layout.addWidget(self.description_label) + self.tutorials_content_layout.addStretch() + + # Tutorial navigation buttons + nav_buttons = QWidget() + nav_buttons.setStyleSheet("background-color: #f9f0f0;") + nav_layout = QHBoxLayout(nav_buttons) + nav_layout.setContentsMargins(10, 5, 10, 5) + + back_btn = QPushButton("Back") + back_btn.setStyleSheet(""" + QPushButton { + background-color: #f0f0f0; + border: 1px solid #ddd; + padding: 5px 15px; + border-radius: 3px; + } + """) + back_btn.clicked.connect(self.tutorial_back) + + next_btn = QPushButton("Next") + next_btn.setStyleSheet(""" + QPushButton { + background-color: #f0f0f0; + border: 1px solid #ddd; + padding: 5px 15px; + border-radius: 3px; + } + """) + next_btn.clicked.connect(self.tutorial_next) + + nav_layout.addWidget(back_btn) + nav_layout.addWidget(next_btn) + + tutorials_layout.addWidget(tutorials_header) + tutorials_layout.addWidget(self.tutorials_content) + tutorials_layout.addWidget(nav_buttons) + + # Right side - Project Details panel + self.project_panel = QWidget() + self.project_panel.setMaximumWidth(771) + project_layout = QVBoxLayout(self.project_panel) + project_layout.setContentsMargins(0, 0, 0, 0) + + # Project header + project_header = QWidget() + project_header.setStyleSheet("background-color: #f0f0f0;") + project_header_layout = QHBoxLayout(project_header) + project_header_layout.setContentsMargins(5, 5, 5, 5) + + project_label = QLabel("Project Details Window") + project_close_btn = QPushButton("×") + project_close_btn.setFixedSize(20, 20) + project_close_btn.setStyleSheet("font-weight: bold; border: none;") + project_close_btn.clicked.connect(self.close_project_details) + + project_header_layout.addWidget(project_label) + project_header_layout.addStretch() + project_header_layout.addWidget(project_close_btn) + + # Create collapsible sections + project_content = QWidget() + project_content_layout = QVBoxLayout(project_content) + project_content_layout.setContentsMargins(10, 10, 10, 10) + + # General Information section (collapsible) + general_info_box = QGroupBox() + general_info_box.setStyleSheet(""" + QGroupBox { + background-color: #f0e6e6; + border-radius: 3px; + margin-bottom: 5px; + } + """) + general_info_layout = QVBoxLayout(general_info_box) + general_info_layout.setContentsMargins(10, 10, 10, 10) + + # Collapsible header + self.general_info_btn = QPushButton("► General Information") + self.general_info_btn.setCheckable(True) + self.general_info_btn.setChecked(False) + self.general_info_btn.setStyleSheet("font-weight: bold; text-align: left; background: none; border: none;") + general_info_layout.addWidget(self.general_info_btn) + + # Content widget + self.general_info_content = QWidget() + self.general_info_content.setStyleSheet("background-color: #fff9f9;") + form_layout = QGridLayout(self.general_info_content) + form_layout.setSpacing(10) + + # Company Name + company_label = QLabel("Company Name") + company_label.setFixedWidth(150) + company_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + company_edit = QLineEdit() + company_edit.setFixedWidth(483) + company_edit.setFixedHeight(25) + company_edit.setStyleSheet("background-color: #ffffff;") + + # Project Title + title_label = QLabel("Project Title") + title_label.setFixedWidth(150) + title_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + title_edit = QLineEdit() + title_edit.setFixedWidth(483) + title_edit.setFixedHeight(25) + title_edit.setStyleSheet("background-color: #ffffff;") + + # Project Description + desc_label = QLabel("Project Description") + desc_label.setFixedWidth(150) + desc_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + desc_edit = QLineEdit() + desc_edit.setFixedWidth(483) + desc_edit.setFixedHeight(125) + desc_edit.setStyleSheet("background-color: #ffffff;") + + # Name of Valuer + valuer_label = QLabel("Name of Valuer") + valuer_label.setFixedWidth(150) + valuer_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + valuer_combo = QComboBox() + valuer_combo.setFixedWidth(164) + valuer_combo.setFixedHeight(19) + valuer_combo.setStyleSheet("background-color: #ffffff;") + for valuer in get_country_name(): + valuer_combo.addItem(valuer) + + # Job Number + job_label = QLabel("Job Number") + job_label.setFixedWidth(150) + job_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + job_edit = QLineEdit() + job_edit.setFixedWidth(164) + job_edit.setFixedHeight(19) + job_edit.setStyleSheet("background-color: #ffffff;") + + # Client + client_label = QLabel("Client") + client_label.setFixedWidth(150) + client_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + client_edit = QLineEdit() + client_edit.setFixedWidth(164) + client_edit.setFixedHeight(19) + client_edit.setStyleSheet("background-color: #ffffff;") + + # Country + country_label = QLabel("Country") + country_label.setFixedWidth(150) + country_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + country_combo = QComboBox() + country_combo.setFixedWidth(164) + country_combo.setFixedHeight(19) + country_combo.setStyleSheet("background-color: #ffffff;") + for country in get_country_name(): + country_combo.addItem(country) + + # Base Year + year_label = QLabel("Base Year") + year_label.setFixedWidth(150) + year_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + year_combo = QComboBox() + year_combo.setFixedWidth(164) + year_combo.setFixedHeight(19) + year_combo.setStyleSheet("background-color: #ffffff;") + for year in get_base_year(): + year_combo.addItem(year) + + # Add widgets to grid layout (row, column) + form_layout.addWidget(company_label, 0, 0) + form_layout.addWidget(company_edit, 0, 1) + form_layout.addWidget(title_label, 1, 0) + form_layout.addWidget(title_edit, 1, 1) + form_layout.addWidget(desc_label, 2, 0) + form_layout.addWidget(desc_edit, 2, 1) + form_layout.addWidget(valuer_label, 3, 0) + form_layout.addWidget(valuer_combo, 3, 1) + form_layout.addWidget(job_label, 4, 0) + form_layout.addWidget(job_edit, 4, 1) + form_layout.addWidget(client_label, 5, 0) + form_layout.addWidget(client_edit, 5, 1) + form_layout.addWidget(country_label, 6, 0) + form_layout.addWidget(country_combo, 6, 1) + form_layout.addWidget(year_label, 7, 0) + form_layout.addWidget(year_combo, 7, 1) + + # Add Save button + save_btn = QPushButton("Save General Info") + save_btn.setStyleSheet("background-color: #4C9141; color: white; font-weight: bold; padding: 6px 18px;") + form_layout.addWidget(save_btn) + + # Store references to the input widgets for later access + self.company_edit = company_edit + self.title_edit = title_edit + self.desc_edit = desc_edit + self.valuer_combo = valuer_combo + self.job_edit = job_edit + self.client_edit = client_edit + self.country_combo = country_combo + self.year_combo = year_combo + + # Connect the save button + save_btn.clicked.connect(self.save_general_info_data) + + general_info_layout.addWidget(self.general_info_content) + + def toggle_general_info_content(checked): + self.general_info_content.setVisible(checked) + self.general_info_btn.setText("▼ General Information" if checked else "► General Information") + self.general_info_btn.toggled.connect(toggle_general_info_content) + + self.general_info_content.setVisible(False) + + # Input Parameters section (collapsible) + input_params_box = QGroupBox() + input_params_box.setStyleSheet(""" + QGroupBox { + background-color: #f0e6e6; + border-radius: 3px; + margin-bottom: 5px; + } + """) + input_params_layout = QVBoxLayout(input_params_box) + input_params_layout.setContentsMargins(10, 10, 10, 10) + + self.input_params_btn = QPushButton("► Input Parameters") + self.input_params_btn.setCheckable(True) + self.input_params_btn.setChecked(False) + self.input_params_btn.setStyleSheet("font-weight: bold; text-align: left; background: none; border: none;") + input_params_layout.addWidget(self.input_params_btn) + + self.input_params_content = QWidget() + self.input_params_content.setStyleSheet("background-color: rgb(240, 230, 230);") + input_params_content_layout = QVBoxLayout(self.input_params_content) + input_params_content_layout.setSpacing(10) + + # --- Begin: Copied from MainWindow.py's self.widget_5 --- + + # Structure Works Data (collapsible) + structure_box = QGroupBox() + structure_box.setStyleSheet("background-color: rgb(240,230,230); margin-bottom: 5px;") + structure_layout = QVBoxLayout(structure_box) + structure_layout.setContentsMargins(5, 5, 5, 5) + + self.structure_btn = QPushButton("► Structure Works Data") + self.structure_btn.setCheckable(True) + self.structure_btn.setChecked(False) + self.structure_btn.setStyleSheet("text-align: left; background: none; border: none;") + structure_layout.addWidget(self.structure_btn) + + self.structure_content = QWidget() + structure_content_layout = QVBoxLayout(self.structure_content) + structure_content_layout.setSpacing(6) + + # Foundation + foundation_btn = QPushButton(" Foundation") + foundation_btn.setIcon(QIcon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled.png")) + foundation_btn.setStyleSheet("text-align: left;") + foundation_btn.clicked.connect(self.openFoundationWindow) + structure_content_layout.addWidget(foundation_btn) + + # Super-Structure + superstructure_btn = QPushButton(" Super-Structure") + superstructure_btn.setIcon(QIcon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled.png")) + superstructure_btn.setStyleSheet("text-align: left;") + superstructure_btn.clicked.connect(self.openSuperStructureWindow) + structure_content_layout.addWidget(superstructure_btn) + + # Sub-Structure + substructure_btn = QPushButton(" Sub-Structure") + substructure_btn.setIcon(QIcon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled.png")) + substructure_btn.setStyleSheet("text-align: left;") + substructure_btn.clicked.connect(self.openSubStructureWindow) + structure_content_layout.addWidget(substructure_btn) + + # Miscellaneous + misc_btn = QPushButton(" Miscellaneous") + misc_btn.setIcon(QIcon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled.png")) + misc_btn.setStyleSheet("text-align: left;") + misc_btn.clicked.connect(self.openMiscellaneousWindow) + structure_content_layout.addWidget(misc_btn) + + self.structure_content.setVisible(False) + structure_layout.addWidget(self.structure_content) + + def toggle_structure_content(checked): + self.structure_content.setVisible(checked) + self.structure_btn.setText("▼ Structure Works Data" if checked else "► Structure Works Data") + self.structure_btn.toggled.connect(toggle_structure_content) + + self.input_params_content.setVisible(False) + input_params_content_layout.addWidget(structure_box) + + # Financial Data + financial_btn = QPushButton("► Financial Data") + financial_btn.setStyleSheet("text-align: left;") + financial_btn.clicked.connect(self.openFinancialWindow) + input_params_content_layout.addWidget(financial_btn) + + # Carbon Emission Data (collapsible) + carbon_box = QGroupBox() + carbon_box.setStyleSheet("background-color: rgb(240,230,230); margin-bottom: 5px;") + carbon_layout = QVBoxLayout(carbon_box) + carbon_layout.setContentsMargins(5, 5, 5, 5) + + self.carbon_btn = QPushButton("► Carbon Emission Data") + self.carbon_btn.setCheckable(True) + self.carbon_btn.setChecked(False) + self.carbon_btn.setStyleSheet(" text-align: left; background: none; border: none;") + carbon_layout.addWidget(self.carbon_btn) + + self.carbon_content = QWidget() + carbon_content_layout = QVBoxLayout(self.carbon_content) + carbon_content_layout.setSpacing(6) + + carbon_cost_btn = QPushButton(" Carbon Emission Cost Data") + carbon_cost_btn.setIcon(QIcon("src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled.png")) + carbon_cost_btn.setStyleSheet("text-align: left;") + carbon_cost_btn.clicked.connect(self.openCarbonEmissionWindow) + carbon_content_layout.addWidget(carbon_cost_btn) + + self.carbon_content.setVisible(False) + carbon_layout.addWidget(self.carbon_content) + + def toggle_carbon_content(checked): + self.carbon_content.setVisible(checked) + self.carbon_btn.setText("▼ Carbon Emission Data" if checked else "► Carbon Emission Data") + self.carbon_btn.toggled.connect(toggle_carbon_content) + + input_params_content_layout.addWidget(carbon_box) + + # Bridge and Traffic Data + bridge_btn = QPushButton("► Bridge and Traffic Data") + bridge_btn.setStyleSheet("text-align: left;") + bridge_btn.clicked.connect(self.openBridgeTrafficWindow) + input_params_content_layout.addWidget(bridge_btn) + + # Maintenance and Repair + maintenance_btn = QPushButton("► Maintenance and Repair") + maintenance_btn.setStyleSheet("text-align: left;") + maintenance_btn.clicked.connect(self.openMaintenanceWindow) + input_params_content_layout.addWidget(maintenance_btn) + + # Disposal and Recycling + demolition_btn = QPushButton("► Disposal and Recycling") + demolition_btn.setStyleSheet("text-align: left;") + demolition_btn.clicked.connect(self.openDemolitionWindow) + input_params_content_layout.addWidget(demolition_btn) + + self.input_params_content.setVisible(False) + input_params_layout.addWidget(self.input_params_content) + + + def toggle_input_params_content(checked): + self.input_params_content.setVisible(checked) + self.input_params_btn.setText("▼ Input Parameters" if checked else "► Input Parameters") + self.input_params_btn.toggled.connect(toggle_input_params_content) + + # Outputs section (collapsible) + outputs_box = QGroupBox() + outputs_box.setStyleSheet(""" + QGroupBox { + background-color: #f0e6e6; + border-radius: 3px; + margin-bottom: 5px; + } + """) + outputs_layout = QVBoxLayout(outputs_box) + outputs_layout.setContentsMargins(10, 10, 10, 10) + + self.outputs_btn = QPushButton("► Outputs") + self.outputs_btn.setCheckable(True) + self.outputs_btn.setChecked(False) + self.outputs_btn.setStyleSheet("font-weight: bold; text-align: left; background: none; border: none;") + outputs_layout.addWidget(self.outputs_btn) + + self.outputs_content = QWidget() + outputs_content_layout = QVBoxLayout(self.outputs_content) + outputs_content_layout.setSpacing(10) + # Add your outputs widgets here, e.g.: + outputs_content_layout.addWidget(QLabel("Outputs fields go here...")) + self.outputs_content.setVisible(False) + outputs_layout.addWidget(self.outputs_content) + + def toggle_outputs_content(checked): + self.outputs_content.setVisible(checked) + self.outputs_btn.setText("▼ Outputs" if checked else "► Outputs") + self.outputs_btn.toggled.connect(toggle_outputs_content) + + # Add all sections to the project content + project_content_layout.addWidget(general_info_box) + project_content_layout.addWidget(input_params_box) + project_content_layout.addWidget(outputs_box) + + project_layout.addWidget(project_header) + project_layout.addWidget(project_content) + project_layout.addStretch() + + # Add both panels to the splitter + self.splitter.addWidget(self.tutorials_panel) + self.splitter.addWidget(self.project_panel) + self.main_layout.addWidget(self.splitter) + + def create_status_bar(self): + status_bar = self.statusBar() + + data_btn = QPushButton("▲ Data") + data_btn.setStyleSheet("padding: 5px 15px;") + + status_bar.addPermanentWidget(data_btn) + + def update_tutorial_content(self): + # Get current page data + page_data = self.tutorial_pages[self.current_tutorial_page - 1] + + # Update the tutorial content + self.page_label.setText(page_data["page_number"]) + self.welcome_label.setText(page_data["title"]) + self.description_label.setText(page_data["content"]) + + def tutorial_next(self): + if self.current_tutorial_page < self.total_tutorial_pages: + self.current_tutorial_page += 1 + self.update_tutorial_content() + elif self.current_tutorial_page == self.total_tutorial_pages: + # Simulate clicking the Project Details tab button + if "Project Details" in self.tab_buttons: + self.tab_buttons["Project Details"].click() + + def tutorial_back(self): + if self.current_tutorial_page > 1: + self.current_tutorial_page -= 1 + self.update_tutorial_content() + + def close_tutorials(self): + # Hide the tutorials panel (in a real app, you might want to remove or collapse it) + self.sender().parent().parent().hide() + + def close_project_details(self): + # Hide the project details panel + self.sender().parent().parent().hide() + + def mousePressEvent(self, event): + # Hide menus when clicking outside + if self.file_menu_widget.isVisible() and not self.file_menu_widget.geometry().contains(event.pos()): + self.file_menu_widget.hide() + + if self.help_menu_widget.isVisible() and not self.help_menu_widget.geometry().contains(event.pos()): + self.help_menu_widget.hide() + + super().mousePressEvent(event) + + def save_general_info_data(self): + data = { + "company_name": self.company_edit.text(), + "project_title": self.title_edit.text(), + "project_description": self.desc_edit.text(), + "name_of_valuer": self.valuer_combo.currentText(), + "job_number": self.job_edit.text(), + "client": self.client_edit.text(), + "country": self.country_combo.currentText(), + "base_year": self.year_combo.currentText(), + } + save_general_info(data) + QMessageBox.information(self, "Success", "Data has been saved.") + # self.confirm_button.setEnabled(False) + + def closeEvent(self, event): + # Clearing the general_info.json file + filename = "src//osbridgelcca//desktop_app//assets//general_info.json" + with open(filename, "w", encoding="utf-8") as f: + json.dump({}, f, indent=4) + super().closeEvent(event) + + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = BICCAStudio() + window.show() + sys.exit(app.exec_()) +#Sampel code for a PyQt5 desktop application given by the user +''' from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget from desktop_app.logic.controller import Controller @@ -30,3 +926,4 @@ def run_analysis(self): window = MainWindow() window.show() app.exec_() +''' \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/08b7798a84ffe0528bdec7dc8efe8fb1c89f77ae.png b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/08b7798a84ffe0528bdec7dc8efe8fb1c89f77ae.png new file mode 100644 index 0000000000000000000000000000000000000000..93ce61c8ffc88c760d0bee4ec6149e1e1103b536 GIT binary patch literal 65874 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

;aN}e%P$YB# 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%0DlmbwCJiYg2tXN`n8Apd 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 literal 0 HcmV?d00001 diff --git a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Contact.png b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Contact.png new file mode 100644 index 0000000000000000000000000000000000000000..fe516c32624e1267a3b7192377512339dee03b8b GIT binary patch literal 283 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#4W2HJAr*|V{s(yv8SreG%=PgM+gpyF3yfVU zjLcscS7dKs{8O=sA;|3|hvdQhCu%KQL}pdHe0o|_?l$AXg^g`ItR0)bJ-oEx?%Z2# z=caVe`g>yK{V;=_92Y+8TxxJLQEOVUx+7|H&));=eSSV0uP;bx-k`PkgVJOTOWu!P zS9md|ZjqP$Z&=Qyn83J7{KJ|*uf^6sa-HLra*^R%WqD!A=j91gf7NqZm6XacZ+f?O b?m_3 z((>skp1BGhk&BE&{vIyS^4!30`7Bo8+hz7|%obaA=hV;4xo)~fzbdDG?x|gI7V-BP zdp?gTe~DWM4f?Kf%D literal 0 HcmV?d00001 diff --git a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Export.png b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Export.png new file mode 100644 index 0000000000000000000000000000000000000000..cf617520b3eed9ba7c7ccc6774fc84148f2ba5b1 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#?Vc`g4O z+5(2$1|}VcTN9W*Oq#87r^DVysV7Zs^X{A9|G#r7m}l?Np}h2QZT7RB$xEf4G#9=r z>u~6`UN<2~m^+Y#;cDyBB|5L%HR3v&=41=J7YN^2?k8YaT(D=6<%B8f7GE#!wW}#F z)YI6zahqy(=T^Cl$X5o5%?aG4)47a&|5`b|&T8JYruoxOVZmv7AFrvb@L`ZT+815? h@w@;2b#GkNbmTc8&hJYD@<);T3K0RSlqYsCNn literal 0 HcmV?d00001 diff --git a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Icon.png b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..56995f1ea792b4fab04e881822b4799f8f075c25 GIT binary patch literal 1093 zcmV-L1iJf)P)cMMo?Zs<_f(H-PegL5tsUX^O zE@}mZN=sX7u!j^X7)WW8b!Xqsd-Gm5wKhiwHoMukyYrjh%+4&p|1JW5R#sMAA;jZY zEH-D**si|#&%9Y~Pd_M0Mi$zGKQXVoPBmuM>(sHg< zkHcoODU@Q^+uOsoeROm*5mz5(`}+FaQmI6W^(KJ>a{vwTGd~!c<2aJWstNCt6g0bXQB0oiO8lybLPt-f&7;nNC02&u7N3o3}Rf&Z~F<-#Zri2+VVNX9G_3I$M%j5+G( z1f5(Nw~R3)%o?qj1LndRK>CRZ?3CMaE~f!-gk*`u&qTirroDZ08vc` z{;Ji*!{_fr`P)yvPYm{4fV($F#G}bEt3o>8oFy4+K5%TP+o1tcgpI7k{_WZhM3if( z?Ei*^*B@Z%{g+nON$$2*AW?6eT)-C)VpMdRJb6`yuRr#os2IWR-Wi^|CPpv8)q$Mg z!AEgmj162s0E{06Ej)`Xmp82Q&)$57#Rs=w{7S(OWHb!VHaAO;qYPLR&siiE?|_~` zSNA@+jRrVfO>wO^d>xIEo(nkr5jUL3gsr4AnSqh(HzAQqhvqA)EqpwN#oHY`i3ygt zz`usndpNW}aO3`6ITwqOl62aFPz0L-CGO}rbanT_d4&|4_!2&Vi1mK&`N|8%{-IJ^ z${USFD6^VdrolLzdV`Q$;%`b{%t1TCvn16Il#mNhAuk@hPj|Hkz>?l65CRwIv}n*7 zAOhESrZUiu^dvn?DIu|B6eJ?52k0w$4-nAKWE$EL1Yj#vejec0&@e#hQ}p)_z`#%* z+L7)go*jOmSYrSM89)$3)5LHVz8?!%tHQZtI4fcDa+B2w@C~H63o&3&`O%+rB-|ej zk|BF|2%c8yJH-#3#r;lMkcv_$6&nzgdU*<#R6Hbmc9CLkjRC;RE?sHC-x>TySTkWb z5qTBN1ShwaUs=vZ$OW{XSS9P30YNchwE1s0zJVVi?awa0p1**6!({MU7L*C&%DRKY z!^0d~L_k7^Mic?d7qW%2C_vpY)&xDtK5cDn`GLZsRgaN=K4*FfLDdw|iW;e`z})OV z`!+W>@&8M7!M#eQvRW>eT_upoWC90=MF%LoWEcyf0K<9i3J5BomRhX_>+9=scXxM3 zt)J5$=I7^KW$CQia7Rt%EYw*k4QI`hdu*ci94J6bYWQ?!W@h^j+beo%n~IEG00000 LNkvXXu0mjff5_1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#OivfbkP61$34y!~20Sj&O$;&uG6|gQ9u7== zGnm&jFzFbuKAv@9&Fk}JC%wzE%d$^TDQ(KAVdA!wJ;!caaz(+6Q@Fw>mrY#aNY2y+ z7rw;i$M3ks9riZ!zrLH6`?RTdE(z~ml2UVJ34g1Wz-qDU5zm2^GI+ZBxvX(0r%1aer?9eo`c7&i8E|4C#8@{>JX978G?Tl)fe8w_|{*7N>XNGxt*DQId= zWK>^}P~6b!IitDA`$wF9=B0hRP1F@zRFte6($dn`Zk>>u!Y+BjD5-~)`C`3j-;b3$ x7wV-JeRG<;%1&q2|KCX;XMaCsTR81q{jAG!*Thw-qk+~jc)I$ztaD0e0swK>Lcjn3 literal 0 HcmV?d00001 diff --git a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (4).png b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector (4).png new file mode 100644 index 0000000000000000000000000000000000000000..c43d9af66e21530be2059affe55ebfe74d4392af GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^96-#;!3HGxgLCyjoC1%?Vg?3oVGw3ym^DWND9BhG zDF{FaAwRahV?wx?NmMK6Wr>dM1 z;^yJ$>3P!q7so?|PnSHu{an1lhBM)DjoC^C#>a6EOKu;$nlj1m=i$9wVf#bFI}+H~ rzq{5YeLZNRA)?RBKeKrHRrx&mliVg<2lze#tzz(W^>bP0l+XkK2%tYX literal 0 HcmV?d00001 diff --git a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector.png b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/Vector.png new file mode 100644 index 0000000000000000000000000000000000000000..718aa64801809430d0322d44d98ecca243e7858f GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#Y)==*kP60Je@5N|3Ovk3jMEz?NAF-!QD9Ve zKcuS2$a&wwmn(8|TcFjh%I}Xw)K-c5{@}7c(k|DivF5^Mkp`!&QbjY`p5^qcIQ-+` zqFoAl`FB(Iv!B`~VAv(KD!RShZreANJ1K8IUwQVA@oI{|i|%Do8bE6qJYD@<);T3K F0RUQZMAQHP literal 0 HcmV?d00001 diff --git a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled (1).png b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled (1).png new file mode 100644 index 0000000000000000000000000000000000000000..8700d20e4eb75585e77489c6e2e4ecc8e4065547 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngd!3HEhbh*6;Qk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFK>ln`7srqa#;5%oc@G%~xbA=K&Arg2P^I6& zlT&&F(`*c)1t7Kx)mZi{*@DOvak7H&#&fYbUq8{T<0r7N?z(eG`16cMYYuT7yV098 zhb_Hz?f3by^+IwPt1oTR%$ literal 0 HcmV?d00001 diff --git a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled.png b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/play_arrow_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..95b18c8e6ead9b365163c1a9984a38ad2de394d8 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^d_c_4!3HF+i2N%7Qk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFK>jpO7srqa#;bj2d7B(WTGHpgalP|n9h*-a zcUgn#4aTwr^H8Bc{hrO1OxWF(?77I}(#|{U@+A&) zGQMA&c(eVFws&@|LT{MXl35e3@?UG@)j6eTJ^#r`ojo!+!Tru1=d&&_u6KBR?JJXy zo$J5w9pbM4WarI@Q0(pI|D-2$(5n5l#0JOEr+XA0Cpt4;0lJXE)78&qol`;+0QU!5 A<^TWy literal 0 HcmV?d00001 diff --git "a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _People Community_ (1).png" "b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _People Community_ (1).png" new file mode 100644 index 0000000000000000000000000000000000000000..44420f696f76afa05b7ad71a20c0214f84d2845c GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#L!K^FH=SSg$W1Wl`RXHqXSAOxZG>)6Ti`5{oqLY2fIYZ zuG0pA#Z5u4E^o^{>Ur%})wJh7t$Z)DPOEvmLH)+^M)8FHwz^%Hb!%R!|2e{VRJFV- teNS8ajEq;4e;Dx`Zsl~5uRT-CXmH89e|hb46`*ezJYD@<);T3K0RV|BbN2uM literal 0 HcmV?d00001 diff --git "a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _People Community_.png" "b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _People Community_.png" new file mode 100644 index 0000000000000000000000000000000000000000..44420f696f76afa05b7ad71a20c0214f84d2845c GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#L!K^FH=SSg$W1Wl`RXHqXSAOxZG>)6Ti`5{oqLY2fIYZ zuG0pA#Z5u4E^o^{>Ur%})wJh7t$Z)DPOEvmLH)+^M)8FHwz^%Hb!%R!|2e{VRJFV- teNS8ajEq;4e;Dx`Zsl~5uRT-CXmH89e|hb46`*ezJYD@<);T3K0RV|BbN2uM literal 0 HcmV?d00001 diff --git "a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _Person Feedback_.png" "b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _Person Feedback_.png" new file mode 100644 index 0000000000000000000000000000000000000000..f90d4fc5429f027cca0f70990bce22b912e6b0d1 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#Wu7jMAr*|deP?+O8Su2ezQDMN@g{?thV%x; zg2xhS3zRo7_8j0aV3a-RzT+Sh=lrEXu7cC%&8xq+S=U!ovBmUC@= z8@ncM_g@wbP0l+XkKX=Y{P literal 0 HcmV?d00001 diff --git "a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _document save as template_.png" "b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _document save as template_.png" new file mode 100644 index 0000000000000000000000000000000000000000..c6b4a57c98fcc3535d1a1b0ca162f8ca10fb3a8d GIT binary patch literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#%bqTdAr*|J0SA2#I|$6V+dc8ePG&!q_6Gj& zChr|W3s|f?v=pZ}u)kyYp>W!*)BA(|M4|gf%1VOlBr~^OdXtg1rRe>1|%O$WD@{!3Opi<85p>QL70(Y)*K0-AY*Zm zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#YEKu(kP61yzP+3c4m@s)?=Xa26EkaSmEO_N zs%_D!+#wyo;itH+VDgEZ(Ld%sQ8+UHfVJMva!)0tQx5*;UmQBRp`?LRC8qq6*&>UE z$)A}_4*XIHQ+7*ZmtK*gF01Wvkmr>BNq0G`$a^dQ{V4G6bA2SZep1Y?(D&W;dS5z( V3Ok!$%mO-t!PC{xWt~$(696?pO&b6J literal 0 HcmV?d00001 diff --git "a/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _save action floppy_.png" "b/src/osbridgelcca/desktop_app/assets/MainWindow_icons/\360\237\246\206 icon _save action floppy_.png" new file mode 100644 index 0000000000000000000000000000000000000000..51f95b7f7ea905fb988e5634a191fbe803e40881 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CN!3HF~3v%Lt6lZ})WHC@o7=#%aX3dcR3NjW4 zxjQkeJ16rJ$YDu$^mSxl*x1kgCy^D%Z}W6<45?tO4csW$V8Eer?2ysYRJW5Hl2-+d z+EU#-GG8t%s8jk?E+bq+k3ef*z<9<;4_Tp1Ak;$CCm!p5uNAMZvo-&|Ze!%@{U*rXyb i>!{l*8o|0J>k`SG4Ejv*C{rG4DI2NZZ*xMwn`C?pE0B?hS_ z3KeWPBN(K`DAl(_q~c^N_um*zopr03KUBhX4Qo literal 0 HcmV?d00001 diff --git a/src/osbridgelcca/desktop_app/assets/country_abbreviations.csv b/src/osbridgelcca/desktop_app/assets/country_abbreviations.csv new file mode 100644 index 0000000..69ab06a --- /dev/null +++ b/src/osbridgelcca/desktop_app/assets/country_abbreviations.csv @@ -0,0 +1,171 @@ +Abbreviation,Country Name +AFG,Afghanistan +AGO,Angola +ALB,Albania +ARE,United Arab Emirates +ARG,Argentina +ARM,Armenia +AUS,Australia +AUT,Austria +AZE,Azerbaijan +BDI,Burundi +BEL,Belgium +BEN,Benin +BFA,Burkina Faso +BGD,Bangladesh +BGR,Bulgaria +BHS,Bahamas +BIH,Bosnia and Herzegovina +BLR,Belarus +BLZ,Belize +BOL,Bolivia +BRA,Brazil +BRN,Bahrain +BTN,Bhutan +BWA,Botswana +CAF,Central African Republic +CAN,Canada +CHE,Switzerland +CHL,Chile +CHN,China +CIV,Côte d'Ivoire +CMR,Cameroon +COD,Democratic Republic of the Congo +COG,Republic of the Congo +COL,Colombia +COM,Comoros +CPV,Cabo Verde +CRI,Costa Rica +CUB,Cuba +CYP,Cyprus +CZE,Czechia +DEU,Germany +DJI,Djibouti +DNK,Denmark +DOM,Dominican Republic +DZA,Algeria +ECU,Ecuador +EGY,Egypt +ERI,Eritrea +ESP,Spain +EST,Estonia +ETH,Ethiopia +FIN,Finland +FJI,Fiji +FRA,France +GAB,Gabon +GBR,United Kingdom +GEO,Georgia +GHA,Ghana +GIN,Guinea +GMB,Gambia +GNB,Guinea-Bissau +GNQ,Equatorial Guinea +GRC,Greece +GTM,Guatemala +GUY,Guyana +HND,Honduras +HRV,Croatia +HTI,Haiti +HUN,Hungary +IDN,Indonesia +IND,India +IRL,Ireland +IRN,Iran +IRQ,Iraq +ISL,Iceland +ISR,Israel +ITA,Italy +JAM,Jamaica +JOR,Jordan +JPN,Japan +KAZ,Kazakhstan +KEN,Kenya +KGZ,Kyrgyzstan +KHM,Cambodia +KOR,South Korea +KWT,Kuwait +LAO,Laos +LBN,Lebanon +LBR,Liberia +LBY,Libya +LKA,Sri Lanka +LSO,Lesotho +LTU,Lithuania +LUX,Luxembourg +LVA,Latvia +MAR,Morocco +MDA,Moldova +MDG,Madagascar +MEX,Mexico +MKD,North Macedonia +MLI,Mali +MMR,Myanmar +MNE,Montenegro +MNG,Mongolia +MOZ,Mozambique +MRT,Mauritania +MUS,Mauritius +MWI,Malawi +MYS,Malaysia +NAM,Namibia +NCL,New Caledonia +NER,Niger +NGA,Nigeria +NIC,Nicaragua +NLD,Netherlands +NOR,Norway +NPL,Nepal +NZL,New Zealand +OMN,Oman +PAK,Pakistan +PAN,Panama +PER,Peru +PHL,Philippines +PNG,Papua New Guinea +POL,Poland +PRT,Portugal +PRY,Paraguay +QAT,Qatar +ROU,Romania +RUS,Russia +RWA,Rwanda +SAU,Saudi Arabia +SDN,Sudan +SEN,Senegal +SLB,Solomon Islands +SLE,Sierra Leone +SLV,El Salvador +SOM,Somalia +SRB,Serbia +STP,São Tomé and Príncipe +SUR,Suriname +SVK,Slovakia +SVN,Slovenia +SWE,Sweden +SWZ,Eswatini +SYR,Syria +TCD,Chad +TGO,Togo +THA,Thailand +TJK,Tajikistan +TKM,Turkmenistan +TTO,Trinidad and Tobago +TUN,Tunisia +TUR,Turkey +TZA,Tanzania +UGA,Uganda +UKR,Ukraine +URY,Uruguay +USA,United States +UZB,Uzbekistan +VCT,Saint Vincent and the Grenadines +VEN,Venezuela +VNM,Vietnam +VUT,Vanuatu +WLD,World +WSM,Samoa +YEM,Yemen +ZAF,South Africa +ZMB,Zambia +ZWE,Zimbabwe diff --git a/src/osbridgelcca/desktop_app/assets/general_info.json b/src/osbridgelcca/desktop_app/assets/general_info.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/osbridgelcca/desktop_app/assets/general_info.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/assets/years_2000_to_2025.csv b/src/osbridgelcca/desktop_app/assets/years_2000_to_2025.csv new file mode 100644 index 0000000..474db58 --- /dev/null +++ b/src/osbridgelcca/desktop_app/assets/years_2000_to_2025.csv @@ -0,0 +1,27 @@ +Year +2000 +2001 +2002 +2003 +2004 +2005 +2006 +2007 +2008 +2009 +2010 +2011 +2012 +2013 +2014 +2015 +2016 +2017 +2018 +2019 +2020 +2021 +2022 +2023 +2024 +2025 diff --git a/src/osbridgelcca/desktop_app/ui/input_form.py b/src/osbridgelcca/desktop_app/ui/input_form.py index 5516c75..29f7c9c 100644 --- a/src/osbridgelcca/desktop_app/ui/input_form.py +++ b/src/osbridgelcca/desktop_app/ui/input_form.py @@ -1 +1,7 @@ # Placeholder for input form UI +import json + +def save_general_info(data, filename="src//osbridgelcca//desktop_app//assets//general_info.json"): + """Save the general information dictionary to a JSON file.""" + with open(filename, "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_BridgeANDTrafficData_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_BridgeANDTrafficData_Window.py new file mode 100644 index 0000000..57381ea --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_BridgeANDTrafficData_Window.py @@ -0,0 +1,675 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_Bridge&TrafficData_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox + # Add this import at the top +#from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + + + + + + + + + + +class Ui_BridgeTraffic_Dialog(object): + def openBridgeTrafficWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, BridgeTraffic_Dialog): + BridgeTraffic_Dialog.setObjectName("BridgeTraffic_Dialog") + BridgeTraffic_Dialog.resize(1440, 900) + BridgeTraffic_Dialog.setStyleSheet("background-color: #fafafa") + self.scrollArea = QtWidgets.QScrollArea(BridgeTraffic_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 50, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents_3 = QtWidgets.QWidget() + self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3") + self.label_16 = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.label_16.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_16.setFont(font) + self.label_16.setStyleSheet("background-color: rgb(240,230,230)") + self.label_16.setAlignment(QtCore.Qt.AlignCenter) + self.label_16.setObjectName("label_16") + self.widget_3 = QtWidgets.QWidget(self.scrollAreaWidgetContents_3) + self.widget_3.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget_3.setStyleSheet("background-color: #fff9f9") + self.widget_3.setObjectName("widget_3") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.widget_3) + self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.pushButton_34 = QtWidgets.QPushButton(self.widget_3) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_34.setFont(font) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_34.setIcon(icon) + self.pushButton_34.setCheckable(True) + self.pushButton_34.setAutoDefault(True) + self.pushButton_34.setObjectName("pushButton_34") + self.verticalLayout_3.addWidget(self.pushButton_34) + self.widget_7 = QtWidgets.QWidget(self.widget_3) + self.widget_7.setObjectName("widget_7") + self.formLayout_3 = QtWidgets.QFormLayout(self.widget_7) + self.formLayout_3.setObjectName("formLayout_3") + self.pushButton_35 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_35.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_35.setIcon(icon1) + self.pushButton_35.setObjectName("pushButton_35") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_35) + self.pushButton_36 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_36.setFont(font) + self.pushButton_36.setIcon(icon1) + self.pushButton_36.setObjectName("pushButton_36") + self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_36) + self.pushButton_37 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_37.setFont(font) + self.pushButton_37.setIcon(icon1) + self.pushButton_37.setObjectName("pushButton_37") + self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_37) + self.pushButton_38 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_38.setFont(font) + self.pushButton_38.setIcon(icon1) + self.pushButton_38.setObjectName("pushButton_38") + self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_38) + self.verticalLayout_3.addWidget(self.widget_7) + self.pushButton_39 = QtWidgets.QPushButton(self.widget_3, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_39.setFont(font) + self.pushButton_39.setObjectName("pushButton_39") + self.verticalLayout_3.addWidget(self.pushButton_39) + self.pushButton_40 = QtWidgets.QPushButton(self.widget_3) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_40.setFont(font) + self.pushButton_40.setIcon(icon) + self.pushButton_40.setCheckable(True) + self.pushButton_40.setObjectName("pushButton_40") + self.verticalLayout_3.addWidget(self.pushButton_40) + self.widget_10 = QtWidgets.QWidget(self.widget_3) + self.widget_10.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_10.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_10.setObjectName("widget_10") + self.pushButton_41 = QtWidgets.QPushButton(self.widget_10, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_41.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_41.setFont(font) + self.pushButton_41.setIcon(icon1) + self.pushButton_41.setObjectName("pushButton_41") + self.verticalLayout_3.addWidget(self.widget_10) + self.pushButton_42 = QtWidgets.QPushButton(self.widget_3, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_42.setFont(font) + self.pushButton_42.setObjectName("pushButton_42") + self.verticalLayout_3.addWidget(self.pushButton_42) + self.pushButton_43 = QtWidgets.QPushButton(self.widget_3, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_43.setFont(font) + self.pushButton_43.setObjectName("pushButton_43") + self.verticalLayout_3.addWidget(self.pushButton_43) + self.pushButton_12 = QtWidgets.QPushButton(self.widget_3, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_12.setFont(font) + self.pushButton_12.setObjectName("pushButton_12") + self.verticalLayout_3.addWidget(self.pushButton_12) + self.label_17 = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.label_17.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_17.setFont(font) + self.label_17.setStyleSheet("background-color: rgb(240,230,230)") + self.label_17.setAlignment(QtCore.Qt.AlignCenter) + self.label_17.setObjectName("label_17") + self.textBrowser_3 = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents_3) + self.textBrowser_3.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser_3.setStyleSheet("background-color: #fff9f9") + self.textBrowser_3.setObjectName("textBrowser_3") + self.verticalScrollBar_3 = QtWidgets.QScrollBar(self.scrollAreaWidgetContents_3) + self.verticalScrollBar_3.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar_3.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar_3.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar_3.setObjectName("verticalScrollBar_3") + self.scrollArea.setWidget(self.scrollAreaWidgetContents_3) + self.widget_4 = QtWidgets.QWidget(BridgeTraffic_Dialog) + self.widget_4.setGeometry(QtCore.QRect(350, 45, 895, 561)) + self.widget_4.setStyleSheet("background-color: #fff9f9; border: 1px solid black;") + self.widget_4.setObjectName("widget_4") + self.label_18 = QtWidgets.QLabel(self.widget_4) + self.label_18.setGeometry(QtCore.QRect(20, 20, 141, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_18.setFont(font) + self.label_18.setStyleSheet("border: none;") + self.label_18.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_18.setObjectName("label_18") + self.label_19 = QtWidgets.QLabel(self.widget_4) + self.label_19.setGeometry(QtCore.QRect(20, 80, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_19.setFont(font) + self.label_19.setStyleSheet("border: none;") + self.label_19.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_19.setObjectName("label_19") + self.label_20 = QtWidgets.QLabel(self.widget_4) + self.label_20.setGeometry(QtCore.QRect(20, 50, 201, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_20.setFont(font) + self.label_20.setStyleSheet("border: none;") + self.label_20.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_20.setObjectName("label_20") + self.label_29 = QtWidgets.QLabel(self.widget_4) + self.label_29.setGeometry(QtCore.QRect(20, 110, 151, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_29.setFont(font) + self.label_29.setStyleSheet("border: none;") + self.label_29.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_29.setObjectName("label_29") + self.label_30 = QtWidgets.QLabel(self.widget_4) + self.label_30.setGeometry(QtCore.QRect(20, 140, 221, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_30.setFont(font) + self.label_30.setStyleSheet("border: none;") + self.label_30.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_30.setObjectName("label_30") + self.comboBox_6 = QtWidgets.QComboBox(self.widget_4) + self.comboBox_6.setGeometry(QtCore.QRect(270, 80, 101, 22)) + self.comboBox_6.setStyleSheet("border:none;") + self.comboBox_6.setStyleSheet("background-color: #ffffff") + self.comboBox_6.setObjectName("comboBox_6") + self.comboBox_7 = QtWidgets.QComboBox(self.widget_4) + self.comboBox_7.setGeometry(QtCore.QRect(270, 20, 101, 22)) + self.comboBox_7.setStyleSheet("border:none;") + self.comboBox_7.setStyleSheet("background-color: #ffffff") + self.comboBox_7.setObjectName("comboBox_7") + self.lineEdit_9 = QtWidgets.QLineEdit(self.widget_4) + self.lineEdit_9.setGeometry(QtCore.QRect(270, 50, 101, 20)) + self.lineEdit_9.setStyleSheet("border:none;") + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_9.setFont(font) + self.lineEdit_9.setStyleSheet("background-color: #ffffff") + self.lineEdit_9.setObjectName("lineEdit_9") + self.lineEdit_10 = QtWidgets.QLineEdit(self.widget_4) + self.lineEdit_10.setGeometry(QtCore.QRect(270, 110, 101, 20)) + self.lineEdit_10.setStyleSheet("border:none;") + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_10.setFont(font) + self.lineEdit_10.setStyleSheet("background-color: #ffffff") + self.lineEdit_10.setObjectName("lineEdit_10") + self.buttonBox_4 = QtWidgets.QDialogButtonBox(self.widget_4) + self.buttonBox_4.accepted.connect(self.save_data) + self.buttonBox_4.setGeometry(QtCore.QRect(540, 520, 341, 32)) + self.buttonBox_4.setStyleSheet("border: none;") + self.buttonBox_4.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox_4.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox_4.setObjectName("buttonBox_4") + self.label_31 = QtWidgets.QLabel(self.widget_4) + self.label_31.setGeometry(QtCore.QRect(390, 200, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_31.setFont(font) + self.label_31.setStyleSheet("border: none;") + self.label_31.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_31.setObjectName("label_31") + self.label_32 = QtWidgets.QLabel(self.widget_4) + self.label_32.setGeometry(QtCore.QRect(390, 50, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_32.setFont(font) + self.label_32.setStyleSheet("border: none;") + self.label_32.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_32.setObjectName("label_32") + self.label_33 = QtWidgets.QLabel(self.widget_4) + self.label_33.setGeometry(QtCore.QRect(390, 80, 61, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_33.setFont(font) + self.label_33.setStyleSheet("border: none;") + self.label_33.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_33.setObjectName("label_33") + self.label_34 = QtWidgets.QLabel(self.widget_4) + self.label_34.setGeometry(QtCore.QRect(390, 110, 61, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_34.setFont(font) + self.label_34.setStyleSheet("border: none;") + self.label_34.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_34.setObjectName("label_34") + self.textBrowser_4 = QtWidgets.QTextBrowser(self.widget_4) + self.textBrowser_4.setGeometry(QtCore.QRect(20, 180, 221, 61)) + self.textBrowser_4.setStyleSheet("boeder:none;") + self.textBrowser_4.setObjectName("textBrowser_4") + self.label_35 = QtWidgets.QLabel(self.widget_4) + self.label_35.setGeometry(QtCore.QRect(20, 260, 221, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_35.setFont(font) + self.label_35.setStyleSheet("border: none;") + self.label_35.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_35.setObjectName("label_35") + self.comboBox_8 = QtWidgets.QComboBox(self.widget_4) + self.comboBox_8.setGeometry(QtCore.QRect(270, 140, 101, 22)) + self.comboBox_8.setStyleSheet("border: none;") + self.comboBox_8.setStyleSheet("background-color: #ffffff") + self.comboBox_8.setObjectName("comboBox_8") + self.comboBox_9 = QtWidgets.QComboBox(self.widget_4) + self.comboBox_9.setGeometry(QtCore.QRect(270, 200, 101, 22)) + self.comboBox_9.setStyleSheet("border: none;") + self.comboBox_9.setStyleSheet("background-color: #ffffff") + self.comboBox_9.setObjectName("comboBox_9") + self.widget_11 = QtWidgets.QWidget(self.widget_4) + self.widget_11.setGeometry(QtCore.QRect(270, 260, 330, 216)) + self.widget_11.setStyleSheet("border: none;") + self.widget_11.setStyleSheet("background-color: #ffffff") + self.widget_11.setObjectName("widget_11") + self.label_40 = QtWidgets.QLabel(self.widget_11) + self.label_40.setGeometry(QtCore.QRect(10, 170, 80, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_40.setFont(font) + self.label_40.setStyleSheet("border: none;") + self.label_40.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_40.setObjectName("label_40") + self.label_36 = QtWidgets.QLabel(self.widget_11) + self.label_36.setGeometry(QtCore.QRect(10, 10, 80, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_36.setFont(font) + self.label_36.setStyleSheet("border: none;") + self.label_36.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_36.setObjectName("label_36") + self.label_37 = QtWidgets.QLabel(self.widget_11) + self.label_37.setGeometry(QtCore.QRect(10, 50, 80, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_37.setFont(font) + self.label_37.setStyleSheet("border: none;") + self.label_37.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_37.setObjectName("label_37") + self.label_39 = QtWidgets.QLabel(self.widget_11) + self.label_39.setGeometry(QtCore.QRect(10, 90, 80, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_39.setFont(font) + self.label_39.setStyleSheet("border: none;") + self.label_39.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_39.setObjectName("label_39") + self.label_38 = QtWidgets.QLabel(self.widget_11) + self.label_38.setGeometry(QtCore.QRect(10, 130, 80, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_38.setFont(font) + self.label_38.setStyleSheet("border: none;") + self.label_38.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_38.setObjectName("label_38") + self.lineEdit_11 = QtWidgets.QLineEdit(self.widget_11) + self.lineEdit_11.setGeometry(QtCore.QRect(110, 10, 171, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_11.setFont(font) + self.lineEdit_11.setStyleSheet("background-color: #ffffff") + self.lineEdit_11.setObjectName("lineEdit_11") + self.lineEdit_12 = QtWidgets.QLineEdit(self.widget_11) + self.lineEdit_12.setGeometry(QtCore.QRect(110, 50, 171, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_12.setFont(font) + self.lineEdit_12.setStyleSheet("background-color: #ffffff") + self.lineEdit_12.setObjectName("lineEdit_12") + self.lineEdit_15 = QtWidgets.QLineEdit(self.widget_11) + self.lineEdit_15.setGeometry(QtCore.QRect(110, 90, 171, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_15.setFont(font) + self.lineEdit_15.setStyleSheet("background-color: #ffffff") + self.lineEdit_15.setObjectName("lineEdit_15") + self.lineEdit_16 = QtWidgets.QLineEdit(self.widget_11) + self.lineEdit_16.setGeometry(QtCore.QRect(110, 130, 171, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_16.setFont(font) + self.lineEdit_16.setStyleSheet("background-color: #ffffff") + self.lineEdit_16.setObjectName("lineEdit_16") + self.lineEdit_17 = QtWidgets.QLineEdit(self.widget_11) + self.lineEdit_17.setGeometry(QtCore.QRect(110, 170, 171, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_17.setFont(font) + self.lineEdit_17.setStyleSheet("background-color: #ffffff") + self.lineEdit_17.setObjectName("lineEdit_17") + self.label_41 = QtWidgets.QLabel(self.widget_4) + self.label_41.setGeometry(QtCore.QRect(610, 350, 61, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_41.setFont(font) + self.label_41.setStyleSheet("border: none;") + self.label_41.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_41.setObjectName("label_41") + self.pushButton = QtWidgets.QPushButton(BridgeTraffic_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 20, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton.setIcon(icon2) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + self.pushButton_6 = QtWidgets.QPushButton(BridgeTraffic_Dialog) + self.pushButton_6.setGeometry(QtCore.QRect(350, 20, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_6.setFont(font) + self.pushButton_6.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_6.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_6.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton_6.setIcon(icon2) + self.pushButton_6.setAutoRepeat(False) + self.pushButton_6.setObjectName("pushButton_6") + + # Add a Save and Close button + # self.buttonBox = QtWidgets.QDialogButtonBox(BridgeTraffic_Dialog) + # self.buttonBox.setGeometry(QtCore.QRect(540, 520, 341, 32)) + # self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + # self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close | QtWidgets.QDialogButtonBox.Save) + # self.buttonBox.setObjectName("buttonBox") + + # # Connect the Close button to the custom close method + # self.buttonBox.rejected.connect(self.show_warning) + + # Connect the Save button to the save_data method (if implemented) + # self.buttonBox.accepted.connect(self.save_data) + + self.retranslateUi(BridgeTraffic_Dialog) + self.buttonBox_4.accepted.connect(lambda: [self.save_data(), BridgeTraffic_Dialog.accept()]) # type: ignore + self.buttonBox_4.rejected.connect(lambda: self.show_warning(BridgeTraffic_Dialog)) # type: ignore + self.pushButton_34.toggled['bool'].connect(self.widget_7.setVisible) # type: ignore + self.pushButton_40.toggled['bool'].connect(self.widget_10.setVisible) # type: ignore + QtCore.QMetaObject.connectSlotsByName(BridgeTraffic_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + def retranslateUi(self, BridgeTraffic_Dialog): + _translate = QtCore.QCoreApplication.translate + BridgeTraffic_Dialog.setWindowTitle(_translate("BridgeTraffic_Dialog", "Bridge and Traffic Data")) + self.label_16.setText(_translate("BridgeTraffic_Dialog", "Input Parameters")) + self.pushButton_34.setText(_translate("BridgeTraffic_Dialog", "Structure Works Data")) + self.pushButton_35.setText(_translate("BridgeTraffic_Dialog", "Foundation")) + self.pushButton_36.setText(_translate("BridgeTraffic_Dialog", "Super-Structure")) + self.pushButton_37.setText(_translate("BridgeTraffic_Dialog", "Sub-Structure")) + self.pushButton_38.setText(_translate("BridgeTraffic_Dialog", "Miscellaneous")) + self.pushButton_39.setText(_translate("BridgeTraffic_Dialog", "Financial Data")) + self.pushButton_40.setText(_translate("BridgeTraffic_Dialog", "Carbon Emission Data")) + self.pushButton_41.setText(_translate("BridgeTraffic_Dialog", "Carbon Emission Cost Data")) + self.pushButton_42.setText(_translate("BridgeTraffic_Dialog", "Bridge and Traffic Data")) + self.pushButton_43.setText(_translate("BridgeTraffic_Dialog", "Maintenance and Repair")) + self.pushButton_12.setText(_translate("BridgeTraffic_Dialog", "Disposal and Recycling")) + self.label_17.setText(_translate("BridgeTraffic_Dialog", "Output")) + self.textBrowser_3.setHtml(_translate("BridgeTraffic_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + self.label_18.setText(_translate("BridgeTraffic_Dialog", "Number of Lanes")) + self.label_19.setText(_translate("BridgeTraffic_Dialog", "Road Roughness")) + self.label_20.setText(_translate("BridgeTraffic_Dialog", "Additional Re-Route Distance")) + self.label_29.setText(_translate("BridgeTraffic_Dialog", "Road Rise and Fall (RF)")) + self.label_30.setText(_translate("BridgeTraffic_Dialog", "Type of Road")) + self.label_31.setText(_translate("BridgeTraffic_Dialog", "(%)")) + self.label_32.setText(_translate("BridgeTraffic_Dialog", "(km)")) + self.label_33.setText(_translate("BridgeTraffic_Dialog", "(mm/km)")) + self.label_34.setText(_translate("BridgeTraffic_Dialog", "(m/km)")) + self.textBrowser_4.setHtml(_translate("BridgeTraffic_Dialog", "\n" +"\n" +"

Annual Increaase in Traffic if Re-Routing duration increases more than a year

")) + self.label_35.setText(_translate("BridgeTraffic_Dialog", "Composition of Various Vehicles")) + self.label_40.setText(_translate("BridgeTraffic_Dialog", "LCV:")) + self.label_36.setText(_translate("BridgeTraffic_Dialog", "Cars:")) + self.label_37.setText(_translate("BridgeTraffic_Dialog", "Buses:")) + self.label_39.setText(_translate("BridgeTraffic_Dialog", "HCV:")) + self.label_38.setText(_translate("BridgeTraffic_Dialog", "MCV:")) + self.label_41.setText(_translate("BridgeTraffic_Dialog", "(PCU/D)")) + self.pushButton.setText(_translate("BridgeTraffic_Dialog", "Project Details Window ")) + self.pushButton_6.setText(_translate("BridgeTraffic_Dialog", "Bridge and Traffic Data ")) + + + def validate_data(self): + """Validate input data before saving""" + try: + # Check numeric fields + float(self.lineEdit_9.text()) # Additional re-route distance + float(self.lineEdit_10.text()) # Road rise and fall + + # Check vehicle composition percentages + vehicle_fields = [ + self.lineEdit_11.text(), # Cars + self.lineEdit_12.text(), # Buses + self.lineEdit_15.text(), # HCV + self.lineEdit_16.text(), # MCV + self.lineEdit_17.text() # LCV + ] + + total = sum(float(x) for x in vehicle_fields if x) + if total > 100: + QMessageBox.warning( + None, + "Validation Error", + "Vehicle composition percentages cannot exceed 100%", + QMessageBox.Ok + ) + return False + + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return + + data = { + # Bridge and Traffic Data + "number_of_lanes": self.comboBox_7.currentText(), + "additional_reroute_distance": self.lineEdit_9.text(), + "road_roughness": self.comboBox_6.currentText(), + "road_rise_and_fall": self.lineEdit_10.text(), + "type_of_road": self.comboBox_8.currentText(), + "annual_traffic_increase": self.comboBox_9.currentText(), + + # Vehicle Composition + "cars": self.lineEdit_11.text(), + "buses": self.lineEdit_12.text(), + "hcv": self.lineEdit_15.text(), + "mcv": self.lineEdit_16.text(), + "lcv": self.lineEdit_17.text(), + } + + # Save to JSON file + try: + import json + from datetime import datetime + + # Create filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = "src//osbridgelcca//desktop_app//assets//"+f"bridge_traffic_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + BridgeTraffic_Dialog = QtWidgets.QDialog() + ui = Ui_BridgeTraffic_Dialog() + ui.setupUi(BridgeTraffic_Dialog) + BridgeTraffic_Dialog.show() + sys.exit(app.exec_()) diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_CarbonEmissionData_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_CarbonEmissionData_Window.py new file mode 100644 index 0000000..7892205 --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_CarbonEmissionData_Window.py @@ -0,0 +1,987 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_CarbonEmissionData_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox + # Add this import at the top + + +#from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + + + + + + + + +class Ui_CarbonEmission_Dialog(object): + def openBridgeTrafficWindow(self): + self.window = QtWidgets.QDialog() + from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, CarbonEmission_Dialog): + CarbonEmission_Dialog.setObjectName("CarbonEmission_Dialog") + CarbonEmission_Dialog.resize(1440, 900) + self.buttonBox = QtWidgets.QDialogButtonBox(CarbonEmission_Dialog) + self.buttonBox.setGeometry(QtCore.QRect(540, 690, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close | QtWidgets.QDialogButtonBox.Save) + self.buttonBox.setObjectName("buttonBox") + self.label = QtWidgets.QLabel(CarbonEmission_Dialog) + self.label.setGeometry(QtCore.QRect(10, 60, 244, 691)) + font = QtGui.QFont() + font.setPointSize(10) + self.label.setFont(font) + self.label.setStyleSheet("background-color: rgb(240,230,230)") + self.label.setText("") + self.label.setObjectName("label") + self.pushButton = QtWidgets.QPushButton(CarbonEmission_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 35, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton.setIcon(icon) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + self.widget_2 = QtWidgets.QWidget(CarbonEmission_Dialog) + self.widget_2.setGeometry(QtCore.QRect(350, 60, 778, 708)) + self.widget_2.setStyleSheet("background-color: #fff9f9") + self.widget_2.setObjectName("widget_2") + self.label_56 = QtWidgets.QLabel(self.widget_2) + self.label_56.setGeometry(QtCore.QRect(20, 10, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_56.setFont(font) + self.label_56.setObjectName("label_56") + self.comboBox_19 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_19.setGeometry(QtCore.QRect(140, 10, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_19.setFont(font) + self.comboBox_19.setStyleSheet("background-color: #ffffff") + self.comboBox_19.setObjectName("comboBox_19") + self.comboBox_19.addItem("") + self.comboBox_19.addItem("") + self.label_57 = QtWidgets.QLabel(self.widget_2) + self.label_57.setGeometry(QtCore.QRect(20, 70, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_57.setFont(font) + self.label_57.setAlignment(QtCore.Qt.AlignCenter) + self.label_57.setObjectName("label_57") + self.label_58 = QtWidgets.QLabel(self.widget_2) + self.label_58.setGeometry(QtCore.QRect(601, 70, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_58.setFont(font) + self.label_58.setAlignment(QtCore.Qt.AlignCenter) + self.label_58.setObjectName("label_58") + self.label_59 = QtWidgets.QLabel(self.widget_2) + self.label_59.setGeometry(QtCore.QRect(191, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_59.setFont(font) + self.label_59.setAlignment(QtCore.Qt.AlignCenter) + self.label_59.setObjectName("label_59") + self.label_60 = QtWidgets.QLabel(self.widget_2) + self.label_60.setGeometry(QtCore.QRect(311, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_60.setFont(font) + self.label_60.setAlignment(QtCore.Qt.AlignCenter) + self.label_60.setObjectName("label_60") + self.label_61 = QtWidgets.QLabel(self.widget_2) + self.label_61.setGeometry(QtCore.QRect(440, 70, 151, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_61.setFont(font) + self.label_61.setAlignment(QtCore.Qt.AlignCenter) + self.label_61.setObjectName("label_61") + self.comboBox_20 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_20.setGeometry(QtCore.QRect(30, 100, 140, 22)) + self.comboBox_20.setStyleSheet("background-color: #ffffff") + self.comboBox_20.setObjectName("comboBox_20") + self.comboBox_20.addItem("") + self.comboBox_20.addItem("") + self.comboBox_21 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_21.setGeometry(QtCore.QRect(30, 130, 140, 22)) + self.comboBox_21.setStyleSheet("background-color: #ffffff") + self.comboBox_21.setObjectName("comboBox_21") + self.comboBox_21.addItem("") + self.comboBox_21.addItem("") + self.lineEdit_37 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_37.setGeometry(QtCore.QRect(210, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_37.setFont(font) + self.lineEdit_37.setStyleSheet("background-color: #ffffff") + self.lineEdit_37.setObjectName("lineEdit_37") + self.lineEdit_38 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_38.setGeometry(QtCore.QRect(210, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_38.setFont(font) + self.lineEdit_38.setStyleSheet("background-color: #ffffff") + self.lineEdit_38.setObjectName("lineEdit_38") + self.label_62 = QtWidgets.QLabel(self.widget_2) + self.label_62.setGeometry(QtCore.QRect(340, 100, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_62.setFont(font) + self.label_62.setStyleSheet("background-color: #ffffff") + self.label_62.setAlignment(QtCore.Qt.AlignCenter) + self.label_62.setObjectName("label_62") + self.label_63 = QtWidgets.QLabel(self.widget_2) + self.label_63.setGeometry(QtCore.QRect(340, 130, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_63.setFont(font) + self.label_63.setStyleSheet("background-color: #ffffff") + self.label_63.setAlignment(QtCore.Qt.AlignCenter) + self.label_63.setObjectName("label_63") + self.lineEdit_39 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_39.setGeometry(QtCore.QRect(450, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_39.setFont(font) + self.lineEdit_39.setStyleSheet("background-color: #ffffff") + self.lineEdit_39.setObjectName("lineEdit_39") + self.lineEdit_40 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_40.setGeometry(QtCore.QRect(450, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_40.setFont(font) + self.lineEdit_40.setStyleSheet("background-color: #ffffff") + self.lineEdit_40.setObjectName("lineEdit_40") + self.lineEdit_41 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_41.setGeometry(QtCore.QRect(610, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_41.setFont(font) + self.lineEdit_41.setStyleSheet("background-color: #ffffff") + self.lineEdit_41.setObjectName("lineEdit_41") + self.lineEdit_42 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_42.setGeometry(QtCore.QRect(610, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_42.setFont(font) + self.lineEdit_42.setStyleSheet("background-color: #ffffff") + self.lineEdit_42.setObjectName("lineEdit_42") + # self.pushButton_13 = QtWidgets.QPushButton(self.widget_2) + # self.pushButton_13.setGeometry(QtCore.QRect(300, 200, 190, 23)) + # self.pushButton_13.setStyleSheet("background-color: #ffffff") + # self.pushButton_13.setObjectName("pushButton_13") + # self.pushButton_49 = QtWidgets.QPushButton(self.widget_2) + # self.pushButton_49.setGeometry(QtCore.QRect(310, 440, 190, 23)) + # self.pushButton_49.setStyleSheet("background-color: #ffffff") + # self.pushButton_49.setObjectName("pushButton_49") + self.buttonBox_5 = QtWidgets.QDialogButtonBox(self.widget_2) + self.buttonBox_5.setGeometry(QtCore.QRect(430, 670, 341, 32)) + self.buttonBox_5.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox_5.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox_5.setCenterButtons(False) + self.buttonBox_5.setObjectName("buttonBox_5") + self.line_7 = QtWidgets.QFrame(self.widget_2) + self.line_7.setGeometry(QtCore.QRect(10, 230, 761, 16)) + self.line_7.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_7.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_7.setLineWidth(2) + self.line_7.setMidLineWidth(2) + self.line_7.setFrameShape(QtWidgets.QFrame.HLine) + self.line_7.setObjectName("line_7") + self.line_8 = QtWidgets.QFrame(self.widget_2) + self.line_8.setGeometry(QtCore.QRect(10, 470, 761, 16)) + self.line_8.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_8.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_8.setLineWidth(2) + self.line_8.setMidLineWidth(2) + self.line_8.setFrameShape(QtWidgets.QFrame.HLine) + self.line_8.setObjectName("line_8") + self.label_74 = QtWidgets.QLabel(self.widget_2) + self.label_74.setGeometry(QtCore.QRect(540, 100, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_74.setFont(font) + self.label_74.setAlignment(QtCore.Qt.AlignCenter) + self.label_74.setObjectName("label_74") + self.label_75 = QtWidgets.QLabel(self.widget_2) + self.label_75.setGeometry(QtCore.QRect(540, 130, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_75.setFont(font) + self.label_75.setAlignment(QtCore.Qt.AlignCenter) + self.label_75.setObjectName("label_75") + self.label_76 = QtWidgets.QLabel(self.widget_2) + self.label_76.setGeometry(QtCore.QRect(700, 100, 71, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_76.setFont(font) + self.label_76.setAlignment(QtCore.Qt.AlignCenter) + self.label_76.setObjectName("label_76") + self.label_77 = QtWidgets.QLabel(self.widget_2) + self.label_77.setGeometry(QtCore.QRect(700, 130, 71, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_77.setFont(font) + self.label_77.setAlignment(QtCore.Qt.AlignCenter) + self.label_77.setObjectName("label_77") + self.label_78 = QtWidgets.QLabel(self.widget_2) + self.label_78.setGeometry(QtCore.QRect(539, 340, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_78.setFont(font) + self.label_78.setAlignment(QtCore.Qt.AlignCenter) + self.label_78.setObjectName("label_78") + self.label_79 = QtWidgets.QLabel(self.widget_2) + self.label_79.setGeometry(QtCore.QRect(699, 370, 71, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_79.setFont(font) + self.label_79.setAlignment(QtCore.Qt.AlignCenter) + self.label_79.setObjectName("label_79") + self.label_64 = QtWidgets.QLabel(self.widget_2) + self.label_64.setGeometry(QtCore.QRect(339, 340, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_64.setFont(font) + self.label_64.setStyleSheet("background-color: #ffffff") + self.label_64.setAlignment(QtCore.Qt.AlignCenter) + self.label_64.setObjectName("label_64") + self.label_65 = QtWidgets.QLabel(self.widget_2) + self.label_65.setGeometry(QtCore.QRect(339, 370, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_65.setFont(font) + self.label_65.setStyleSheet("background-color: #ffffff") + self.label_65.setAlignment(QtCore.Qt.AlignCenter) + self.label_65.setObjectName("label_65") + self.lineEdit_43 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_43.setGeometry(QtCore.QRect(609, 370, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_43.setFont(font) + self.lineEdit_43.setStyleSheet("background-color: #ffffff") + self.lineEdit_43.setObjectName("lineEdit_43") + self.lineEdit_44 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_44.setGeometry(QtCore.QRect(209, 370, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_44.setFont(font) + self.lineEdit_44.setStyleSheet("background-color: #ffffff") + self.lineEdit_44.setObjectName("lineEdit_44") + self.label_80 = QtWidgets.QLabel(self.widget_2) + self.label_80.setGeometry(QtCore.QRect(699, 340, 71, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_80.setFont(font) + self.label_80.setAlignment(QtCore.Qt.AlignCenter) + self.label_80.setObjectName("label_80") + self.label_66 = QtWidgets.QLabel(self.widget_2) + self.label_66.setGeometry(QtCore.QRect(19, 250, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_66.setFont(font) + self.label_66.setObjectName("label_66") + self.lineEdit_45 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_45.setGeometry(QtCore.QRect(449, 370, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_45.setFont(font) + self.lineEdit_45.setStyleSheet("background-color: #ffffff") + self.lineEdit_45.setObjectName("lineEdit_45") + self.label_67 = QtWidgets.QLabel(self.widget_2) + self.label_67.setGeometry(QtCore.QRect(19, 310, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_67.setFont(font) + self.label_67.setAlignment(QtCore.Qt.AlignCenter) + self.label_67.setObjectName("label_67") + self.label_81 = QtWidgets.QLabel(self.widget_2) + self.label_81.setGeometry(QtCore.QRect(539, 370, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_81.setFont(font) + self.label_81.setAlignment(QtCore.Qt.AlignCenter) + self.label_81.setObjectName("label_81") + self.label_68 = QtWidgets.QLabel(self.widget_2) + self.label_68.setGeometry(QtCore.QRect(600, 310, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_68.setFont(font) + self.label_68.setAlignment(QtCore.Qt.AlignCenter) + self.label_68.setObjectName("label_68") + self.label_69 = QtWidgets.QLabel(self.widget_2) + self.label_69.setGeometry(QtCore.QRect(190, 310, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_69.setFont(font) + self.label_69.setAlignment(QtCore.Qt.AlignCenter) + self.label_69.setObjectName("label_69") + self.comboBox_22 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_22.setGeometry(QtCore.QRect(29, 340, 140, 22)) + self.comboBox_22.setStyleSheet("background-color: #ffffff") + self.comboBox_22.setObjectName("comboBox_22") + self.comboBox_22.addItem("") + self.comboBox_22.addItem("") + self.label_70 = QtWidgets.QLabel(self.widget_2) + self.label_70.setGeometry(QtCore.QRect(439, 310, 151, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_70.setFont(font) + self.label_70.setAlignment(QtCore.Qt.AlignCenter) + self.label_70.setObjectName("label_70") + self.lineEdit_46 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_46.setGeometry(QtCore.QRect(449, 340, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_46.setFont(font) + self.lineEdit_46.setStyleSheet("background-color: #ffffff") + self.lineEdit_46.setObjectName("lineEdit_46") + self.comboBox_23 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_23.setGeometry(QtCore.QRect(29, 370, 140, 22)) + self.comboBox_23.setStyleSheet("background-color: #ffffff") + self.comboBox_23.setObjectName("comboBox_23") + self.comboBox_23.addItem("") + self.comboBox_23.addItem("") + self.comboBox_24 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_24.setGeometry(QtCore.QRect(139, 250, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_24.setFont(font) + self.comboBox_24.setStyleSheet("background-color: #ffffff") + self.comboBox_24.setObjectName("comboBox_24") + self.comboBox_24.addItem("") + self.comboBox_24.addItem("") + self.lineEdit_47 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_47.setGeometry(QtCore.QRect(609, 340, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_47.setFont(font) + self.lineEdit_47.setStyleSheet("background-color: #ffffff") + self.lineEdit_47.setObjectName("lineEdit_47") + self.lineEdit_48 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_48.setGeometry(QtCore.QRect(209, 340, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_48.setFont(font) + self.lineEdit_48.setStyleSheet("background-color: #ffffff") + self.lineEdit_48.setObjectName("lineEdit_48") + self.label_71 = QtWidgets.QLabel(self.widget_2) + self.label_71.setGeometry(QtCore.QRect(310, 310, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_71.setFont(font) + self.label_71.setAlignment(QtCore.Qt.AlignCenter) + self.label_71.setObjectName("label_71") + self.label_82 = QtWidgets.QLabel(self.widget_2) + self.label_82.setGeometry(QtCore.QRect(539, 580, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_82.setFont(font) + self.label_82.setAlignment(QtCore.Qt.AlignCenter) + self.label_82.setObjectName("label_82") + self.label_83 = QtWidgets.QLabel(self.widget_2) + self.label_83.setGeometry(QtCore.QRect(699, 610, 71, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_83.setFont(font) + self.label_83.setAlignment(QtCore.Qt.AlignCenter) + self.label_83.setObjectName("label_83") + self.label_84 = QtWidgets.QLabel(self.widget_2) + self.label_84.setGeometry(QtCore.QRect(339, 580, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_84.setFont(font) + self.label_84.setStyleSheet("background-color: #ffffff") + self.label_84.setAlignment(QtCore.Qt.AlignCenter) + self.label_84.setObjectName("label_84") + self.label_85 = QtWidgets.QLabel(self.widget_2) + self.label_85.setGeometry(QtCore.QRect(339, 610, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_85.setFont(font) + self.label_85.setStyleSheet("background-color: #ffffff") + self.label_85.setAlignment(QtCore.Qt.AlignCenter) + self.label_85.setObjectName("label_85") + self.lineEdit_49 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_49.setGeometry(QtCore.QRect(609, 610, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_49.setFont(font) + self.lineEdit_49.setStyleSheet("background-color: #ffffff") + self.lineEdit_49.setObjectName("lineEdit_49") + self.lineEdit_50 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_50.setGeometry(QtCore.QRect(209, 610, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_50.setFont(font) + self.lineEdit_50.setStyleSheet("background-color: #ffffff") + self.lineEdit_50.setObjectName("lineEdit_50") + self.label_86 = QtWidgets.QLabel(self.widget_2) + self.label_86.setGeometry(QtCore.QRect(699, 580, 71, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_86.setFont(font) + self.label_86.setAlignment(QtCore.Qt.AlignCenter) + self.label_86.setObjectName("label_86") + self.label_87 = QtWidgets.QLabel(self.widget_2) + self.label_87.setGeometry(QtCore.QRect(19, 490, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_87.setFont(font) + self.label_87.setObjectName("label_87") + self.lineEdit_51 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_51.setGeometry(QtCore.QRect(449, 610, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_51.setFont(font) + self.lineEdit_51.setStyleSheet("background-color: #ffffff") + self.lineEdit_51.setObjectName("lineEdit_51") + self.label_88 = QtWidgets.QLabel(self.widget_2) + self.label_88.setGeometry(QtCore.QRect(19, 550, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_88.setFont(font) + self.label_88.setAlignment(QtCore.Qt.AlignCenter) + self.label_88.setObjectName("label_88") + self.label_89 = QtWidgets.QLabel(self.widget_2) + self.label_89.setGeometry(QtCore.QRect(539, 610, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_89.setFont(font) + self.label_89.setAlignment(QtCore.Qt.AlignCenter) + self.label_89.setObjectName("label_89") + self.label_90 = QtWidgets.QLabel(self.widget_2) + self.label_90.setGeometry(QtCore.QRect(600, 550, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_90.setFont(font) + self.label_90.setAlignment(QtCore.Qt.AlignCenter) + self.label_90.setObjectName("label_90") + self.label_91 = QtWidgets.QLabel(self.widget_2) + self.label_91.setGeometry(QtCore.QRect(190, 550, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_91.setFont(font) + self.label_91.setAlignment(QtCore.Qt.AlignCenter) + self.label_91.setObjectName("label_91") + self.comboBox_25 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_25.setGeometry(QtCore.QRect(29, 580, 140, 22)) + self.comboBox_25.setStyleSheet("background-color: #ffffff") + self.comboBox_25.setObjectName("comboBox_25") + self.comboBox_25.addItem("") + self.comboBox_25.addItem("") + self.label_92 = QtWidgets.QLabel(self.widget_2) + self.label_92.setGeometry(QtCore.QRect(439, 550, 151, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_92.setFont(font) + self.label_92.setAlignment(QtCore.Qt.AlignCenter) + self.label_92.setObjectName("label_92") + self.lineEdit_52 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_52.setGeometry(QtCore.QRect(449, 580, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_52.setFont(font) + self.lineEdit_52.setStyleSheet("background-color: #ffffff") + self.lineEdit_52.setObjectName("lineEdit_52") + self.comboBox_26 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_26.setGeometry(QtCore.QRect(29, 610, 140, 22)) + self.comboBox_26.setStyleSheet("background-color: #ffffff") + self.comboBox_26.setObjectName("comboBox_26") + self.comboBox_26.addItem("") + self.comboBox_26.addItem("") + self.comboBox_27 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_27.setGeometry(QtCore.QRect(139, 490, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_27.setFont(font) + self.comboBox_27.setStyleSheet("background-color: #ffffff") + self.comboBox_27.setObjectName("comboBox_27") + self.comboBox_27.addItem("") + self.comboBox_27.addItem("") + self.lineEdit_53 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_53.setGeometry(QtCore.QRect(609, 580, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_53.setFont(font) + self.lineEdit_53.setStyleSheet("background-color: #ffffff") + self.lineEdit_53.setObjectName("lineEdit_53") + self.lineEdit_54 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_54.setGeometry(QtCore.QRect(209, 580, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_54.setFont(font) + self.lineEdit_54.setStyleSheet("background-color: #ffffff") + self.lineEdit_54.setObjectName("lineEdit_54") + self.label_93 = QtWidgets.QLabel(self.widget_2) + self.label_93.setGeometry(QtCore.QRect(310, 550, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_93.setFont(font) + self.label_93.setAlignment(QtCore.Qt.AlignCenter) + self.label_93.setObjectName("label_93") + self.pushButton_50 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_50.setGeometry(QtCore.QRect(310, 670, 190, 23)) + self.pushButton_50.setStyleSheet("background-color: #ffffff") + self.pushButton_50.setObjectName("pushButton_50") + self.scrollArea = QtWidgets.QScrollArea(CarbonEmission_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 65, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents_4 = QtWidgets.QWidget() + self.scrollAreaWidgetContents_4.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents_4.setObjectName("scrollAreaWidgetContents_4") + self.label_72 = QtWidgets.QLabel(self.scrollAreaWidgetContents_4) + self.label_72.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_72.setFont(font) + self.label_72.setStyleSheet("background-color: rgb(240,230,230)") + self.label_72.setAlignment(QtCore.Qt.AlignCenter) + self.label_72.setObjectName("label_72") + self.widget_11 = QtWidgets.QWidget(self.scrollAreaWidgetContents_4) + self.widget_11.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget_11.setStyleSheet("background-color: #fff9f9") + self.widget_11.setObjectName("widget_11") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.widget_11) + self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.pushButton_51 = QtWidgets.QPushButton(self.widget_11) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_51.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_51.setIcon(icon1) + self.pushButton_51.setCheckable(True) + self.pushButton_51.setAutoDefault(True) + self.pushButton_51.setObjectName("pushButton_51") + self.verticalLayout_4.addWidget(self.pushButton_51) + self.widget_12 = QtWidgets.QWidget(self.widget_11) + self.widget_12.setObjectName("widget_12") + self.formLayout_4 = QtWidgets.QFormLayout(self.widget_12) + self.formLayout_4.setObjectName("formLayout_4") + self.pushButton_52 = QtWidgets.QPushButton(self.widget_12, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_52.setFont(font) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_52.setIcon(icon2) + self.pushButton_52.setObjectName("pushButton_52") + self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_52) + self.pushButton_53 = QtWidgets.QPushButton(self.widget_12, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_53.setFont(font) + self.pushButton_53.setIcon(icon2) + self.pushButton_53.setObjectName("pushButton_53") + self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_53) + self.pushButton_54 = QtWidgets.QPushButton(self.widget_12, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_54.setFont(font) + self.pushButton_54.setIcon(icon2) + self.pushButton_54.setObjectName("pushButton_54") + self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_54) + self.pushButton_55 = QtWidgets.QPushButton(self.widget_12, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_55.setFont(font) + self.pushButton_55.setIcon(icon2) + self.pushButton_55.setObjectName("pushButton_55") + self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_55) + self.verticalLayout_4.addWidget(self.widget_12) + self.pushButton_56 = QtWidgets.QPushButton(self.widget_11, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_56.setFont(font) + self.pushButton_56.setObjectName("pushButton_56") + self.verticalLayout_4.addWidget(self.pushButton_56) + self.pushButton_57 = QtWidgets.QPushButton(self.widget_11) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_57.setFont(font) + self.pushButton_57.setIcon(icon1) + self.pushButton_57.setCheckable(True) + self.pushButton_57.setObjectName("pushButton_57") + self.verticalLayout_4.addWidget(self.pushButton_57) + self.widget_13 = QtWidgets.QWidget(self.widget_11) + self.widget_13.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_13.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_13.setObjectName("widget_13") + self.pushButton_58 = QtWidgets.QPushButton(self.widget_13, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_58.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_58.setFont(font) + self.pushButton_58.setIcon(icon2) + self.pushButton_58.setObjectName("pushButton_58") + self.verticalLayout_4.addWidget(self.widget_13) + self.pushButton_59 = QtWidgets.QPushButton(self.widget_11, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_59.setFont(font) + self.pushButton_59.setObjectName("pushButton_59") + self.verticalLayout_4.addWidget(self.pushButton_59) + self.pushButton_60 = QtWidgets.QPushButton(self.widget_11, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_60.setFont(font) + self.pushButton_60.setObjectName("pushButton_60") + self.verticalLayout_4.addWidget(self.pushButton_60) + self.pushButton_61 = QtWidgets.QPushButton(self.widget_11, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_61.setFont(font) + self.pushButton_61.setObjectName("pushButton_61") + self.verticalLayout_4.addWidget(self.pushButton_61) + self.label_73 = QtWidgets.QLabel(self.scrollAreaWidgetContents_4) + self.label_73.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_73.setFont(font) + self.label_73.setStyleSheet("background-color: rgb(240,230,230)") + self.label_73.setAlignment(QtCore.Qt.AlignCenter) + self.label_73.setObjectName("label_73") + self.textBrowser_4 = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents_4) + self.textBrowser_4.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser_4.setStyleSheet("background-color: #fff9f9") + self.textBrowser_4.setObjectName("textBrowser_4") + self.verticalScrollBar_4 = QtWidgets.QScrollBar(self.scrollAreaWidgetContents_4) + self.verticalScrollBar_4.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar_4.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar_4.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar_4.setObjectName("verticalScrollBar_4") + self.scrollArea.setWidget(self.scrollAreaWidgetContents_4) + self.pushButton_62 = QtWidgets.QPushButton(CarbonEmission_Dialog) + self.pushButton_62.setGeometry(QtCore.QRect(350, 35, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_62.setFont(font) + self.pushButton_62.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_62.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_62.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton_62.setIcon(icon) + self.pushButton_62.setAutoRepeat(False) + self.pushButton_62.setObjectName("pushButton_62") + + self.retranslateUi(CarbonEmission_Dialog) + #self.buttonBox_5.accepted.connect(CarbonEmission_Dialog.accept) # type: ignore + self.buttonBox_5.accepted.connect(self.handle_save) + self.buttonBox.rejected.connect(lambda: self.show_warning(CarbonEmission_Dialog)) + self.buttonBox_5.rejected.connect(lambda: self.show_warning(CarbonEmission_Dialog)) # type: ignore + self.pushButton_51.toggled['bool'].connect(self.widget_12.setVisible) # type: ignore + self.pushButton_57.toggled['bool'].connect(self.widget_13.setVisible) # type: ignore + #self.buttonBox.rejected.connect(self.show_warning) # type: ignore + QtCore.QMetaObject.connectSlotsByName(CarbonEmission_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + def retranslateUi(self, CarbonEmission_Dialog): + _translate = QtCore.QCoreApplication.translate + CarbonEmission_Dialog.setWindowTitle(_translate("CarbonEmission_Dialog", "Carbon Emission Data")) + self.pushButton.setText(_translate("CarbonEmission_Dialog", "Project Details Window ")) + self.label_56.setText(_translate("CarbonEmission_Dialog", "Component:")) + self.comboBox_19.setItemText(0, _translate("CarbonEmission_Dialog", "Earthwork")) + self.comboBox_19.setItemText(1, _translate("CarbonEmission_Dialog", "RCC in Foundation")) + self.label_57.setText(_translate("CarbonEmission_Dialog", "Material Type and Grade")) + self.label_58.setText(_translate("CarbonEmission_Dialog", "Carbon Emission Factor")) + self.label_59.setText(_translate("CarbonEmission_Dialog", "Quantity")) + self.label_60.setText(_translate("CarbonEmission_Dialog", "Unit")) + self.label_61.setText(_translate("CarbonEmission_Dialog", "Emboided Carbon Energy")) + self.comboBox_20.setItemText(0, _translate("CarbonEmission_Dialog", "Concrete")) + self.comboBox_20.setItemText(1, _translate("CarbonEmission_Dialog", "Steel")) + self.comboBox_21.setItemText(0, _translate("CarbonEmission_Dialog", "Steel")) + self.comboBox_21.setItemText(1, _translate("CarbonEmission_Dialog", "Concrete")) + self.label_62.setText(_translate("CarbonEmission_Dialog", "

m3

")) + self.label_63.setText(_translate("CarbonEmission_Dialog", "kg")) + #self.pushButton_13.setText(_translate("CarbonEmission_Dialog", "+ Add Material")) + #self.pushButton_49.setText(_translate("CarbonEmission_Dialog", "+ Add Material")) + self.label_74.setText(_translate("CarbonEmission_Dialog", "(MJ/kg)")) + self.label_75.setText(_translate("CarbonEmission_Dialog", "(MJ/kg)")) + self.label_76.setText(_translate("CarbonEmission_Dialog", "kg C02e/kg")) + self.label_77.setText(_translate("CarbonEmission_Dialog", "kg C02e/kg")) + self.label_78.setText(_translate("CarbonEmission_Dialog", "(MJ/kg)")) + self.label_79.setText(_translate("CarbonEmission_Dialog", "kg C02e/kg")) + self.label_64.setText(_translate("CarbonEmission_Dialog", "

m3

")) + self.label_65.setText(_translate("CarbonEmission_Dialog", "kg")) + self.label_80.setText(_translate("CarbonEmission_Dialog", "kg C02e/kg")) + self.label_66.setText(_translate("CarbonEmission_Dialog", "Component:")) + self.label_67.setText(_translate("CarbonEmission_Dialog", "Material Type and Grade")) + self.label_81.setText(_translate("CarbonEmission_Dialog", "(MJ/kg)")) + self.label_68.setText(_translate("CarbonEmission_Dialog", "Carbon Emission Factor")) + self.label_69.setText(_translate("CarbonEmission_Dialog", "Quantity")) + self.comboBox_22.setItemText(0, _translate("CarbonEmission_Dialog", "Concrete")) + self.comboBox_22.setItemText(1, _translate("CarbonEmission_Dialog", "Steel")) + self.label_70.setText(_translate("CarbonEmission_Dialog", "Emboided Carbon Energy")) + self.comboBox_23.setItemText(0, _translate("CarbonEmission_Dialog", "Steel")) + self.comboBox_23.setItemText(1, _translate("CarbonEmission_Dialog", "Concrete")) + self.comboBox_24.setItemText(0, _translate("CarbonEmission_Dialog", "Earthwork")) + self.comboBox_24.setItemText(1, _translate("CarbonEmission_Dialog", "RCC in Foundation")) + self.label_71.setText(_translate("CarbonEmission_Dialog", "Unit")) + self.label_82.setText(_translate("CarbonEmission_Dialog", "(MJ/kg)")) + self.label_83.setText(_translate("CarbonEmission_Dialog", "kg C02e/kg")) + self.label_84.setText(_translate("CarbonEmission_Dialog", "

m3

")) + self.label_85.setText(_translate("CarbonEmission_Dialog", "kg")) + self.label_86.setText(_translate("CarbonEmission_Dialog", "kg C02e/kg")) + self.label_87.setText(_translate("CarbonEmission_Dialog", "Component:")) + self.label_88.setText(_translate("CarbonEmission_Dialog", "Material Type and Grade")) + self.label_89.setText(_translate("CarbonEmission_Dialog", "(MJ/kg)")) + self.label_90.setText(_translate("CarbonEmission_Dialog", "Carbon Emission Factor")) + self.label_91.setText(_translate("CarbonEmission_Dialog", "Quantity")) + self.comboBox_25.setItemText(0, _translate("CarbonEmission_Dialog", "Concrete")) + self.comboBox_25.setItemText(1, _translate("CarbonEmission_Dialog", "Steel")) + self.label_92.setText(_translate("CarbonEmission_Dialog", "Emboided Carbon Energy")) + self.comboBox_26.setItemText(0, _translate("CarbonEmission_Dialog", "Steel")) + self.comboBox_26.setItemText(1, _translate("CarbonEmission_Dialog", "Concrete")) + self.comboBox_27.setItemText(0, _translate("CarbonEmission_Dialog", "Earthwork")) + self.comboBox_27.setItemText(1, _translate("CarbonEmission_Dialog", "RCC in Foundation")) + self.label_93.setText(_translate("CarbonEmission_Dialog", "Unit")) + self.pushButton_50.setText(_translate("CarbonEmission_Dialog", "+ Add Material")) + self.label_72.setText(_translate("CarbonEmission_Dialog", "Input Parameters")) + self.pushButton_51.setText(_translate("CarbonEmission_Dialog", "Structure Works Data")) + self.pushButton_52.setText(_translate("CarbonEmission_Dialog", "Foundation")) + self.pushButton_53.setText(_translate("CarbonEmission_Dialog", "Super-Structure")) + self.pushButton_54.setText(_translate("CarbonEmission_Dialog", "Sub-Structure")) + self.pushButton_55.setText(_translate("CarbonEmission_Dialog", "Miscellaneous")) + self.pushButton_56.setText(_translate("CarbonEmission_Dialog", "Financial Data")) + self.pushButton_57.setText(_translate("CarbonEmission_Dialog", "Carbon Emission Data")) + self.pushButton_58.setText(_translate("CarbonEmission_Dialog", "Carbon Emission Cost Data")) + self.pushButton_59.setText(_translate("CarbonEmission_Dialog", "Bridge and Traffic Data")) + self.pushButton_60.setText(_translate("CarbonEmission_Dialog", "Maintenance and Repair")) + self.pushButton_61.setText(_translate("CarbonEmission_Dialog", "Disposal and Recycling")) + self.label_73.setText(_translate("CarbonEmission_Dialog", "Output")) + self.textBrowser_4.setHtml(_translate("CarbonEmission_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + self.pushButton_62.setText(_translate("CarbonEmission_Dialog", "Carbon Emission Data ")) + + def validate_data(self): + """Validate input data before saving""" + try: + # Validate numeric fields + float(self.lineEdit_37.text()) if self.lineEdit_37.text() else 0 + float(self.lineEdit_38.text()) if self.lineEdit_38.text() else 0 + float(self.lineEdit_39.text()) if self.lineEdit_39.text() else 0 + float(self.lineEdit_40.text()) if self.lineEdit_40.text() else 0 + float(self.lineEdit_41.text()) if self.lineEdit_41.text() else 0 + float(self.lineEdit_42.text()) if self.lineEdit_42.text() else 0 + float(self.lineEdit_48.text()) if self.lineEdit_48.text() else 0 + float(self.lineEdit_44.text()) if self.lineEdit_44.text() else 0 + float(self.lineEdit_46.text()) if self.lineEdit_46.text() else 0 + float(self.lineEdit_47.text()) if self.lineEdit_47.text() else 0 + float(self.lineEdit_45.text()) if self.lineEdit_45.text() else 0 + float(self.lineEdit_54.text()) if self.lineEdit_54.text() else 0 + float(self.lineEdit_50.text()) if self.lineEdit_50.text() else 0 + float(self.lineEdit_52.text()) if self.lineEdit_52.text() else 0 + float(self.lineEdit_53.text()) if self.lineEdit_53.text() else 0 + float(self.lineEdit_51.text()) if self.lineEdit_51.text() else 0 + float(self.lineEdit_49.text()) if self.lineEdit_49.text() else 0 + + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all numeric fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return False + + data = { + # First component section + "component_1": self.comboBox_19.currentText(), + "material_type_1": self.comboBox_20.currentText(), + "material_grade_1": self.comboBox_21.currentText(), + "quantity_1": self.lineEdit_37.text(), + "unit_1": self.lineEdit_38.text(), + "embodied_carbon_energy_1": self.lineEdit_39.text(), + "carbon_emission_factor_1": self.lineEdit_41.text(), + + # Second component section + "component_2": self.comboBox_24.currentText(), + "material_type_2": self.comboBox_22.currentText(), + "material_grade_2": self.comboBox_23.currentText(), + "quantity_2": self.lineEdit_48.text(), + "unit_2": self.lineEdit_44.text(), + "embodied_carbon_energy_2": self.lineEdit_46.text(), + "carbon_emission_factor_2": self.lineEdit_47.text(), + + # Third component section + "component_3": self.comboBox_27.currentText(), + "material_type_3": self.comboBox_25.currentText(), + "material_grade_3": self.comboBox_26.currentText(), + "quantity_3": self.lineEdit_54.text(), + "unit_3": self.lineEdit_50.text(), + "embodied_carbon_energy_3": self.lineEdit_52.text(), + "carbon_emission_factor_3": self.lineEdit_53.text(), + } + + # Save to JSON file + try: + import json + from datetime import datetime + + # Create filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"carbon_emission_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Carbon emission data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + def handle_save(self): + """Handle the save operation and close the dialog if successful""" + if self.save_data(): # Only close if save was successful + CarbonEmission_Dialog.accept() + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + CarbonEmission_Dialog = QtWidgets.QDialog() + ui = Ui_CarbonEmission_Dialog() + ui.setupUi(CarbonEmission_Dialog) + CarbonEmission_Dialog.show() + sys.exit(app.exec_()) diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_DemolitionANDRecyclingData_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_DemolitionANDRecyclingData_Window.py new file mode 100644 index 0000000..a9167b2 --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_DemolitionANDRecyclingData_Window.py @@ -0,0 +1,486 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_Demolition&RecyclingData_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox + + + +#from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + + + + + + + +class Ui_Demolition_Dialog(object): + def openBridgeTrafficWindow(self): + from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, Demolition_Dialog): + Demolition_Dialog.setObjectName("Demolition_Dialog") + Demolition_Dialog.resize(1440, 900) + Demolition_Dialog.setStyleSheet("background-color: #fafafa") + self.pushButton_6 = QtWidgets.QPushButton(Demolition_Dialog) + self.pushButton_6.setGeometry(QtCore.QRect(350, 30, 261, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_6.setFont(font) + self.pushButton_6.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_6.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_6.setStyleSheet("background-color: rgb(240,230,230)") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_6.setIcon(icon) + self.pushButton_6.setAutoRepeat(False) + self.pushButton_6.setObjectName("pushButton_6") + self.label = QtWidgets.QLabel(Demolition_Dialog) + self.label.setGeometry(QtCore.QRect(10, 55, 244, 691)) + font = QtGui.QFont() + font.setPointSize(10) + self.label.setFont(font) + self.label.setStyleSheet("background-color: rgb(240,230,230)") + self.label.setText("") + self.label.setObjectName("label") + self.widget_2 = QtWidgets.QWidget(Demolition_Dialog) + self.widget_2.setGeometry(QtCore.QRect(350, 55, 692, 205)) + self.widget_2.setStyleSheet("background-color: #fff9f9") + self.widget_2.setObjectName("widget_2") + self.label_7 = QtWidgets.QLabel(self.widget_2) + self.label_7.setGeometry(QtCore.QRect(20, 110, 231, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_7.setFont(font) + self.label_7.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_7.setObjectName("label_7") + self.label_8 = QtWidgets.QLabel(self.widget_2) + self.label_8.setGeometry(QtCore.QRect(20, 140, 221, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_8.setFont(font) + self.label_8.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_8.setObjectName("label_8") + self.lineEdit_5 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_5.setGeometry(QtCore.QRect(290, 30, 121, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_5.setFont(font) + self.lineEdit_5.setStyleSheet("background-color: #ffffff") + self.lineEdit_5.setObjectName("lineEdit_5") + self.lineEdit_6 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_6.setGeometry(QtCore.QRect(290, 110, 121, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_6.setFont(font) + self.lineEdit_6.setStyleSheet("background-color: #ffffff") + self.lineEdit_6.setText("") + self.lineEdit_6.setObjectName("lineEdit_6") + self.buttonBox_2 = QtWidgets.QDialogButtonBox(self.widget_2) + self.buttonBox_2.setGeometry(QtCore.QRect(340, 170, 341, 32)) + self.buttonBox_2.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox_2.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox_2.setObjectName("buttonBox_2") + self.lineEdit_13 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_13.setGeometry(QtCore.QRect(290, 140, 121, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_13.setFont(font) + self.lineEdit_13.setStyleSheet("background-color: #ffffff") + self.lineEdit_13.setObjectName("lineEdit_13") + self.label_21 = QtWidgets.QLabel(self.widget_2) + self.label_21.setGeometry(QtCore.QRect(420, 30, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_21.setFont(font) + self.label_21.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_21.setObjectName("label_21") + self.label_23 = QtWidgets.QLabel(self.widget_2) + self.label_23.setGeometry(QtCore.QRect(420, 110, 71, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_23.setFont(font) + self.label_23.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_23.setObjectName("label_23") + self.textBrowser_2 = QtWidgets.QTextBrowser(self.widget_2) + self.textBrowser_2.setGeometry(QtCore.QRect(20, 20, 256, 51)) + self.textBrowser_2.setObjectName("textBrowser_2") + self.label_22 = QtWidgets.QLabel(self.widget_2) + self.label_22.setGeometry(QtCore.QRect(420, 140, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_22.setFont(font) + self.label_22.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_22.setObjectName("label_22") + self.pushButton = QtWidgets.QPushButton(Demolition_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 30, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton.setIcon(icon) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + self.scrollArea = QtWidgets.QScrollArea(Demolition_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 60, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.label_2 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_2.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_2.setFont(font) + self.label_2.setStyleSheet("background-color: rgb(240,230,230)") + self.label_2.setAlignment(QtCore.Qt.AlignCenter) + self.label_2.setObjectName("label_2") + self.widget = QtWidgets.QWidget(self.scrollAreaWidgetContents) + self.widget.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget.setStyleSheet("background-color: #fff9f9") + self.widget.setObjectName("widget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.widget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.pushButton_15 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_15.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_15.setIcon(icon1) + self.pushButton_15.setCheckable(True) + self.pushButton_15.setAutoDefault(True) + self.pushButton_15.setObjectName("pushButton_15") + self.verticalLayout.addWidget(self.pushButton_15) + self.widget_5 = QtWidgets.QWidget(self.widget) + self.widget_5.setObjectName("widget_5") + self.formLayout = QtWidgets.QFormLayout(self.widget_5) + self.formLayout.setObjectName("formLayout") + self.pushButton_20 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_20.setFont(font) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_20.setIcon(icon2) + self.pushButton_20.setObjectName("pushButton_20") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_20) + self.pushButton_21 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_21.setFont(font) + self.pushButton_21.setIcon(icon2) + self.pushButton_21.setObjectName("pushButton_21") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_21) + self.pushButton_22 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_22.setFont(font) + self.pushButton_22.setIcon(icon2) + self.pushButton_22.setObjectName("pushButton_22") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_22) + self.pushButton_23 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_23.setFont(font) + self.pushButton_23.setIcon(icon2) + self.pushButton_23.setObjectName("pushButton_23") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_23) + self.verticalLayout.addWidget(self.widget_5) + self.pushButton_19 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_19.setFont(font) + self.pushButton_19.setObjectName("pushButton_19") + self.verticalLayout.addWidget(self.pushButton_19) + self.pushButton_16 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_16.setFont(font) + self.pushButton_16.setIcon(icon1) + self.pushButton_16.setCheckable(True) + self.pushButton_16.setObjectName("pushButton_16") + self.verticalLayout.addWidget(self.pushButton_16) + self.widget_8 = QtWidgets.QWidget(self.widget) + self.widget_8.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_8.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_8.setObjectName("widget_8") + self.pushButton_14 = QtWidgets.QPushButton(self.widget_8, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_14.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_14.setFont(font) + self.pushButton_14.setIcon(icon2) + self.pushButton_14.setObjectName("pushButton_14") + self.verticalLayout.addWidget(self.widget_8) + self.pushButton_17 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_17.setFont(font) + self.pushButton_17.setObjectName("pushButton_17") + self.verticalLayout.addWidget(self.pushButton_17) + self.pushButton_18 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_18.setFont(font) + self.pushButton_18.setObjectName("pushButton_18") + self.verticalLayout.addWidget(self.pushButton_18) + self.pushButton_10 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_10.setFont(font) + self.pushButton_10.setObjectName("pushButton_10") + self.verticalLayout.addWidget(self.pushButton_10) + self.label_3 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_3.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_3.setFont(font) + self.label_3.setStyleSheet("background-color: rgb(240,230,230)") + self.label_3.setAlignment(QtCore.Qt.AlignCenter) + self.label_3.setObjectName("label_3") + self.textBrowser = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents) + self.textBrowser.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser.setStyleSheet("background-color: #fff9f9") + self.textBrowser.setObjectName("textBrowser") + self.verticalScrollBar = QtWidgets.QScrollBar(self.scrollAreaWidgetContents) + self.verticalScrollBar.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.widget.raise_() + self.label_2.raise_() + self.label_3.raise_() + self.textBrowser.raise_() + self.verticalScrollBar.raise_() + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + + self.retranslateUi(Demolition_Dialog) + self.buttonBox_2.accepted.connect(self.handle_save) # type: ignore + self.buttonBox_2.rejected.connect(lambda: self.show_warning(Demolition_Dialog)) # type: ignore + self.pushButton_15.toggled['bool'].connect(self.widget_5.setVisible) # type: ignore + self.pushButton_16.toggled['bool'].connect(self.widget_8.setVisible) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Demolition_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + def retranslateUi(self, Demolition_Dialog): + _translate = QtCore.QCoreApplication.translate + Demolition_Dialog.setWindowTitle(_translate("Demolition_Dialog", "Demolition and Recycling Data")) + self.pushButton_6.setText(_translate("Demolition_Dialog", "Demolition and Recycling Data ")) + self.label_7.setText(_translate("Demolition_Dialog", "Scrap Value of Structural Steel")) + self.label_8.setText(_translate("Demolition_Dialog", "Structural Steel Scrap")) + self.label_21.setText(_translate("Demolition_Dialog", "(%)")) + self.label_23.setText(_translate("Demolition_Dialog", "(INR/MT)")) + self.textBrowser_2.setHtml(_translate("Demolition_Dialog", "\n" +"\n" +"

Demolition Cost rate as percentage to total construction cost

")) + self.label_22.setText(_translate("Demolition_Dialog", "(%)")) + self.pushButton.setText(_translate("Demolition_Dialog", "Project Details Window ")) + self.label_2.setText(_translate("Demolition_Dialog", "Input Parameters")) + self.pushButton_15.setText(_translate("Demolition_Dialog", "Structure Works Data")) + self.pushButton_20.setText(_translate("Demolition_Dialog", "Foundation")) + self.pushButton_21.setText(_translate("Demolition_Dialog", "Super-Structure")) + self.pushButton_22.setText(_translate("Demolition_Dialog", "Sub-Structure")) + self.pushButton_23.setText(_translate("Demolition_Dialog", "Miscellaneous")) + self.pushButton_19.setText(_translate("Demolition_Dialog", "Financial Data")) + self.pushButton_16.setText(_translate("Demolition_Dialog", "Carbon Emission Data")) + self.pushButton_14.setText(_translate("Demolition_Dialog", "Carbon Emission Cost Data")) + self.pushButton_17.setText(_translate("Demolition_Dialog", "Bridge and Traffic Data")) + self.pushButton_18.setText(_translate("Demolition_Dialog", "Maintenance and Repair")) + self.pushButton_10.setText(_translate("Demolition_Dialog", "Disposal and Recycling")) + self.label_3.setText(_translate("Demolition_Dialog", "Output")) + self.textBrowser.setHtml(_translate("Demolition_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + + def validate_data(self): + """Validate input data before saving""" + try: + # Validate numeric fields + float(self.lineEdit_5.text()) if self.lineEdit_5.text() else 0 + float(self.lineEdit_6.text()) if self.lineEdit_6.text() else 0 + float(self.lineEdit_13.text()) if self.lineEdit_13.text() else 0 + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all numeric fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return False + + data = { + "demolition_cost_rate": self.lineEdit_5.text(), + "scrap_value_structural_steel": self.lineEdit_6.text(), + "structural_steel_scrap": self.lineEdit_13.text(), + } + + # Save to JSON file + try: + import json + from datetime import datetime + + # Create filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"demolition_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Demolition data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + def handle_save(self): + """Handle the save operation and close the dialog if successful""" + if self.save_data(): # Only close if save was successful + Demolition_Dialog.accept() + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + Demolition_Dialog = QtWidgets.QDialog() + ui = Ui_Demolition_Dialog() + ui.setupUi(Demolition_Dialog) + Demolition_Dialog.show() + sys.exit(app.exec_()) diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_FinancialData_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_FinancialData_Window.py new file mode 100644 index 0000000..713a0b8 --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_FinancialData_Window.py @@ -0,0 +1,520 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_FinancialData_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox + + + +#from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + + + + + + +class Ui_FinancialData_Dialog(object): + def openBridgeTrafficWindow(self): + from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, FinancialData_Dialog): + FinancialData_Dialog.setObjectName("FinancialData_Dialog") + FinancialData_Dialog.resize(1440, 900) + FinancialData_Dialog.setStyleSheet("background-color:#FAFAFA") + self.label = QtWidgets.QLabel(FinancialData_Dialog) + self.label.setGeometry(QtCore.QRect(10, 65, 244, 691)) + font = QtGui.QFont() + font.setPointSize(10) + self.label.setFont(font) + self.label.setStyleSheet("background-color: rgb(240,230,230)") + self.label.setText("") + self.label.setObjectName("label") + self.pushButton = QtWidgets.QPushButton(FinancialData_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 40, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton.setIcon(icon) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + self.widget_2 = QtWidgets.QWidget(FinancialData_Dialog) + self.widget_2.setGeometry(QtCore.QRect(350, 65, 736, 249)) + self.widget_2.setStyleSheet("background-color: #fff9f9") + self.widget_2.setObjectName("widget_2") + self.label_4 = QtWidgets.QLabel(self.widget_2) + self.label_4.setGeometry(QtCore.QRect(20, 20, 141, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_4.setFont(font) + self.label_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_4.setObjectName("label_4") + self.label_5 = QtWidgets.QLabel(self.widget_2) + self.label_5.setGeometry(QtCore.QRect(20, 80, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_5.setFont(font) + self.label_5.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_5.setObjectName("label_5") + self.label_6 = QtWidgets.QLabel(self.widget_2) + self.label_6.setGeometry(QtCore.QRect(20, 50, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_6.setFont(font) + self.label_6.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_6.setObjectName("label_6") + self.label_7 = QtWidgets.QLabel(self.widget_2) + self.label_7.setGeometry(QtCore.QRect(20, 110, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_7.setFont(font) + self.label_7.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_7.setObjectName("label_7") + self.label_8 = QtWidgets.QLabel(self.widget_2) + self.label_8.setGeometry(QtCore.QRect(20, 140, 221, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_8.setFont(font) + self.label_8.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_8.setObjectName("label_8") + self.comboBox_2 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_2.setGeometry(QtCore.QRect(270, 80, 101, 22)) + self.comboBox_2.setStyleSheet("background-color: #ffffff") + self.comboBox_2.setObjectName("comboBox_2") + self.comboBox_3 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_3.setGeometry(QtCore.QRect(270, 50, 101, 22)) + self.comboBox_3.setStyleSheet("background-color: #ffffff") + self.comboBox_3.setObjectName("comboBox_3") + self.lineEdit_5 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_5.setGeometry(QtCore.QRect(270, 20, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_5.setFont(font) + self.lineEdit_5.setStyleSheet("background-color: #ffffff") + self.lineEdit_5.setObjectName("lineEdit_5") + self.lineEdit_6 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_6.setGeometry(QtCore.QRect(270, 110, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_6.setFont(font) + self.lineEdit_6.setStyleSheet("background-color: #ffffff") + self.lineEdit_6.setObjectName("lineEdit_6") + self.buttonBox_2 = QtWidgets.QDialogButtonBox(self.widget_2) + self.buttonBox_2.setGeometry(QtCore.QRect(380, 210, 341, 32)) + self.buttonBox_2.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox_2.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox_2.setObjectName("buttonBox_2") + self.lineEdit_13 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_13.setGeometry(QtCore.QRect(270, 140, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_13.setFont(font) + self.lineEdit_13.setStyleSheet("background-color: #ffffff") + self.lineEdit_13.setObjectName("lineEdit_13") + self.label_21 = QtWidgets.QLabel(self.widget_2) + self.label_21.setGeometry(QtCore.QRect(390, 20, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_21.setFont(font) + self.label_21.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_21.setObjectName("label_21") + self.label_22 = QtWidgets.QLabel(self.widget_2) + self.label_22.setGeometry(QtCore.QRect(390, 50, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_22.setFont(font) + self.label_22.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_22.setObjectName("label_22") + self.label_23 = QtWidgets.QLabel(self.widget_2) + self.label_23.setGeometry(QtCore.QRect(390, 110, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_23.setFont(font) + self.label_23.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_23.setObjectName("label_23") + self.label_24 = QtWidgets.QLabel(self.widget_2) + self.label_24.setGeometry(QtCore.QRect(390, 140, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_24.setFont(font) + self.label_24.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_24.setObjectName("label_24") + self.scrollArea = QtWidgets.QScrollArea(FinancialData_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 70, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.label_2 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_2.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_2.setFont(font) + self.label_2.setStyleSheet("background-color: rgb(240,230,230)") + self.label_2.setAlignment(QtCore.Qt.AlignCenter) + self.label_2.setObjectName("label_2") + self.widget = QtWidgets.QWidget(self.scrollAreaWidgetContents) + self.widget.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget.setStyleSheet("background-color: #fff9f9") + self.widget.setObjectName("widget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.widget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.pushButton_15 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_15.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_15.setIcon(icon1) + self.pushButton_15.setCheckable(True) + self.pushButton_15.setAutoDefault(True) + self.pushButton_15.setObjectName("pushButton_15") + self.verticalLayout.addWidget(self.pushButton_15) + self.widget_5 = QtWidgets.QWidget(self.widget) + self.widget_5.setObjectName("widget_5") + self.formLayout = QtWidgets.QFormLayout(self.widget_5) + self.formLayout.setObjectName("formLayout") + self.pushButton_20 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_20.setFont(font) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_20.setIcon(icon2) + self.pushButton_20.setObjectName("pushButton_20") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_20) + self.pushButton_21 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_21.setFont(font) + self.pushButton_21.setIcon(icon2) + self.pushButton_21.setObjectName("pushButton_21") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_21) + self.pushButton_22 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_22.setFont(font) + self.pushButton_22.setIcon(icon2) + self.pushButton_22.setObjectName("pushButton_22") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_22) + self.pushButton_23 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_23.setFont(font) + self.pushButton_23.setIcon(icon2) + self.pushButton_23.setObjectName("pushButton_23") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_23) + self.verticalLayout.addWidget(self.widget_5) + self.pushButton_19 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_19.setFont(font) + self.pushButton_19.setObjectName("pushButton_19") + self.verticalLayout.addWidget(self.pushButton_19) + self.pushButton_16 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_16.setFont(font) + self.pushButton_16.setIcon(icon1) + self.pushButton_16.setCheckable(True) + self.pushButton_16.setObjectName("pushButton_16") + self.verticalLayout.addWidget(self.pushButton_16) + self.widget_8 = QtWidgets.QWidget(self.widget) + self.widget_8.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_8.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_8.setObjectName("widget_8") + self.pushButton_14 = QtWidgets.QPushButton(self.widget_8, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_14.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_14.setFont(font) + self.pushButton_14.setIcon(icon2) + self.pushButton_14.setObjectName("pushButton_14") + self.verticalLayout.addWidget(self.widget_8) + self.pushButton_17 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_17.setFont(font) + self.pushButton_17.setObjectName("pushButton_17") + self.verticalLayout.addWidget(self.pushButton_17) + self.pushButton_18 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_18.setFont(font) + self.pushButton_18.setObjectName("pushButton_18") + self.verticalLayout.addWidget(self.pushButton_18) + self.pushButton_10 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_10.setFont(font) + self.pushButton_10.setObjectName("pushButton_10") + self.verticalLayout.addWidget(self.pushButton_10) + self.label_3 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_3.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_3.setFont(font) + self.label_3.setStyleSheet("background-color: rgb(240,230,230)") + self.label_3.setAlignment(QtCore.Qt.AlignCenter) + self.label_3.setObjectName("label_3") + self.textBrowser = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents) + self.textBrowser.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser.setStyleSheet("background-color: #fff9f9") + self.textBrowser.setObjectName("textBrowser") + self.verticalScrollBar = QtWidgets.QScrollBar(self.scrollAreaWidgetContents) + self.verticalScrollBar.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.widget.raise_() + self.label_2.raise_() + self.label_3.raise_() + self.textBrowser.raise_() + self.verticalScrollBar.raise_() + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + self.pushButton_6 = QtWidgets.QPushButton(FinancialData_Dialog) + self.pushButton_6.setGeometry(QtCore.QRect(350, 40, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_6.setFont(font) + self.pushButton_6.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_6.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_6.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton_6.setIcon(icon) + self.pushButton_6.setAutoRepeat(False) + self.pushButton_6.setObjectName("pushButton_6") + + self.retranslateUi(FinancialData_Dialog) + self.buttonBox_2.accepted.connect(self.handle_save) # type: ignore + self.buttonBox_2.rejected.connect(lambda: self.show_warning(FinancialData_Dialog)) # type: ignore + self.pushButton_15.toggled['bool'].connect(self.widget_5.setVisible) # type: ignore + self.pushButton_16.toggled['bool'].connect(self.widget_8.setVisible) # type: ignore + QtCore.QMetaObject.connectSlotsByName(FinancialData_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + def retranslateUi(self, FinancialData_Dialog): + _translate = QtCore.QCoreApplication.translate + FinancialData_Dialog.setWindowTitle(_translate("FinancialData_Dialog", "Dialog")) + self.pushButton.setText(_translate("FinancialData_Dialog", "Project Details Window ")) + self.label_4.setText(_translate("FinancialData_Dialog", "Real Discount Rate")) + self.label_5.setText(_translate("FinancialData_Dialog", "Investment Ratio")) + self.label_6.setText(_translate("FinancialData_Dialog", "Interest Rate")) + self.label_7.setText(_translate("FinancialData_Dialog", "Duration of Study ")) + self.label_8.setText(_translate("FinancialData_Dialog", "Time for construction of Base Project")) + self.label_21.setText(_translate("FinancialData_Dialog", "(%)")) + self.label_22.setText(_translate("FinancialData_Dialog", "(%)")) + self.label_23.setText(_translate("FinancialData_Dialog", "(years)")) + self.label_24.setText(_translate("FinancialData_Dialog", "(years)")) + self.label_2.setText(_translate("FinancialData_Dialog", "Input Parameters")) + self.pushButton_15.setText(_translate("FinancialData_Dialog", "Structure Works Data")) + self.pushButton_20.setText(_translate("FinancialData_Dialog", "Foundation")) + self.pushButton_21.setText(_translate("FinancialData_Dialog", "Super-Structure")) + self.pushButton_22.setText(_translate("FinancialData_Dialog", "Sub-Structure")) + self.pushButton_23.setText(_translate("FinancialData_Dialog", "Miscellaneous")) + self.pushButton_19.setText(_translate("FinancialData_Dialog", "Financial Data")) + self.pushButton_16.setText(_translate("FinancialData_Dialog", "Carbon Emission Data")) + self.pushButton_14.setText(_translate("FinancialData_Dialog", "Carbon Emission Cost Data")) + self.pushButton_17.setText(_translate("FinancialData_Dialog", "Bridge and Traffic Data")) + self.pushButton_18.setText(_translate("FinancialData_Dialog", "Maintenance and Repair")) + self.pushButton_10.setText(_translate("FinancialData_Dialog", "Disposal and Recycling")) + self.label_3.setText(_translate("FinancialData_Dialog", "Output")) + self.textBrowser.setHtml(_translate("FinancialData_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + self.pushButton_6.setText(_translate("FinancialData_Dialog", "Financial Data ")) + + def validate_data(self): + """Validate input data before saving""" + try: + # Validate numeric fields + float(self.lineEdit_5.text()) if self.lineEdit_5.text() else 0 # Real Discount Rate + float(self.comboBox_3.currentText()) if self.comboBox_3.currentText() else 0 # Interest Rate + float(self.comboBox_2.currentText()) if self.comboBox_2.currentText() else 0 # Investment Ratio + float(self.lineEdit_6.text()) if self.lineEdit_6.text() else 0 # Duration of Study + float(self.lineEdit_13.text()) if self.lineEdit_13.text() else 0 # Time for construction + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all numeric fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return False + + data = { + "real_discount_rate": self.lineEdit_5.text(), + "interest_rate": self.comboBox_3.currentText(), + "investment_ratio": self.comboBox_2.currentText(), + "duration_of_study": self.lineEdit_6.text(), + "construction_time_base_project": self.lineEdit_13.text(), + } + + # Save to JSON file + try: + import json + from datetime import datetime + + # Create filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"financial_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Financial data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + def handle_save(self): + """Handle the save operation and close the dialog if successful""" + if self.save_data(): # Only close if save was successful + FinancialData_Dialog.accept() + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + FinancialData_Dialog = QtWidgets.QDialog() + ui = Ui_FinancialData_Dialog() + ui.setupUi(FinancialData_Dialog) + FinancialData_Dialog.show() + sys.exit(app.exec_()) diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_Foundation_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_Foundation_Window.py new file mode 100644 index 0000000..69a8792 --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_Foundation_Window.py @@ -0,0 +1,764 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_Foundation_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +#from form_data_storage import save_form_data +from PyQt5.QtWidgets import QMessageBox + +#from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + + + + + + + + + +class Ui_Foundation_Dialog(object): + def openBridgeTrafficWindow(self): + from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, Foundation_Dialog): + Foundation_Dialog.setObjectName("Foundation_Dialog") + Foundation_Dialog.resize(1440, 900) + Foundation_Dialog.setAutoFillBackground(False) + Foundation_Dialog.setStyleSheet("background-color:#FAFAFA") + self.label = QtWidgets.QLabel(Foundation_Dialog) + self.label.setGeometry(QtCore.QRect(10, 50, 244, 691)) + font = QtGui.QFont() + font.setPointSize(10) + self.label.setFont(font) + self.label.setStyleSheet("background-color: rgb(240,230,230)") + self.label.setText("") + self.label.setObjectName("label") + self.pushButton = QtWidgets.QPushButton(Foundation_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 25, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton.setIcon(icon) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + self.scrollArea = QtWidgets.QScrollArea(Foundation_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 55, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.label_2 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_2.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_2.setFont(font) + self.label_2.setStyleSheet("background-color: rgb(240,230,230)") + self.label_2.setAlignment(QtCore.Qt.AlignCenter) + self.label_2.setObjectName("label_2") + self.widget = QtWidgets.QWidget(self.scrollAreaWidgetContents) + self.widget.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget.setStyleSheet("background-color: #fff9f9") + self.widget.setObjectName("widget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.widget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.pushButton_15 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_15.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_15.setIcon(icon1) + self.pushButton_15.setCheckable(True) + self.pushButton_15.setAutoDefault(True) + self.pushButton_15.setObjectName("pushButton_15") + self.verticalLayout.addWidget(self.pushButton_15) + self.widget_5 = QtWidgets.QWidget(self.widget) + self.widget_5.setObjectName("widget_5") + self.formLayout = QtWidgets.QFormLayout(self.widget_5) + self.formLayout.setObjectName("formLayout") + self.pushButton_20 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_20.setFont(font) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_20.setIcon(icon2) + self.pushButton_20.setObjectName("pushButton_20") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_20) + self.pushButton_21 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_21.setFont(font) + self.pushButton_21.setIcon(icon2) + self.pushButton_21.setObjectName("pushButton_21") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_21) + self.pushButton_22 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_22.setFont(font) + self.pushButton_22.setIcon(icon2) + self.pushButton_22.setObjectName("pushButton_22") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_22) + self.pushButton_23 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_23.setFont(font) + self.pushButton_23.setIcon(icon2) + self.pushButton_23.setObjectName("pushButton_23") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_23) + self.verticalLayout.addWidget(self.widget_5) + self.pushButton_19 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_19.setFont(font) + self.pushButton_19.setObjectName("pushButton_19") + self.verticalLayout.addWidget(self.pushButton_19) + self.pushButton_16 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_16.setFont(font) + self.pushButton_16.setIcon(icon1) + self.pushButton_16.setCheckable(True) + self.pushButton_16.setObjectName("pushButton_16") + self.verticalLayout.addWidget(self.pushButton_16) + self.widget_8 = QtWidgets.QWidget(self.widget) + self.widget_8.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_8.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_8.setObjectName("widget_8") + self.pushButton_14 = QtWidgets.QPushButton(self.widget_8, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_14.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_14.setFont(font) + self.pushButton_14.setIcon(icon2) + self.pushButton_14.setObjectName("pushButton_14") + self.verticalLayout.addWidget(self.widget_8) + self.pushButton_17 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_17.setFont(font) + self.pushButton_17.setObjectName("pushButton_17") + self.verticalLayout.addWidget(self.pushButton_17) + self.pushButton_18 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_18.setFont(font) + self.pushButton_18.setObjectName("pushButton_18") + self.verticalLayout.addWidget(self.pushButton_18) + self.pushButton_10 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_10.setFont(font) + self.pushButton_10.setObjectName("pushButton_10") + self.verticalLayout.addWidget(self.pushButton_10) + self.label_3 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_3.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_3.setFont(font) + self.label_3.setStyleSheet("background-color: rgb(240,230,230)") + self.label_3.setAlignment(QtCore.Qt.AlignCenter) + self.label_3.setObjectName("label_3") + self.textBrowser = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents) + self.textBrowser.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser.setStyleSheet("background-color: #fff9f9") + self.textBrowser.setObjectName("textBrowser") + self.verticalScrollBar = QtWidgets.QScrollBar(self.scrollAreaWidgetContents) + self.verticalScrollBar.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.widget.raise_() + self.label_2.raise_() + self.label_3.raise_() + self.textBrowser.raise_() + self.verticalScrollBar.raise_() + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + self.widget_2 = QtWidgets.QWidget(Foundation_Dialog) + self.widget_2.setGeometry(QtCore.QRect(350, 50, 778, 624)) + self.widget_2.setStyleSheet("background-color: #fff9f9") + self.widget_2.setObjectName("widget_2") + self.label_4 = QtWidgets.QLabel(self.widget_2) + self.label_4.setGeometry(QtCore.QRect(20, 10, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_4.setFont(font) + self.label_4.setObjectName("label_4") + self.comboBox = QtWidgets.QComboBox(self.widget_2) + self.comboBox.setGeometry(QtCore.QRect(140, 10, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox.setFont(font) + self.comboBox.setStyleSheet("background-color: #ffffff") + self.comboBox.setObjectName("comboBox") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.pushButton_2 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_2.setGeometry(QtCore.QRect(350, 10, 190, 23)) + self.pushButton_2.setStyleSheet("background-color: #ffffff") + self.pushButton_2.setObjectName("pushButton_2") + self.label_5 = QtWidgets.QLabel(self.widget_2) + self.label_5.setGeometry(QtCore.QRect(20, 70, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_5.setFont(font) + self.label_5.setAlignment(QtCore.Qt.AlignCenter) + self.label_5.setObjectName("label_5") + self.label_6 = QtWidgets.QLabel(self.widget_2) + self.label_6.setGeometry(QtCore.QRect(551, 70, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_6.setFont(font) + self.label_6.setAlignment(QtCore.Qt.AlignCenter) + self.label_6.setObjectName("label_6") + self.label_7 = QtWidgets.QLabel(self.widget_2) + self.label_7.setGeometry(QtCore.QRect(191, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_7.setFont(font) + self.label_7.setAlignment(QtCore.Qt.AlignCenter) + self.label_7.setObjectName("label_7") + self.label_8 = QtWidgets.QLabel(self.widget_2) + self.label_8.setGeometry(QtCore.QRect(311, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_8.setFont(font) + self.label_8.setAlignment(QtCore.Qt.AlignCenter) + self.label_8.setObjectName("label_8") + self.label_9 = QtWidgets.QLabel(self.widget_2) + self.label_9.setGeometry(QtCore.QRect(431, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_9.setFont(font) + self.label_9.setAlignment(QtCore.Qt.AlignCenter) + self.label_9.setObjectName("label_9") + self.comboBox_2 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_2.setGeometry(QtCore.QRect(30, 100, 140, 22)) + self.comboBox_2.setStyleSheet("background-color: #ffffff") + self.comboBox_2.setObjectName("comboBox_2") + self.comboBox_3 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_3.setGeometry(QtCore.QRect(30, 130, 140, 22)) + self.comboBox_3.setStyleSheet("background-color: #ffffff") + self.comboBox_3.setObjectName("comboBox_3") + self.lineEdit = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit.setGeometry(QtCore.QRect(210, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit.setFont(font) + self.lineEdit.setStyleSheet("background-color: #ffffff") + self.lineEdit.setObjectName("lineEdit") + self.lineEdit_2 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_2.setGeometry(QtCore.QRect(210, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_2.setFont(font) + self.lineEdit_2.setStyleSheet("background-color: #ffffff") + self.lineEdit_2.setObjectName("lineEdit_2") + self.label_10 = QtWidgets.QLabel(self.widget_2) + self.label_10.setGeometry(QtCore.QRect(340, 100, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_10.setFont(font) + self.label_10.setStyleSheet("background-color: #ffffff") + self.label_10.setAlignment(QtCore.Qt.AlignCenter) + self.label_10.setObjectName("label_10") + self.label_11 = QtWidgets.QLabel(self.widget_2) + self.label_11.setGeometry(QtCore.QRect(340, 130, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_11.setFont(font) + self.label_11.setStyleSheet("background-color: #ffffff") + self.label_11.setAlignment(QtCore.Qt.AlignCenter) + self.label_11.setObjectName("label_11") + self.lineEdit_3 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_3.setGeometry(QtCore.QRect(450, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_3.setFont(font) + self.lineEdit_3.setStyleSheet("background-color: #ffffff") + self.lineEdit_3.setObjectName("lineEdit_3") + self.lineEdit_4 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_4.setGeometry(QtCore.QRect(450, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_4.setFont(font) + self.lineEdit_4.setStyleSheet("background-color: #ffffff") + self.lineEdit_4.setObjectName("lineEdit_4") + self.lineEdit_5 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_5.setGeometry(QtCore.QRect(570, 100, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_5.setFont(font) + self.lineEdit_5.setStyleSheet("background-color: #ffffff") + self.lineEdit_5.setObjectName("lineEdit_5") + self.lineEdit_6 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_6.setGeometry(QtCore.QRect(570, 130, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_6.setFont(font) + self.lineEdit_6.setStyleSheet("background-color: #ffffff") + self.lineEdit_6.setObjectName("lineEdit_6") + self.pushButton_3 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_3.setGeometry(QtCore.QRect(300, 200, 190, 23)) + self.pushButton_3.setStyleSheet("background-color: #ffffff") + self.pushButton_3.setObjectName("pushButton_3") + self.label_12 = QtWidgets.QLabel(self.widget_2) + self.label_12.setGeometry(QtCore.QRect(30, 330, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_12.setFont(font) + self.label_12.setAlignment(QtCore.Qt.AlignCenter) + self.label_12.setObjectName("label_12") + self.comboBox_4 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_4.setGeometry(QtCore.QRect(150, 270, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_4.setFont(font) + self.comboBox_4.setStyleSheet("background-color: #ffffff") + self.comboBox_4.setObjectName("comboBox_4") + self.comboBox_4.addItem("") + self.comboBox_4.addItem("") + self.label_13 = QtWidgets.QLabel(self.widget_2) + self.label_13.setGeometry(QtCore.QRect(441, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_13.setFont(font) + self.label_13.setAlignment(QtCore.Qt.AlignCenter) + self.label_13.setObjectName("label_13") + self.label_14 = QtWidgets.QLabel(self.widget_2) + self.label_14.setGeometry(QtCore.QRect(350, 390, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_14.setFont(font) + self.label_14.setStyleSheet("background-color: #ffffff") + self.label_14.setAlignment(QtCore.Qt.AlignCenter) + self.label_14.setObjectName("label_14") + self.label_15 = QtWidgets.QLabel(self.widget_2) + self.label_15.setGeometry(QtCore.QRect(561, 330, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_15.setFont(font) + self.label_15.setAlignment(QtCore.Qt.AlignCenter) + self.label_15.setObjectName("label_15") + self.label_16 = QtWidgets.QLabel(self.widget_2) + self.label_16.setGeometry(QtCore.QRect(350, 360, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_16.setFont(font) + self.label_16.setStyleSheet("background-color: #ffffff") + self.label_16.setAlignment(QtCore.Qt.AlignCenter) + self.label_16.setObjectName("label_16") + self.label_17 = QtWidgets.QLabel(self.widget_2) + self.label_17.setGeometry(QtCore.QRect(321, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_17.setFont(font) + self.label_17.setAlignment(QtCore.Qt.AlignCenter) + self.label_17.setObjectName("label_17") + self.lineEdit_7 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_7.setGeometry(QtCore.QRect(220, 390, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_7.setFont(font) + self.lineEdit_7.setStyleSheet("background-color: #ffffff") + self.lineEdit_7.setObjectName("lineEdit_7") + self.lineEdit_8 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_8.setGeometry(QtCore.QRect(580, 360, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_8.setFont(font) + self.lineEdit_8.setStyleSheet("background-color: #ffffff") + self.lineEdit_8.setObjectName("lineEdit_8") + self.label_18 = QtWidgets.QLabel(self.widget_2) + self.label_18.setGeometry(QtCore.QRect(201, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_18.setFont(font) + self.label_18.setAlignment(QtCore.Qt.AlignCenter) + self.label_18.setObjectName("label_18") + self.pushButton_4 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_4.setGeometry(QtCore.QRect(310, 460, 190, 23)) + self.pushButton_4.setStyleSheet("background-color: #ffffff") + self.pushButton_4.setObjectName("pushButton_4") + self.lineEdit_9 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_9.setGeometry(QtCore.QRect(220, 360, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_9.setFont(font) + self.lineEdit_9.setStyleSheet("background-color: #ffffff") + self.lineEdit_9.setObjectName("lineEdit_9") + self.lineEdit_10 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_10.setGeometry(QtCore.QRect(460, 360, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_10.setFont(font) + self.lineEdit_10.setStyleSheet("background-color: #ffffff") + self.lineEdit_10.setObjectName("lineEdit_10") + self.lineEdit_11 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_11.setGeometry(QtCore.QRect(460, 390, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_11.setFont(font) + self.lineEdit_11.setStyleSheet("background-color: #ffffff") + self.lineEdit_11.setObjectName("lineEdit_11") + self.pushButton_5 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_5.setGeometry(QtCore.QRect(360, 270, 190, 23)) + self.pushButton_5.setStyleSheet("background-color: #ffffff") + self.pushButton_5.setObjectName("pushButton_5") + self.label_19 = QtWidgets.QLabel(self.widget_2) + self.label_19.setGeometry(QtCore.QRect(30, 270, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_19.setFont(font) + self.label_19.setObjectName("label_19") + self.lineEdit_12 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_12.setGeometry(QtCore.QRect(580, 390, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_12.setFont(font) + self.lineEdit_12.setStyleSheet("background-color: #ffffff") + self.lineEdit_12.setObjectName("lineEdit_12") + self.comboBox_5 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_5.setGeometry(QtCore.QRect(40, 360, 140, 22)) + self.comboBox_5.setStyleSheet("background-color: #ffffff") + self.comboBox_5.setObjectName("comboBox_5") + self.comboBox_5.addItem("") + self.comboBox_5.addItem("") + self.comboBox_6 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_6.setGeometry(QtCore.QRect(40, 390, 140, 22)) + self.comboBox_6.setStyleSheet("background-color: #ffffff") + self.comboBox_6.setObjectName("comboBox_6") + self.comboBox_6.addItem("") + self.comboBox_6.addItem("") + self.buttonBox = QtWidgets.QDialogButtonBox(self.widget_2) + self.buttonBox.setGeometry(QtCore.QRect(420, 580, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox.setCenterButtons(False) + self.buttonBox.setObjectName("buttonBox") + self.line = QtWidgets.QFrame(self.widget_2) + self.line.setGeometry(QtCore.QRect(10, 250, 761, 16)) + self.line.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line.setLineWidth(2) + self.line.setMidLineWidth(2) + self.line.setFrameShape(QtWidgets.QFrame.HLine) + self.line.setObjectName("line") + self.line_2 = QtWidgets.QFrame(self.widget_2) + self.line_2.setGeometry(QtCore.QRect(10, 500, 761, 16)) + self.line_2.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_2.setLineWidth(2) + self.line_2.setMidLineWidth(2) + self.line_2.setFrameShape(QtWidgets.QFrame.HLine) + self.line_2.setObjectName("line_2") + self.pushButton_6 = QtWidgets.QPushButton(Foundation_Dialog) + self.pushButton_6.setGeometry(QtCore.QRect(350, 25, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_6.setFont(font) + self.pushButton_6.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_6.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_6.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton_6.setIcon(icon) + self.pushButton_6.setAutoRepeat(False) + self.pushButton_6.setObjectName("pushButton_6") + + self.retranslateUi(Foundation_Dialog) + self.buttonBox.accepted.connect(self.handle_save) # type: ignore + #self.buttonBox.accepted.connect(self.save_data) + self.buttonBox.rejected.connect(lambda: self.show_warning(Foundation_Dialog)) # type: ignore + self.pushButton_15.toggled['bool'].connect(self.widget_5.setVisible) # type: ignore + self.pushButton_16.toggled['bool'].connect(self.widget_8.setVisible) # type: ignore + + QtCore.QMetaObject.connectSlotsByName(Foundation_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + ''' + def save_data(self): + """ + Collect form data and save it to the global dictionary. + """ + data = { + "component": self.comboBox.currentText(), + "sub_component": self.comboBox_2.currentText(), + "quantity": self.lineEdit.text(), + "unit": self.label_10.text(), + "rate": self.lineEdit_3.text(), + "material_type": self.comboBox_4.currentText(), + } + save_form_data("Foundation_Dialog", data)''' + + def retranslateUi(self, Foundation_Dialog): + _translate = QtCore.QCoreApplication.translate + Foundation_Dialog.setWindowTitle(_translate("Foundation_Dialog", "Dialog")) + self.pushButton.setText(_translate("Foundation_Dialog", "Project Details Window ")) + self.label_2.setText(_translate("Foundation_Dialog", "Input Parameters")) + self.pushButton_15.setText(_translate("Foundation_Dialog", "Structure Works Data")) + self.pushButton_20.setText(_translate("Foundation_Dialog", "Foundation")) + self.pushButton_21.setText(_translate("Foundation_Dialog", "Super-Structure")) + self.pushButton_22.setText(_translate("Foundation_Dialog", "Sub-Structure")) + self.pushButton_23.setText(_translate("Foundation_Dialog", "Miscellaneous")) + self.pushButton_19.setText(_translate("Foundation_Dialog", "Financial Data")) + self.pushButton_16.setText(_translate("Foundation_Dialog", "Carbon Emission Data")) + self.pushButton_14.setText(_translate("Foundation_Dialog", "Carbon Emission Cost Data")) + self.pushButton_17.setText(_translate("Foundation_Dialog", "Bridge and Traffic Data")) + self.pushButton_18.setText(_translate("Foundation_Dialog", "Maintenance and Repair")) + self.pushButton_10.setText(_translate("Foundation_Dialog", "Disposal and Recycling")) + self.label_3.setText(_translate("Foundation_Dialog", "Output")) + self.textBrowser.setHtml(_translate("Foundation_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + self.label_4.setText(_translate("Foundation_Dialog", "Components:")) + self.comboBox.setItemText(0, _translate("Foundation_Dialog", "Earthwork")) + self.comboBox.setItemText(1, _translate("Foundation_Dialog", "RCC in Foundation")) + self.pushButton_2.setText(_translate("Foundation_Dialog", "+ Add Sub-Component")) + self.label_5.setText(_translate("Foundation_Dialog", "Material Type and Grade")) + self.label_6.setText(_translate("Foundation_Dialog", "Rate Data Source")) + self.label_7.setText(_translate("Foundation_Dialog", "Quantity")) + self.label_8.setText(_translate("Foundation_Dialog", "Unit")) + self.label_9.setText(_translate("Foundation_Dialog", "Rate")) + self.label_10.setText(_translate("Foundation_Dialog", "

m3

")) + self.label_11.setText(_translate("Foundation_Dialog", "kg")) + self.pushButton_3.setText(_translate("Foundation_Dialog", "+ Add Material")) + self.label_12.setText(_translate("Foundation_Dialog", "Material Type and Grade")) + self.comboBox_4.setItemText(0, _translate("Foundation_Dialog", "RCC in Foundation")) + self.comboBox_4.setItemText(1, _translate("Foundation_Dialog", "Earthwork")) + self.label_13.setText(_translate("Foundation_Dialog", "Rate")) + self.label_14.setText(_translate("Foundation_Dialog", "kg")) + self.label_15.setText(_translate("Foundation_Dialog", "Rate Data Source")) + self.label_16.setText(_translate("Foundation_Dialog", "

m3

")) + self.label_17.setText(_translate("Foundation_Dialog", "Unit")) + self.label_18.setText(_translate("Foundation_Dialog", "Quantity")) + self.pushButton_4.setText(_translate("Foundation_Dialog", "+ Add Material")) + self.pushButton_5.setText(_translate("Foundation_Dialog", "+ Add Sub-Component")) + self.label_19.setText(_translate("Foundation_Dialog", "Components:")) + self.comboBox_5.setItemText(0, _translate("Foundation_Dialog", "Concrete")) + self.comboBox_5.setItemText(1, _translate("Foundation_Dialog", "Steel")) + self.comboBox_6.setItemText(0, _translate("Foundation_Dialog", "Steel")) + self.comboBox_6.setItemText(1, _translate("Foundation_Dialog", "Concrete")) + self.pushButton_6.setText(_translate("Foundation_Dialog", "Foundation ")) + + def validate_data(self): + """Validate input data before saving""" + try: + # Validate numeric fields + float(self.lineEdit.text()) if self.lineEdit.text() else 0 # Quantity 1 + float(self.lineEdit_2.text()) if self.lineEdit_2.text() else 0 # Quantity 2 + float(self.lineEdit_3.text()) if self.lineEdit_3.text() else 0 # Rate 1 + float(self.lineEdit_4.text()) if self.lineEdit_4.text() else 0 # Rate 2 + float(self.lineEdit_5.text()) if self.lineEdit_5.text() else 0 # Rate Data Source 1 + float(self.lineEdit_6.text()) if self.lineEdit_6.text() else 0 # Rate Data Source 2 + float(self.lineEdit_7.text()) if self.lineEdit_7.text() else 0 # Quantity 3 + float(self.lineEdit_8.text()) if self.lineEdit_8.text() else 0 # Rate Data Source 3 + float(self.lineEdit_9.text()) if self.lineEdit_9.text() else 0 # Quantity 4 + float(self.lineEdit_10.text()) if self.lineEdit_10.text() else 0 # Rate 3 + float(self.lineEdit_11.text()) if self.lineEdit_11.text() else 0 # Rate 4 + float(self.lineEdit_12.text()) if self.lineEdit_12.text() else 0 # Rate Data Source 4 + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all numeric fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return False + + data = { + "component_1": self.comboBox.currentText(), + "sub_component_1": self.comboBox_2.currentText(), + "material_type_1": self.comboBox_5.currentText(), + "material_grade_1": self.comboBox_6.currentText(), + "quantity_1": self.lineEdit.text(), + "unit_1": self.label_10.text(), + "rate_1": self.lineEdit_3.text(), + "rate_data_source_1": self.lineEdit_5.text(), + + "component_2": self.comboBox_4.currentText(), + "sub_component_2": self.comboBox_3.currentText(), + "material_type_2": self.comboBox_5.currentText(), + "material_grade_2": self.comboBox_6.currentText(), + "quantity_2": self.lineEdit_2.text(), + "unit_2": self.label_11.text(), + "rate_2": self.lineEdit_4.text(), + "rate_data_source_2": self.lineEdit_6.text(), + + "quantity_3": self.lineEdit_9.text(), + "unit_3": self.label_16.text(), + "rate_3": self.lineEdit_10.text(), + "quantity_4": self.lineEdit_7.text(), + "unit_4": self.label_14.text(), + "rate_4": self.lineEdit_11.text(), + "rate_data_source_3": self.lineEdit_8.text(), + "rate_data_source_4": self.lineEdit_12.text(), + } + + # Save to JSON file + try: + import json + from datetime import datetime + + # Create filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"foundation_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Foundation data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + def handle_save(self): + """Handle the save operation and close the dialog if successful""" + if self.save_data(): # Only close if save was successful + Foundation_Dialog.accept() + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + Foundation_Dialog = QtWidgets.QDialog() + ui = Ui_Foundation_Dialog() + ui.setupUi(Foundation_Dialog) + Foundation_Dialog.show() + sys.exit(app.exec_()) diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_MaintenanceANDRepairData_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_MaintenanceANDRepairData_Window.py new file mode 100644 index 0000000..a36d92b --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_MaintenanceANDRepairData_Window.py @@ -0,0 +1,571 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_Maintenance&RepairData_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox + + + + + +#from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + + + + + +class Ui_Maintenance_Dialog(object): + def openBridgeTrafficWindow(self): + from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, Maintenance_Dialog): + Maintenance_Dialog.setObjectName("Maintenance_Dialog") + Maintenance_Dialog.resize(1440, 900) + Maintenance_Dialog.setStyleSheet("background-color: #fafafa") + self.pushButton_6 = QtWidgets.QPushButton(Maintenance_Dialog) + self.pushButton_6.setGeometry(QtCore.QRect(350, 35, 261, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_6.setFont(font) + self.pushButton_6.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_6.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_6.setStyleSheet("background-color: rgb(240,230,230)") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_6.setIcon(icon) + self.pushButton_6.setAutoRepeat(False) + self.pushButton_6.setObjectName("pushButton_6") + self.label = QtWidgets.QLabel(Maintenance_Dialog) + self.label.setGeometry(QtCore.QRect(10, 60, 244, 691)) + font = QtGui.QFont() + font.setPointSize(10) + self.label.setFont(font) + self.label.setStyleSheet("background-color: rgb(240,230,230)") + self.label.setText("") + self.label.setObjectName("label") + self.widget_2 = QtWidgets.QWidget(Maintenance_Dialog) + self.widget_2.setGeometry(QtCore.QRect(350, 60, 784, 304)) + self.widget_2.setStyleSheet("background-color: #fff9f9") + self.widget_2.setObjectName("widget_2") + self.label_4 = QtWidgets.QLabel(self.widget_2) + self.label_4.setGeometry(QtCore.QRect(20, 240, 201, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_4.setFont(font) + self.label_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_4.setObjectName("label_4") + self.label_5 = QtWidgets.QLabel(self.widget_2) + self.label_5.setGeometry(QtCore.QRect(600, 60, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_5.setFont(font) + self.label_5.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_5.setObjectName("label_5") + self.label_6 = QtWidgets.QLabel(self.widget_2) + self.label_6.setGeometry(QtCore.QRect(600, 30, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_6.setFont(font) + self.label_6.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_6.setObjectName("label_6") + self.label_7 = QtWidgets.QLabel(self.widget_2) + self.label_7.setGeometry(QtCore.QRect(600, 90, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_7.setFont(font) + self.label_7.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_7.setObjectName("label_7") + self.label_8 = QtWidgets.QLabel(self.widget_2) + self.label_8.setGeometry(QtCore.QRect(20, 200, 221, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_8.setFont(font) + self.label_8.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_8.setObjectName("label_8") + self.lineEdit_5 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_5.setGeometry(QtCore.QRect(270, 30, 181, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_5.setFont(font) + self.lineEdit_5.setStyleSheet("background-color: #ffffff") + self.lineEdit_5.setObjectName("lineEdit_5") + self.buttonBox_2 = QtWidgets.QDialogButtonBox(self.widget_2) + self.buttonBox_2.setGeometry(QtCore.QRect(440, 270, 341, 32)) + self.buttonBox_2.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox_2.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox_2.setObjectName("buttonBox_2") + self.label_21 = QtWidgets.QLabel(self.widget_2) + self.label_21.setGeometry(QtCore.QRect(470, 30, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_21.setFont(font) + self.label_21.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_21.setObjectName("label_21") + self.label_22 = QtWidgets.QLabel(self.widget_2) + self.label_22.setGeometry(QtCore.QRect(470, 90, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_22.setFont(font) + self.label_22.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_22.setObjectName("label_22") + self.label_23 = QtWidgets.QLabel(self.widget_2) + self.label_23.setGeometry(QtCore.QRect(470, 200, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_23.setFont(font) + self.label_23.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_23.setObjectName("label_23") + self.label_24 = QtWidgets.QLabel(self.widget_2) + self.label_24.setGeometry(QtCore.QRect(470, 240, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_24.setFont(font) + self.label_24.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_24.setObjectName("label_24") + self.textBrowser_2 = QtWidgets.QTextBrowser(self.widget_2) + self.textBrowser_2.setGeometry(QtCore.QRect(20, 10, 231, 51)) + self.textBrowser_2.setObjectName("textBrowser_2") + self.textBrowser_3 = QtWidgets.QTextBrowser(self.widget_2) + self.textBrowser_3.setGeometry(QtCore.QRect(20, 70, 231, 51)) + self.textBrowser_3.setObjectName("textBrowser_3") + self.textBrowser_4 = QtWidgets.QTextBrowser(self.widget_2) + self.textBrowser_4.setGeometry(QtCore.QRect(20, 130, 231, 51)) + self.textBrowser_4.setObjectName("textBrowser_4") + self.lineEdit_7 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_7.setGeometry(QtCore.QRect(270, 90, 181, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_7.setFont(font) + self.lineEdit_7.setStyleSheet("background-color: #ffffff") + self.lineEdit_7.setObjectName("lineEdit_7") + self.lineEdit_8 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_8.setGeometry(QtCore.QRect(270, 150, 181, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_8.setFont(font) + self.lineEdit_8.setStyleSheet("background-color: #ffffff") + self.lineEdit_8.setObjectName("lineEdit_8") + self.lineEdit_9 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_9.setGeometry(QtCore.QRect(270, 200, 181, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_9.setFont(font) + self.lineEdit_9.setStyleSheet("background-color: #ffffff") + self.lineEdit_9.setObjectName("lineEdit_9") + self.lineEdit_10 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_10.setGeometry(QtCore.QRect(270, 240, 181, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_10.setFont(font) + self.lineEdit_10.setStyleSheet("background-color: #ffffff") + self.lineEdit_10.setObjectName("lineEdit_10") + self.label_25 = QtWidgets.QLabel(self.widget_2) + self.label_25.setGeometry(QtCore.QRect(470, 150, 51, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_25.setFont(font) + self.label_25.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.label_25.setObjectName("label_25") + self.pushButton = QtWidgets.QPushButton(Maintenance_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 35, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton.setIcon(icon) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + self.scrollArea = QtWidgets.QScrollArea(Maintenance_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 65, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.label_2 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_2.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_2.setFont(font) + self.label_2.setStyleSheet("background-color: rgb(240,230,230)") + self.label_2.setAlignment(QtCore.Qt.AlignCenter) + self.label_2.setObjectName("label_2") + self.widget = QtWidgets.QWidget(self.scrollAreaWidgetContents) + self.widget.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget.setStyleSheet("background-color: #fff9f9") + self.widget.setObjectName("widget") + self.verticalLayout = QtWidgets.QVBoxLayout(self.widget) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.pushButton_15 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_15.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_15.setIcon(icon1) + self.pushButton_15.setCheckable(True) + self.pushButton_15.setAutoDefault(True) + self.pushButton_15.setObjectName("pushButton_15") + self.verticalLayout.addWidget(self.pushButton_15) + self.widget_5 = QtWidgets.QWidget(self.widget) + self.widget_5.setObjectName("widget_5") + self.formLayout = QtWidgets.QFormLayout(self.widget_5) + self.formLayout.setObjectName("formLayout") + self.pushButton_20 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_20.setFont(font) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_20.setIcon(icon2) + self.pushButton_20.setObjectName("pushButton_20") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_20) + self.pushButton_21 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_21.setFont(font) + self.pushButton_21.setIcon(icon2) + self.pushButton_21.setObjectName("pushButton_21") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_21) + self.pushButton_22 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_22.setFont(font) + self.pushButton_22.setIcon(icon2) + self.pushButton_22.setObjectName("pushButton_22") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_22) + self.pushButton_23 = QtWidgets.QPushButton(self.widget_5, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_23.setFont(font) + self.pushButton_23.setIcon(icon2) + self.pushButton_23.setObjectName("pushButton_23") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_23) + self.verticalLayout.addWidget(self.widget_5) + self.pushButton_19 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_19.setFont(font) + self.pushButton_19.setObjectName("pushButton_19") + self.verticalLayout.addWidget(self.pushButton_19) + self.pushButton_16 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_16.setFont(font) + self.pushButton_16.setIcon(icon1) + self.pushButton_16.setCheckable(True) + self.pushButton_16.setObjectName("pushButton_16") + self.verticalLayout.addWidget(self.pushButton_16) + self.widget_8 = QtWidgets.QWidget(self.widget) + self.widget_8.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_8.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_8.setObjectName("widget_8") + self.pushButton_14 = QtWidgets.QPushButton(self.widget_8, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_14.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_14.setFont(font) + self.pushButton_14.setIcon(icon2) + self.pushButton_14.setObjectName("pushButton_14") + self.verticalLayout.addWidget(self.widget_8) + self.pushButton_17 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_17.setFont(font) + self.pushButton_17.setObjectName("pushButton_17") + self.verticalLayout.addWidget(self.pushButton_17) + self.pushButton_18 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_18.setFont(font) + self.pushButton_18.setObjectName("pushButton_18") + self.verticalLayout.addWidget(self.pushButton_18) + self.pushButton_10 = QtWidgets.QPushButton(self.widget, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_10.setFont(font) + self.pushButton_10.setObjectName("pushButton_10") + self.verticalLayout.addWidget(self.pushButton_10) + self.label_3 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_3.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_3.setFont(font) + self.label_3.setStyleSheet("background-color: rgb(240,230,230)") + self.label_3.setAlignment(QtCore.Qt.AlignCenter) + self.label_3.setObjectName("label_3") + self.textBrowser = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents) + self.textBrowser.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser.setStyleSheet("background-color: #fff9f9") + self.textBrowser.setObjectName("textBrowser") + self.verticalScrollBar = QtWidgets.QScrollBar(self.scrollAreaWidgetContents) + self.verticalScrollBar.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.widget.raise_() + self.label_2.raise_() + self.label_3.raise_() + self.textBrowser.raise_() + self.verticalScrollBar.raise_() + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + + self.retranslateUi(Maintenance_Dialog) + self.buttonBox_2.accepted.connect(self.handle_save) # type: ignore + self.buttonBox_2.rejected.connect(lambda: self.show_warning(Maintenance_Dialog)) # type: ignore + self.pushButton_15.toggled['bool'].connect(self.widget_5.setVisible) # type: ignore + self.pushButton_16.toggled['bool'].connect(self.widget_8.setVisible) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Maintenance_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + def retranslateUi(self, Maintenance_Dialog): + _translate = QtCore.QCoreApplication.translate + Maintenance_Dialog.setWindowTitle(_translate("Maintenance_Dialog", "Maintenance and Repair Data")) + self.pushButton_6.setText(_translate("Maintenance_Dialog", "Maintenance and Repair Data ")) + self.label_4.setText(_translate("Maintenance_Dialog", "Frequency of Routine Inspection")) + self.label_5.setText(_translate("Maintenance_Dialog", "Investment Ratio")) + self.label_6.setText(_translate("Maintenance_Dialog", "Interest Rate")) + self.label_7.setText(_translate("Maintenance_Dialog", "Duration of Study ")) + self.label_8.setText(_translate("Maintenance_Dialog", "Frequency of Periodic Maintenance")) + self.label_21.setText(_translate("Maintenance_Dialog", "(%)")) + self.label_22.setText(_translate("Maintenance_Dialog", "(%)")) + self.label_23.setText(_translate("Maintenance_Dialog", "(years)")) + self.label_24.setText(_translate("Maintenance_Dialog", "(years)")) + self.textBrowser_2.setHtml(_translate("Maintenance_Dialog", "\n" +"\n" +"

Periodic Maintenance Cost rate as percentage to total construction cost

")) + self.textBrowser_3.setHtml(_translate("Maintenance_Dialog", "\n" +"\n" +"

Annual Routine Inspection cost rate as percentage of total construction cost

")) + self.textBrowser_4.setHtml(_translate("Maintenance_Dialog", "\n" +"\n" +"

Repair and Rehabilitation cost rate as
percentage of total construction cost

")) + self.label_25.setText(_translate("Maintenance_Dialog", "(%)")) + self.pushButton.setText(_translate("Maintenance_Dialog", "Project Details Window ")) + self.label_2.setText(_translate("Maintenance_Dialog", "Input Parameters")) + self.pushButton_15.setText(_translate("Maintenance_Dialog", "Structure Works Data")) + self.pushButton_20.setText(_translate("Maintenance_Dialog", "Foundation")) + self.pushButton_21.setText(_translate("Maintenance_Dialog", "Super-Structure")) + self.pushButton_22.setText(_translate("Maintenance_Dialog", "Sub-Structure")) + self.pushButton_23.setText(_translate("Maintenance_Dialog", "Miscellaneous")) + self.pushButton_19.setText(_translate("Maintenance_Dialog", "Financial Data")) + self.pushButton_16.setText(_translate("Maintenance_Dialog", "Carbon Emission Data")) + self.pushButton_14.setText(_translate("Maintenance_Dialog", "Carbon Emission Cost Data")) + self.pushButton_17.setText(_translate("Maintenance_Dialog", "Bridge and Traffic Data")) + self.pushButton_18.setText(_translate("Maintenance_Dialog", "Maintenance and Repair")) + self.pushButton_10.setText(_translate("Maintenance_Dialog", "Disposal and Recycling")) + self.label_3.setText(_translate("Maintenance_Dialog", "Output")) + self.textBrowser.setHtml(_translate("Maintenance_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + + def validate_data(self): + """Validate input data before saving""" + try: + # Validate all numeric fields + float(self.lineEdit_5.text()) if self.lineEdit_5.text() else 0 # Periodic Maintenance Cost Rate + float(self.lineEdit_7.text()) if self.lineEdit_7.text() else 0 # Annual Routine Inspection Cost Rate + float(self.lineEdit_8.text()) if self.lineEdit_8.text() else 0 # Repair Rehabilitation Cost Rate + float(self.lineEdit_9.text()) if self.lineEdit_9.text() else 0 # Frequency of Periodic Maintenance + float(self.lineEdit_10.text()) if self.lineEdit_10.text() else 0 # Frequency of Routine Inspection + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all numeric fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return False + + data = { + "periodic_maintenance": { + "cost_rate": self.lineEdit_5.text(), + "description": "Periodic Maintenance Cost rate as percentage to total construction cost" + }, + "routine_inspection": { + "cost_rate": self.lineEdit_7.text(), + "frequency": self.lineEdit_10.text(), + "description": "Annual Routine Inspection cost rate as percentage of total construction cost" + }, + "repair_rehabilitation": { + "cost_rate": self.lineEdit_8.text(), + "description": "Repair and Rehabilitation cost rate as percentage of total construction cost" + }, + "periodic_maintenance_frequency": self.lineEdit_9.text(), + "financial_parameters": { + "interest_rate": self.label_6.text().replace("(%)", "").strip(), + "investment_ratio": self.label_5.text().replace("(%)", "").strip(), + "study_duration": self.label_7.text().replace("(years)", "").strip() + } + } + + # Save to JSON file + try: + import json + from datetime import datetime + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"maintenance_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Maintenance data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + def handle_save(self): + """Handle the save operation and close the dialog if successful""" + if self.save_data(): # Only close if save was successful + Maintenance_Dialog.accept() + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + Maintenance_Dialog = QtWidgets.QDialog() + ui = Ui_Maintenance_Dialog() + ui.setupUi(Maintenance_Dialog) + Maintenance_Dialog.show() + sys.exit(app.exec_()) diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_Miscellaneous_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_Miscellaneous_Window.py new file mode 100644 index 0000000..cfe53e2 --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_Miscellaneous_Window.py @@ -0,0 +1,743 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_Miscellaneous_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox + + + + + + +#from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + + + + +class Ui_Miscellaneous_Dialog(object): + def openBridgeTrafficWindow(self): + from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, Miscellaneous_Dialog): + Miscellaneous_Dialog.setObjectName("Miscellaneous_Dialog") + Miscellaneous_Dialog.resize(1440, 900) + Miscellaneous_Dialog.setStyleSheet("background-color:#FAFAFA") + self.pushButton = QtWidgets.QPushButton(Miscellaneous_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 10, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton.setIcon(icon) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + self.widget_2 = QtWidgets.QWidget(Miscellaneous_Dialog) + self.widget_2.setGeometry(QtCore.QRect(350, 35, 778, 624)) + self.widget_2.setStyleSheet("background-color: #fff9f9") + self.widget_2.setObjectName("widget_2") + self.label_38 = QtWidgets.QLabel(self.widget_2) + self.label_38.setGeometry(QtCore.QRect(20, 10, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_38.setFont(font) + self.label_38.setObjectName("label_38") + self.comboBox_13 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_13.setGeometry(QtCore.QRect(140, 10, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_13.setFont(font) + self.comboBox_13.setStyleSheet("background-color: #ffffff") + self.comboBox_13.setObjectName("comboBox_13") + self.comboBox_13.addItem("") + self.pushButton_10 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_10.setGeometry(QtCore.QRect(350, 10, 190, 23)) + self.pushButton_10.setStyleSheet("background-color: #ffffff") + self.pushButton_10.setObjectName("pushButton_10") + self.label_39 = QtWidgets.QLabel(self.widget_2) + self.label_39.setGeometry(QtCore.QRect(20, 70, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_39.setFont(font) + self.label_39.setAlignment(QtCore.Qt.AlignCenter) + self.label_39.setObjectName("label_39") + self.label_40 = QtWidgets.QLabel(self.widget_2) + self.label_40.setGeometry(QtCore.QRect(551, 70, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_40.setFont(font) + self.label_40.setAlignment(QtCore.Qt.AlignCenter) + self.label_40.setObjectName("label_40") + self.label_41 = QtWidgets.QLabel(self.widget_2) + self.label_41.setGeometry(QtCore.QRect(191, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_41.setFont(font) + self.label_41.setAlignment(QtCore.Qt.AlignCenter) + self.label_41.setObjectName("label_41") + self.label_42 = QtWidgets.QLabel(self.widget_2) + self.label_42.setGeometry(QtCore.QRect(311, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_42.setFont(font) + self.label_42.setAlignment(QtCore.Qt.AlignCenter) + self.label_42.setObjectName("label_42") + self.label_43 = QtWidgets.QLabel(self.widget_2) + self.label_43.setGeometry(QtCore.QRect(431, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_43.setFont(font) + self.label_43.setAlignment(QtCore.Qt.AlignCenter) + self.label_43.setObjectName("label_43") + self.comboBox_14 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_14.setGeometry(QtCore.QRect(30, 100, 140, 22)) + self.comboBox_14.setStyleSheet("background-color: #ffffff") + self.comboBox_14.setObjectName("comboBox_14") + self.comboBox_15 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_15.setGeometry(QtCore.QRect(30, 130, 140, 22)) + self.comboBox_15.setStyleSheet("background-color: #ffffff") + self.comboBox_15.setObjectName("comboBox_15") + self.lineEdit_25 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_25.setGeometry(QtCore.QRect(210, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_25.setFont(font) + self.lineEdit_25.setStyleSheet("background-color: #ffffff") + self.lineEdit_25.setObjectName("lineEdit_25") + self.lineEdit_26 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_26.setGeometry(QtCore.QRect(210, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_26.setFont(font) + self.lineEdit_26.setStyleSheet("background-color: #ffffff") + self.lineEdit_26.setObjectName("lineEdit_26") + self.label_44 = QtWidgets.QLabel(self.widget_2) + self.label_44.setGeometry(QtCore.QRect(340, 100, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_44.setFont(font) + self.label_44.setStyleSheet("background-color: #ffffff") + self.label_44.setAlignment(QtCore.Qt.AlignCenter) + self.label_44.setObjectName("label_44") + self.label_45 = QtWidgets.QLabel(self.widget_2) + self.label_45.setGeometry(QtCore.QRect(340, 130, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_45.setFont(font) + self.label_45.setStyleSheet("background-color: #ffffff") + self.label_45.setAlignment(QtCore.Qt.AlignCenter) + self.label_45.setObjectName("label_45") + self.lineEdit_27 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_27.setGeometry(QtCore.QRect(450, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_27.setFont(font) + self.lineEdit_27.setStyleSheet("background-color: #ffffff") + self.lineEdit_27.setObjectName("lineEdit_27") + self.lineEdit_28 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_28.setGeometry(QtCore.QRect(450, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_28.setFont(font) + self.lineEdit_28.setStyleSheet("background-color: #ffffff") + self.lineEdit_28.setObjectName("lineEdit_28") + self.lineEdit_29 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_29.setGeometry(QtCore.QRect(570, 100, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_29.setFont(font) + self.lineEdit_29.setStyleSheet("background-color: #ffffff") + self.lineEdit_29.setObjectName("lineEdit_29") + self.lineEdit_30 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_30.setGeometry(QtCore.QRect(570, 130, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_30.setFont(font) + self.lineEdit_30.setStyleSheet("background-color: #ffffff") + self.lineEdit_30.setObjectName("lineEdit_30") + self.pushButton_13 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_13.setGeometry(QtCore.QRect(300, 200, 190, 23)) + self.pushButton_13.setStyleSheet("background-color: #ffffff") + self.pushButton_13.setObjectName("pushButton_13") + self.label_46 = QtWidgets.QLabel(self.widget_2) + self.label_46.setGeometry(QtCore.QRect(30, 330, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_46.setFont(font) + self.label_46.setAlignment(QtCore.Qt.AlignCenter) + self.label_46.setObjectName("label_46") + self.comboBox_16 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_16.setGeometry(QtCore.QRect(150, 270, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_16.setFont(font) + self.comboBox_16.setStyleSheet("background-color: #ffffff") + self.comboBox_16.setObjectName("comboBox_16") + self.comboBox_16.addItem("") + self.label_47 = QtWidgets.QLabel(self.widget_2) + self.label_47.setGeometry(QtCore.QRect(441, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_47.setFont(font) + self.label_47.setAlignment(QtCore.Qt.AlignCenter) + self.label_47.setObjectName("label_47") + self.label_48 = QtWidgets.QLabel(self.widget_2) + self.label_48.setGeometry(QtCore.QRect(350, 390, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_48.setFont(font) + self.label_48.setStyleSheet("background-color: #ffffff") + self.label_48.setAlignment(QtCore.Qt.AlignCenter) + self.label_48.setObjectName("label_48") + self.label_49 = QtWidgets.QLabel(self.widget_2) + self.label_49.setGeometry(QtCore.QRect(561, 330, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_49.setFont(font) + self.label_49.setAlignment(QtCore.Qt.AlignCenter) + self.label_49.setObjectName("label_49") + self.label_50 = QtWidgets.QLabel(self.widget_2) + self.label_50.setGeometry(QtCore.QRect(350, 360, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_50.setFont(font) + self.label_50.setStyleSheet("background-color: #ffffff") + self.label_50.setAlignment(QtCore.Qt.AlignCenter) + self.label_50.setObjectName("label_50") + self.label_51 = QtWidgets.QLabel(self.widget_2) + self.label_51.setGeometry(QtCore.QRect(321, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_51.setFont(font) + self.label_51.setAlignment(QtCore.Qt.AlignCenter) + self.label_51.setObjectName("label_51") + self.lineEdit_31 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_31.setGeometry(QtCore.QRect(220, 390, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_31.setFont(font) + self.lineEdit_31.setStyleSheet("background-color: #ffffff") + self.lineEdit_31.setObjectName("lineEdit_31") + self.lineEdit_32 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_32.setGeometry(QtCore.QRect(580, 360, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_32.setFont(font) + self.lineEdit_32.setStyleSheet("background-color: #ffffff") + self.lineEdit_32.setObjectName("lineEdit_32") + self.label_52 = QtWidgets.QLabel(self.widget_2) + self.label_52.setGeometry(QtCore.QRect(201, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_52.setFont(font) + self.label_52.setAlignment(QtCore.Qt.AlignCenter) + self.label_52.setObjectName("label_52") + self.pushButton_14 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_14.setGeometry(QtCore.QRect(310, 460, 190, 23)) + self.pushButton_14.setStyleSheet("background-color: #ffffff") + self.pushButton_14.setObjectName("pushButton_14") + self.lineEdit_33 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_33.setGeometry(QtCore.QRect(220, 360, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_33.setFont(font) + self.lineEdit_33.setStyleSheet("background-color: #ffffff") + self.lineEdit_33.setObjectName("lineEdit_33") + self.lineEdit_34 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_34.setGeometry(QtCore.QRect(460, 360, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_34.setFont(font) + self.lineEdit_34.setStyleSheet("background-color: #ffffff") + self.lineEdit_34.setObjectName("lineEdit_34") + self.lineEdit_35 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_35.setGeometry(QtCore.QRect(460, 390, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_35.setFont(font) + self.lineEdit_35.setStyleSheet("background-color: #ffffff") + self.lineEdit_35.setObjectName("lineEdit_35") + self.pushButton_15 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_15.setGeometry(QtCore.QRect(360, 270, 190, 23)) + self.pushButton_15.setStyleSheet("background-color: #ffffff") + self.pushButton_15.setObjectName("pushButton_15") + self.label_53 = QtWidgets.QLabel(self.widget_2) + self.label_53.setGeometry(QtCore.QRect(30, 270, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_53.setFont(font) + self.label_53.setObjectName("label_53") + self.lineEdit_36 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_36.setGeometry(QtCore.QRect(580, 390, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_36.setFont(font) + self.lineEdit_36.setStyleSheet("background-color: #ffffff") + self.lineEdit_36.setObjectName("lineEdit_36") + self.comboBox_17 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_17.setGeometry(QtCore.QRect(40, 360, 140, 22)) + self.comboBox_17.setStyleSheet("background-color: #ffffff") + self.comboBox_17.setObjectName("comboBox_17") + self.comboBox_17.addItem("") + self.comboBox_17.addItem("") + self.comboBox_18 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_18.setGeometry(QtCore.QRect(40, 390, 140, 22)) + self.comboBox_18.setStyleSheet("background-color: #ffffff") + self.comboBox_18.setObjectName("comboBox_18") + self.comboBox_18.addItem("") + self.comboBox_18.addItem("") + self.line_5 = QtWidgets.QFrame(self.widget_2) + self.line_5.setGeometry(QtCore.QRect(10, 250, 761, 16)) + self.line_5.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_5.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_5.setLineWidth(2) + self.line_5.setMidLineWidth(2) + self.line_5.setFrameShape(QtWidgets.QFrame.HLine) + self.line_5.setObjectName("line_5") + self.line_6 = QtWidgets.QFrame(self.widget_2) + self.line_6.setGeometry(QtCore.QRect(10, 500, 761, 16)) + self.line_6.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_6.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_6.setLineWidth(2) + self.line_6.setMidLineWidth(2) + self.line_6.setFrameShape(QtWidgets.QFrame.HLine) + self.line_6.setObjectName("line_6") + self.buttonBox = QtWidgets.QDialogButtonBox(self.widget_2) + self.buttonBox.setGeometry(QtCore.QRect(430, 590, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox.setObjectName("buttonBox") + self.label = QtWidgets.QLabel(Miscellaneous_Dialog) + self.label.setGeometry(QtCore.QRect(10, 35, 244, 691)) + font = QtGui.QFont() + font.setPointSize(10) + self.label.setFont(font) + self.label.setStyleSheet("background-color: rgb(240,230,230)") + self.label.setText("") + self.label.setObjectName("label") + self.pushButton_6 = QtWidgets.QPushButton(Miscellaneous_Dialog) + self.pushButton_6.setGeometry(QtCore.QRect(350, 10, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_6.setFont(font) + self.pushButton_6.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_6.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_6.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton_6.setIcon(icon) + self.pushButton_6.setAutoRepeat(False) + self.pushButton_6.setObjectName("pushButton_6") + self.scrollArea = QtWidgets.QScrollArea(Miscellaneous_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 40, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents_3 = QtWidgets.QWidget() + self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3") + self.label_54 = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.label_54.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_54.setFont(font) + self.label_54.setStyleSheet("background-color: rgb(240,230,230)") + self.label_54.setAlignment(QtCore.Qt.AlignCenter) + self.label_54.setObjectName("label_54") + self.widget_4 = QtWidgets.QWidget(self.scrollAreaWidgetContents_3) + self.widget_4.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget_4.setStyleSheet("background-color: #fff9f9") + self.widget_4.setObjectName("widget_4") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.widget_4) + self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.pushButton_34 = QtWidgets.QPushButton(self.widget_4) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_34.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_34.setIcon(icon1) + self.pushButton_34.setCheckable(True) + self.pushButton_34.setAutoDefault(True) + self.pushButton_34.setObjectName("pushButton_34") + self.verticalLayout_3.addWidget(self.pushButton_34) + self.widget_7 = QtWidgets.QWidget(self.widget_4) + self.widget_7.setObjectName("widget_7") + self.formLayout_3 = QtWidgets.QFormLayout(self.widget_7) + self.formLayout_3.setObjectName("formLayout_3") + self.pushButton_35 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_35.setFont(font) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_35.setIcon(icon2) + self.pushButton_35.setObjectName("pushButton_35") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_35) + self.pushButton_36 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_36.setFont(font) + self.pushButton_36.setIcon(icon2) + self.pushButton_36.setObjectName("pushButton_36") + self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_36) + self.pushButton_37 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_37.setFont(font) + self.pushButton_37.setIcon(icon2) + self.pushButton_37.setObjectName("pushButton_37") + self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_37) + self.pushButton_38 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_38.setFont(font) + self.pushButton_38.setIcon(icon2) + self.pushButton_38.setObjectName("pushButton_38") + self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_38) + self.verticalLayout_3.addWidget(self.widget_7) + self.pushButton_39 = QtWidgets.QPushButton(self.widget_4, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_39.setFont(font) + self.pushButton_39.setObjectName("pushButton_39") + self.verticalLayout_3.addWidget(self.pushButton_39) + self.pushButton_40 = QtWidgets.QPushButton(self.widget_4) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_40.setFont(font) + self.pushButton_40.setIcon(icon1) + self.pushButton_40.setCheckable(True) + self.pushButton_40.setObjectName("pushButton_40") + self.verticalLayout_3.addWidget(self.pushButton_40) + self.widget_10 = QtWidgets.QWidget(self.widget_4) + self.widget_10.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_10.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_10.setObjectName("widget_10") + self.pushButton_41 = QtWidgets.QPushButton(self.widget_10, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_41.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_41.setFont(font) + self.pushButton_41.setIcon(icon2) + self.pushButton_41.setObjectName("pushButton_41") + self.verticalLayout_3.addWidget(self.widget_10) + self.pushButton_42 = QtWidgets.QPushButton(self.widget_4, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_42.setFont(font) + self.pushButton_42.setObjectName("pushButton_42") + self.verticalLayout_3.addWidget(self.pushButton_42) + self.pushButton_43 = QtWidgets.QPushButton(self.widget_4, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_43.setFont(font) + self.pushButton_43.setObjectName("pushButton_43") + self.verticalLayout_3.addWidget(self.pushButton_43) + self.pushButton_16 = QtWidgets.QPushButton(self.widget_4, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_16.setFont(font) + self.pushButton_16.setObjectName("pushButton_16") + self.verticalLayout_3.addWidget(self.pushButton_16) + self.label_55 = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.label_55.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_55.setFont(font) + self.label_55.setStyleSheet("background-color: rgb(240,230,230)") + self.label_55.setAlignment(QtCore.Qt.AlignCenter) + self.label_55.setObjectName("label_55") + self.textBrowser_3 = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents_3) + self.textBrowser_3.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser_3.setStyleSheet("background-color: #fff9f9") + self.textBrowser_3.setObjectName("textBrowser_3") + self.verticalScrollBar_3 = QtWidgets.QScrollBar(self.scrollAreaWidgetContents_3) + self.verticalScrollBar_3.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar_3.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar_3.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar_3.setObjectName("verticalScrollBar_3") + self.scrollArea.setWidget(self.scrollAreaWidgetContents_3) + + self.retranslateUi(Miscellaneous_Dialog) + self.buttonBox.accepted.connect(self.handle_save) # type: ignore + self.buttonBox.rejected.connect(lambda: self.show_warning(Miscellaneous_Dialog)) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Miscellaneous_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + def retranslateUi(self, Miscellaneous_Dialog): + _translate = QtCore.QCoreApplication.translate + Miscellaneous_Dialog.setWindowTitle(_translate("Miscellaneous_Dialog", "Dialog")) + self.pushButton.setText(_translate("Miscellaneous_Dialog", "Project Details Window ")) + self.label_38.setText(_translate("Miscellaneous_Dialog", "Components:")) + self.comboBox_13.setItemText(0, _translate("Miscellaneous_Dialog", "Expansion Joint")) + self.pushButton_10.setText(_translate("Miscellaneous_Dialog", "+ Add Sub-Component")) + self.label_39.setText(_translate("Miscellaneous_Dialog", "Material Type and Grade")) + self.label_40.setText(_translate("Miscellaneous_Dialog", "Rate Data Source")) + self.label_41.setText(_translate("Miscellaneous_Dialog", "Quantity")) + self.label_42.setText(_translate("Miscellaneous_Dialog", "Unit")) + self.label_43.setText(_translate("Miscellaneous_Dialog", "Rate")) + self.label_44.setText(_translate("Miscellaneous_Dialog", "

m3

")) + self.label_45.setText(_translate("Miscellaneous_Dialog", "kg")) + self.pushButton_13.setText(_translate("Miscellaneous_Dialog", "+ Add Material")) + self.label_46.setText(_translate("Miscellaneous_Dialog", "Material Type and Grade")) + self.comboBox_16.setItemText(0, _translate("Miscellaneous_Dialog", "Bearing")) + self.label_47.setText(_translate("Miscellaneous_Dialog", "Rate")) + self.label_48.setText(_translate("Miscellaneous_Dialog", "kg")) + self.label_49.setText(_translate("Miscellaneous_Dialog", "Rate Data Source")) + self.label_50.setText(_translate("Miscellaneous_Dialog", "

m3

")) + self.label_51.setText(_translate("Miscellaneous_Dialog", "Unit")) + self.label_52.setText(_translate("Miscellaneous_Dialog", "Quantity")) + self.pushButton_14.setText(_translate("Miscellaneous_Dialog", "+ Add Material")) + self.pushButton_15.setText(_translate("Miscellaneous_Dialog", "+ Add Sub-Component")) + self.label_53.setText(_translate("Miscellaneous_Dialog", "Components:")) + self.comboBox_17.setItemText(0, _translate("Miscellaneous_Dialog", "Concrete")) + self.comboBox_17.setItemText(1, _translate("Miscellaneous_Dialog", "Steel")) + self.comboBox_18.setItemText(0, _translate("Miscellaneous_Dialog", "Steel")) + self.comboBox_18.setItemText(1, _translate("Miscellaneous_Dialog", "Concrete")) + self.pushButton_6.setText(_translate("Miscellaneous_Dialog", "Miscellaneous ")) + self.label_54.setText(_translate("Miscellaneous_Dialog", "Input Parameters")) + self.pushButton_34.setText(_translate("Miscellaneous_Dialog", "Structure Works Data")) + self.pushButton_35.setText(_translate("Miscellaneous_Dialog", "Foundation")) + self.pushButton_36.setText(_translate("Miscellaneous_Dialog", "Super-Structure")) + self.pushButton_37.setText(_translate("Miscellaneous_Dialog", "Sub-Structure")) + self.pushButton_38.setText(_translate("Miscellaneous_Dialog", "Miscellaneous")) + self.pushButton_39.setText(_translate("Miscellaneous_Dialog", "Financial Data")) + self.pushButton_40.setText(_translate("Miscellaneous_Dialog", "Carbon Emission Data")) + self.pushButton_41.setText(_translate("Miscellaneous_Dialog", "Carbon Emission Cost Data")) + self.pushButton_42.setText(_translate("Miscellaneous_Dialog", "Bridge and Traffic Data")) + self.pushButton_43.setText(_translate("Miscellaneous_Dialog", "Maintenance and Repair")) + self.pushButton_16.setText(_translate("Miscellaneous_Dialog", "Disposal and Recycling")) + self.label_55.setText(_translate("Miscellaneous_Dialog", "Output")) + self.textBrowser_3.setHtml(_translate("Miscellaneous_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + + def validate_data(self): + """Validate input data before saving""" + try: + # Validate all numeric fields + fields_to_validate = [ + self.lineEdit_25, self.lineEdit_26, self.lineEdit_27, + self.lineEdit_28, self.lineEdit_29, self.lineEdit_30, + self.lineEdit_31, self.lineEdit_32, self.lineEdit_33, + self.lineEdit_34, self.lineEdit_35, self.lineEdit_36 + ] + + for field in fields_to_validate: + if field.text(): # Only validate if field is not empty + float(field.text()) + + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all numeric fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return False + + data = { + "expansion_joint": { + "component": self.comboBox_13.currentText(), + "materials": [ + { + "type_grade": self.comboBox_14.currentText(), + "quantity": self.lineEdit_25.text(), + "unit": self.label_44.text(), + "rate": self.lineEdit_27.text(), + "rate_source": self.lineEdit_29.text() + }, + { + "type_grade": self.comboBox_15.currentText(), + "quantity": self.lineEdit_26.text(), + "unit": self.label_45.text(), + "rate": self.lineEdit_28.text(), + "rate_source": self.lineEdit_30.text() + } + ] + }, + "bearing": { + "component": self.comboBox_16.currentText(), + "materials": [ + { + "type_grade": self.comboBox_17.currentText(), + "quantity": self.lineEdit_33.text(), + "unit": self.label_50.text(), + "rate": self.lineEdit_34.text(), + "rate_source": self.lineEdit_32.text() + }, + { + "type_grade": self.comboBox_18.currentText(), + "quantity": self.lineEdit_31.text(), + "unit": self.label_48.text(), + "rate": self.lineEdit_35.text(), + "rate_source": self.lineEdit_36.text() + } + ] + } + } + + # Save to JSON file + try: + import json + from datetime import datetime + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"miscellaneous_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Miscellaneous data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + def handle_save(self): + """Handle the save operation and close the dialog if successful""" + if self.save_data(): # Only close if save was successful + Miscellaneous_Dialog.accept() + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + Miscellaneous_Dialog = QtWidgets.QDialog() + ui = Ui_Miscellaneous_Dialog() + ui.setupUi(Miscellaneous_Dialog) + Miscellaneous_Dialog.show() + sys.exit(app.exec_()) diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_SubStructure_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_SubStructure_Window.py new file mode 100644 index 0000000..62f1c04 --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_SubStructure_Window.py @@ -0,0 +1,723 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_SubStructure_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox +#from Warning_Window import Ui_Warning_Dialog + + + + + + + +#from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + + + +class Ui_SubStructure_Dialog(object): + def openBridgeTrafficWindow(self): + from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, SubStructure_Dialog): + SubStructure_Dialog.setObjectName("SubStructure_Dialog") + SubStructure_Dialog.resize(1440, 900) + SubStructure_Dialog.setStyleSheet("background-color:#FAFAFA") + self.label = QtWidgets.QLabel(SubStructure_Dialog) + self.label.setGeometry(QtCore.QRect(10, 35, 244, 691)) + font = QtGui.QFont() + font.setPointSize(10) + self.label.setFont(font) + self.label.setStyleSheet("background-color: rgb(240,230,230)") + self.label.setText("") + self.label.setObjectName("label") + self.pushButton = QtWidgets.QPushButton(SubStructure_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 10, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton.setIcon(icon) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + self.widget_2 = QtWidgets.QWidget(SubStructure_Dialog) + self.widget_2.setGeometry(QtCore.QRect(350, 35, 778, 624)) + self.widget_2.setStyleSheet("background-color: #fff9f9") + self.widget_2.setObjectName("widget_2") + self.label_38 = QtWidgets.QLabel(self.widget_2) + self.label_38.setGeometry(QtCore.QRect(20, 10, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_38.setFont(font) + self.label_38.setObjectName("label_38") + self.comboBox_13 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_13.setGeometry(QtCore.QRect(140, 10, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_13.setFont(font) + self.comboBox_13.setStyleSheet("background-color: #ffffff") + self.comboBox_13.setObjectName("comboBox_13") + self.comboBox_13.addItem("") + self.pushButton_10 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_10.setGeometry(QtCore.QRect(350, 10, 190, 23)) + self.pushButton_10.setStyleSheet("background-color: #ffffff") + self.pushButton_10.setObjectName("pushButton_10") + self.label_39 = QtWidgets.QLabel(self.widget_2) + self.label_39.setGeometry(QtCore.QRect(20, 70, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_39.setFont(font) + self.label_39.setAlignment(QtCore.Qt.AlignCenter) + self.label_39.setObjectName("label_39") + self.label_40 = QtWidgets.QLabel(self.widget_2) + self.label_40.setGeometry(QtCore.QRect(551, 70, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_40.setFont(font) + self.label_40.setAlignment(QtCore.Qt.AlignCenter) + self.label_40.setObjectName("label_40") + self.label_41 = QtWidgets.QLabel(self.widget_2) + self.label_41.setGeometry(QtCore.QRect(191, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_41.setFont(font) + self.label_41.setAlignment(QtCore.Qt.AlignCenter) + self.label_41.setObjectName("label_41") + self.label_42 = QtWidgets.QLabel(self.widget_2) + self.label_42.setGeometry(QtCore.QRect(311, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_42.setFont(font) + self.label_42.setAlignment(QtCore.Qt.AlignCenter) + self.label_42.setObjectName("label_42") + self.label_43 = QtWidgets.QLabel(self.widget_2) + self.label_43.setGeometry(QtCore.QRect(431, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_43.setFont(font) + self.label_43.setAlignment(QtCore.Qt.AlignCenter) + self.label_43.setObjectName("label_43") + self.comboBox_14 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_14.setGeometry(QtCore.QRect(30, 100, 140, 22)) + self.comboBox_14.setStyleSheet("background-color: #ffffff") + self.comboBox_14.setObjectName("comboBox_14") + self.comboBox_15 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_15.setGeometry(QtCore.QRect(30, 130, 140, 22)) + self.comboBox_15.setStyleSheet("background-color: #ffffff") + self.comboBox_15.setObjectName("comboBox_15") + self.lineEdit_25 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_25.setGeometry(QtCore.QRect(210, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_25.setFont(font) + self.lineEdit_25.setStyleSheet("background-color: #ffffff") + self.lineEdit_25.setObjectName("lineEdit_25") + self.lineEdit_26 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_26.setGeometry(QtCore.QRect(210, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_26.setFont(font) + self.lineEdit_26.setStyleSheet("background-color: #ffffff") + self.lineEdit_26.setObjectName("lineEdit_26") + self.label_44 = QtWidgets.QLabel(self.widget_2) + self.label_44.setGeometry(QtCore.QRect(340, 100, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_44.setFont(font) + self.label_44.setStyleSheet("background-color: #ffffff") + self.label_44.setAlignment(QtCore.Qt.AlignCenter) + self.label_44.setObjectName("label_44") + self.label_45 = QtWidgets.QLabel(self.widget_2) + self.label_45.setGeometry(QtCore.QRect(340, 130, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_45.setFont(font) + self.label_45.setStyleSheet("background-color: #ffffff") + self.label_45.setAlignment(QtCore.Qt.AlignCenter) + self.label_45.setObjectName("label_45") + self.lineEdit_27 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_27.setGeometry(QtCore.QRect(450, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_27.setFont(font) + self.lineEdit_27.setStyleSheet("background-color: #ffffff") + self.lineEdit_27.setObjectName("lineEdit_27") + self.lineEdit_28 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_28.setGeometry(QtCore.QRect(450, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_28.setFont(font) + self.lineEdit_28.setStyleSheet("background-color: #ffffff") + self.lineEdit_28.setObjectName("lineEdit_28") + self.lineEdit_29 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_29.setGeometry(QtCore.QRect(570, 100, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_29.setFont(font) + self.lineEdit_29.setStyleSheet("background-color: #ffffff") + self.lineEdit_29.setObjectName("lineEdit_29") + self.lineEdit_30 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_30.setGeometry(QtCore.QRect(570, 130, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_30.setFont(font) + self.lineEdit_30.setStyleSheet("background-color: #ffffff") + self.lineEdit_30.setObjectName("lineEdit_30") + self.pushButton_13 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_13.setGeometry(QtCore.QRect(300, 200, 190, 23)) + self.pushButton_13.setStyleSheet("background-color: #ffffff") + self.pushButton_13.setObjectName("pushButton_13") + self.label_46 = QtWidgets.QLabel(self.widget_2) + self.label_46.setGeometry(QtCore.QRect(30, 330, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_46.setFont(font) + self.label_46.setAlignment(QtCore.Qt.AlignCenter) + self.label_46.setObjectName("label_46") + self.comboBox_16 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_16.setGeometry(QtCore.QRect(150, 270, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_16.setFont(font) + self.comboBox_16.setStyleSheet("background-color: #ffffff") + self.comboBox_16.setObjectName("comboBox_16") + self.comboBox_16.addItem("") + self.label_47 = QtWidgets.QLabel(self.widget_2) + self.label_47.setGeometry(QtCore.QRect(441, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_47.setFont(font) + self.label_47.setAlignment(QtCore.Qt.AlignCenter) + self.label_47.setObjectName("label_47") + self.label_48 = QtWidgets.QLabel(self.widget_2) + self.label_48.setGeometry(QtCore.QRect(350, 390, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_48.setFont(font) + self.label_48.setStyleSheet("background-color: #ffffff") + self.label_48.setAlignment(QtCore.Qt.AlignCenter) + self.label_48.setObjectName("label_48") + self.label_49 = QtWidgets.QLabel(self.widget_2) + self.label_49.setGeometry(QtCore.QRect(561, 330, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_49.setFont(font) + self.label_49.setAlignment(QtCore.Qt.AlignCenter) + self.label_49.setObjectName("label_49") + self.label_50 = QtWidgets.QLabel(self.widget_2) + self.label_50.setGeometry(QtCore.QRect(350, 360, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_50.setFont(font) + self.label_50.setStyleSheet("background-color: #ffffff") + self.label_50.setAlignment(QtCore.Qt.AlignCenter) + self.label_50.setObjectName("label_50") + self.label_51 = QtWidgets.QLabel(self.widget_2) + self.label_51.setGeometry(QtCore.QRect(321, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_51.setFont(font) + self.label_51.setAlignment(QtCore.Qt.AlignCenter) + self.label_51.setObjectName("label_51") + self.lineEdit_31 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_31.setGeometry(QtCore.QRect(220, 390, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_31.setFont(font) + self.lineEdit_31.setStyleSheet("background-color: #ffffff") + self.lineEdit_31.setObjectName("lineEdit_31") + self.lineEdit_32 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_32.setGeometry(QtCore.QRect(580, 360, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_32.setFont(font) + self.lineEdit_32.setStyleSheet("background-color: #ffffff") + self.lineEdit_32.setObjectName("lineEdit_32") + self.label_52 = QtWidgets.QLabel(self.widget_2) + self.label_52.setGeometry(QtCore.QRect(201, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_52.setFont(font) + self.label_52.setAlignment(QtCore.Qt.AlignCenter) + self.label_52.setObjectName("label_52") + self.pushButton_14 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_14.setGeometry(QtCore.QRect(310, 460, 190, 23)) + self.pushButton_14.setStyleSheet("background-color: #ffffff") + self.pushButton_14.setObjectName("pushButton_14") + self.pushButton_15 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_15.setGeometry(QtCore.QRect(360, 270, 190, 23)) + self.pushButton_15.setStyleSheet("background-color: #ffffff") + self.pushButton_15.setObjectName("pushButton_15") + self.label_53 = QtWidgets.QLabel(self.widget_2) + self.label_53.setGeometry(QtCore.QRect(30, 270, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_53.setFont(font) + self.label_53.setObjectName("label_53") + self.lineEdit_36 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_36.setGeometry(QtCore.QRect(580, 390, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_36.setFont(font) + self.lineEdit_36.setStyleSheet("background-color: #ffffff") + self.lineEdit_36.setObjectName("lineEdit_36") + self.comboBox_17 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_17.setGeometry(QtCore.QRect(40, 360, 140, 22)) + self.comboBox_17.setStyleSheet("background-color: #ffffff") + self.comboBox_17.setObjectName("comboBox_17") + self.comboBox_17.addItem("") + self.comboBox_17.addItem("") + self.comboBox_18 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_18.setGeometry(QtCore.QRect(40, 390, 140, 22)) + self.comboBox_18.setStyleSheet("background-color: #ffffff") + self.comboBox_18.setObjectName("comboBox_18") + self.comboBox_18.addItem("") + self.comboBox_18.addItem("") + self.line_5 = QtWidgets.QFrame(self.widget_2) + self.line_5.setGeometry(QtCore.QRect(10, 250, 761, 16)) + self.line_5.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_5.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_5.setLineWidth(2) + self.line_5.setMidLineWidth(2) + self.line_5.setFrameShape(QtWidgets.QFrame.HLine) + self.line_5.setObjectName("line_5") + self.line_6 = QtWidgets.QFrame(self.widget_2) + self.line_6.setGeometry(QtCore.QRect(10, 500, 761, 16)) + self.line_6.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_6.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_6.setLineWidth(2) + self.line_6.setMidLineWidth(2) + self.line_6.setFrameShape(QtWidgets.QFrame.HLine) + self.line_6.setObjectName("line_6") + self.buttonBox = QtWidgets.QDialogButtonBox(self.widget_2) + self.buttonBox.setGeometry(QtCore.QRect(430, 590, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox.setObjectName("buttonBox") + self.pushButton_6 = QtWidgets.QPushButton(SubStructure_Dialog) + self.pushButton_6.setGeometry(QtCore.QRect(350, 10, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_6.setFont(font) + self.pushButton_6.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_6.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_6.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton_6.setIcon(icon) + self.pushButton_6.setAutoRepeat(False) + self.pushButton_6.setObjectName("pushButton_6") + self.scrollArea = QtWidgets.QScrollArea(SubStructure_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 40, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents_3 = QtWidgets.QWidget() + self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3") + self.label_54 = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.label_54.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_54.setFont(font) + self.label_54.setStyleSheet("background-color: rgb(240,230,230)") + self.label_54.setAlignment(QtCore.Qt.AlignCenter) + self.label_54.setObjectName("label_54") + self.widget_4 = QtWidgets.QWidget(self.scrollAreaWidgetContents_3) + self.widget_4.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget_4.setStyleSheet("background-color: #fff9f9") + self.widget_4.setObjectName("widget_4") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.widget_4) + self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.pushButton_34 = QtWidgets.QPushButton(self.widget_4) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_34.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_34.setIcon(icon1) + self.pushButton_34.setCheckable(True) + self.pushButton_34.setAutoDefault(True) + self.pushButton_34.setObjectName("pushButton_34") + self.verticalLayout_3.addWidget(self.pushButton_34) + self.widget_7 = QtWidgets.QWidget(self.widget_4) + self.widget_7.setObjectName("widget_7") + self.formLayout_3 = QtWidgets.QFormLayout(self.widget_7) + self.formLayout_3.setObjectName("formLayout_3") + self.pushButton_35 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_35.setFont(font) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_35.setIcon(icon2) + self.pushButton_35.setObjectName("pushButton_35") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_35) + self.pushButton_36 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_36.setFont(font) + self.pushButton_36.setIcon(icon2) + self.pushButton_36.setObjectName("pushButton_36") + self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_36) + self.pushButton_37 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_37.setFont(font) + self.pushButton_37.setIcon(icon2) + self.pushButton_37.setObjectName("pushButton_37") + self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_37) + self.pushButton_38 = QtWidgets.QPushButton(self.widget_7, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_38.setFont(font) + self.pushButton_38.setIcon(icon2) + self.pushButton_38.setObjectName("pushButton_38") + self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_38) + self.verticalLayout_3.addWidget(self.widget_7) + self.pushButton_39 = QtWidgets.QPushButton(self.widget_4, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_39.setFont(font) + self.pushButton_39.setObjectName("pushButton_39") + self.verticalLayout_3.addWidget(self.pushButton_39) + self.pushButton_40 = QtWidgets.QPushButton(self.widget_4) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_40.setFont(font) + self.pushButton_40.setIcon(icon1) + self.pushButton_40.setCheckable(True) + self.pushButton_40.setObjectName("pushButton_40") + self.verticalLayout_3.addWidget(self.pushButton_40) + self.widget_10 = QtWidgets.QWidget(self.widget_4) + self.widget_10.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_10.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_10.setObjectName("widget_10") + self.pushButton_41 = QtWidgets.QPushButton(self.widget_10, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_41.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_41.setFont(font) + self.pushButton_41.setIcon(icon2) + self.pushButton_41.setObjectName("pushButton_41") + self.verticalLayout_3.addWidget(self.widget_10) + self.pushButton_42 = QtWidgets.QPushButton(self.widget_4, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_42.setFont(font) + self.pushButton_42.setObjectName("pushButton_42") + self.verticalLayout_3.addWidget(self.pushButton_42) + self.pushButton_43 = QtWidgets.QPushButton(self.widget_4, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_43.setFont(font) + self.pushButton_43.setObjectName("pushButton_43") + self.verticalLayout_3.addWidget(self.pushButton_43) + self.pushButton_16 = QtWidgets.QPushButton(self.widget_4, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_16.setFont(font) + self.pushButton_16.setObjectName("pushButton_16") + self.verticalLayout_3.addWidget(self.pushButton_16) + self.label_55 = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.label_55.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_55.setFont(font) + self.label_55.setStyleSheet("background-color: rgb(240,230,230)") + self.label_55.setAlignment(QtCore.Qt.AlignCenter) + self.label_55.setObjectName("label_55") + self.textBrowser_3 = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents_3) + self.textBrowser_3.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser_3.setStyleSheet("background-color: #fff9f9") + self.textBrowser_3.setObjectName("textBrowser_3") + self.verticalScrollBar_3 = QtWidgets.QScrollBar(self.scrollAreaWidgetContents_3) + self.verticalScrollBar_3.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar_3.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar_3.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar_3.setObjectName("verticalScrollBar_3") + self.scrollArea.setWidget(self.scrollAreaWidgetContents_3) + + self.retranslateUi(SubStructure_Dialog) + self.buttonBox.accepted.connect(self.handle_save) # type: ignore# type: ignore + self.buttonBox.rejected.connect(lambda: self.show_warning(SubStructure_Dialog)) # type: ignore + #self.buttonBox.rejected.connect(SubStructure_Dialog.reject) + QtCore.QMetaObject.connectSlotsByName(SubStructure_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + def retranslateUi(self, SubStructure_Dialog): + _translate = QtCore.QCoreApplication.translate + SubStructure_Dialog.setWindowTitle(_translate("SubStructure_Dialog", "Sub-Structure Dialog")) + self.pushButton.setText(_translate("SubStructure_Dialog", "Project Details Window ")) + self.label_38.setText(_translate("SubStructure_Dialog", "Components:")) + self.comboBox_13.setItemText(0, _translate("SubStructure_Dialog", "Piers")) + self.pushButton_10.setText(_translate("SubStructure_Dialog", "+ Add Sub-Component")) + self.label_39.setText(_translate("SubStructure_Dialog", "Material Type and Grade")) + self.label_40.setText(_translate("SubStructure_Dialog", "Rate Data Source")) + self.label_41.setText(_translate("SubStructure_Dialog", "Quantity")) + self.label_42.setText(_translate("SubStructure_Dialog", "Unit")) + self.label_43.setText(_translate("SubStructure_Dialog", "Rate")) + self.label_44.setText(_translate("SubStructure_Dialog", "

m3

")) + self.label_45.setText(_translate("SubStructure_Dialog", "kg")) + self.pushButton_13.setText(_translate("SubStructure_Dialog", "+ Add Material")) + self.label_46.setText(_translate("SubStructure_Dialog", "Material Type and Grade")) + self.comboBox_16.setItemText(0, _translate("SubStructure_Dialog", "Abutment")) + self.label_47.setText(_translate("SubStructure_Dialog", "Rate")) + self.label_48.setText(_translate("SubStructure_Dialog", "kg")) + self.label_49.setText(_translate("SubStructure_Dialog", "Rate Data Source")) + self.label_50.setText(_translate("SubStructure_Dialog", "

m3

")) + self.label_51.setText(_translate("SubStructure_Dialog", "Unit")) + self.label_52.setText(_translate("SubStructure_Dialog", "Quantity")) + self.pushButton_14.setText(_translate("SubStructure_Dialog", "+ Add Material")) + self.pushButton_15.setText(_translate("SubStructure_Dialog", "+ Add Sub-Component")) + self.label_53.setText(_translate("SubStructure_Dialog", "Components:")) + self.comboBox_17.setItemText(0, _translate("SubStructure_Dialog", "Concrete")) + self.comboBox_17.setItemText(1, _translate("SubStructure_Dialog", "Steel")) + self.comboBox_18.setItemText(0, _translate("SubStructure_Dialog", "Steel")) + self.comboBox_18.setItemText(1, _translate("SubStructure_Dialog", "Concrete")) + self.pushButton_6.setText(_translate("SubStructure_Dialog", "Sub-Structure ")) + self.label_54.setText(_translate("SubStructure_Dialog", "Input Parameters")) + self.pushButton_34.setText(_translate("SubStructure_Dialog", "Structure Works Data")) + self.pushButton_35.setText(_translate("SubStructure_Dialog", "Foundation")) + self.pushButton_36.setText(_translate("SubStructure_Dialog", "Super-Structure")) + self.pushButton_37.setText(_translate("SubStructure_Dialog", "Sub-Structure")) + self.pushButton_38.setText(_translate("SubStructure_Dialog", "Miscellaneous")) + self.pushButton_39.setText(_translate("SubStructure_Dialog", "Financial Data")) + self.pushButton_40.setText(_translate("SubStructure_Dialog", "Carbon Emission Data")) + self.pushButton_41.setText(_translate("SubStructure_Dialog", "Carbon Emission Cost Data")) + self.pushButton_42.setText(_translate("SubStructure_Dialog", "Bridge and Traffic Data")) + self.pushButton_43.setText(_translate("SubStructure_Dialog", "Maintenance and Repair")) + self.pushButton_16.setText(_translate("SubStructure_Dialog", "Disposal and Recycling")) + self.label_55.setText(_translate("SubStructure_Dialog", "Output")) + self.textBrowser_3.setHtml(_translate("SubStructure_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + + def validate_data(self): + """Validate input data before saving""" + try: + # Validate all numeric fields + fields_to_validate = [ + self.lineEdit_25, self.lineEdit_26, self.lineEdit_27, + self.lineEdit_28, self.lineEdit_29, self.lineEdit_30, + self.lineEdit_31, self.lineEdit_32, self.lineEdit_33, + self.lineEdit_34, self.lineEdit_35, self.lineEdit_36 + ] + + for field in fields_to_validate: + if field.text(): # Only validate if field is not empty + float(field.text()) + + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all numeric fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return False + + data = { + "piers": { + "component": self.comboBox_13.currentText(), + "materials": [ + { + "type_grade": self.comboBox_14.currentText(), + "quantity": self.lineEdit_25.text(), + "unit": self.label_44.text(), + "rate": self.lineEdit_27.text(), + "rate_source": self.lineEdit_29.text() + }, + { + "type_grade": self.comboBox_15.currentText(), + "quantity": self.lineEdit_26.text(), + "unit": self.label_45.text(), + "rate": self.lineEdit_28.text(), + "rate_source": self.lineEdit_30.text() + } + ] + }, + "abutment": { + "component": self.comboBox_16.currentText(), + "materials": [ + { + "type_grade": self.comboBox_17.currentText(), + "quantity": self.lineEdit_33.text(), + "unit": self.label_50.text(), + "rate": self.lineEdit_34.text(), + "rate_source": self.lineEdit_32.text() + }, + { + "type_grade": self.comboBox_18.currentText(), + "quantity": self.lineEdit_31.text(), + "unit": self.label_48.text(), + "rate": self.lineEdit_35.text(), + "rate_source": self.lineEdit_36.text() + } + ] + } + } + + # Save to JSON file + try: + import json + from datetime import datetime + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"substructure_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Sub-structure data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + def handle_save(self): + """Handle the save operation and close the dialog if successful""" + if self.save_data(): # Only close if save was successful + SubStructure_Dialog.accept() + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + SubStructure_Dialog = QtWidgets.QDialog() + ui = Ui_SubStructure_Dialog() + ui.setupUi(SubStructure_Dialog) + SubStructure_Dialog.show() + sys.exit(app.exec_()) diff --git a/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_SuperStructure_Window.py b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_SuperStructure_Window.py new file mode 100644 index 0000000..d407f2d --- /dev/null +++ b/src/osbridgelcca/desktop_app/ui/project_details_windows/ProjectDetails_SuperStructure_Window.py @@ -0,0 +1,742 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'C:\Users\saans\AppData\Local\Programs\Python\Python310\Lib\site-packages\qt5_applications\Qt\bin\ProjectDetails_SuperStructure_Window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox + + + + + + + + +#from ProjectDetails_SuperStructure_Window import Ui_SuperStructure_Dialog + + +class Ui_SuperStructure_Dialog(object): + def openBridgeTrafficWindow(self): + from ProjectDetails_BridgeANDTrafficData_Window import Ui_BridgeTraffic_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_BridgeTraffic_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFoundationWindow(self): + from ProjectDetails_Foundation_Window import Ui_Foundation_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Foundation_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openCarbonEmissionWindow(self): + from ProjectDetails_CarbonEmissionData_Window import Ui_CarbonEmission_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_CarbonEmission_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openDemolitionWindow(self): + from ProjectDetails_DemolitionANDRecyclingData_Window import Ui_Demolition_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Demolition_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openFinancialWindow(self): + from ProjectDetails_FinancialData_Window import Ui_FinancialData_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_FinancialData_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMaintenanceWindow(self): + from ProjectDetails_MaintenanceANDRepairData_Window import Ui_Maintenance_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Maintenance_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openMiscellaneousWindow(self): + from ProjectDetails_Miscellaneous_Window import Ui_Miscellaneous_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_Miscellaneous_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSubStructureWindow(self): + from ProjectDetails_SubStructure_Window import Ui_SubStructure_Dialog + self.window = QtWidgets.QDialog() + self.ui = Ui_SubStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def openSuperStructureWindow(self): + self.window = QtWidgets.QDialog() + self.ui = Ui_SuperStructure_Dialog() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, SuperStructure_Dialog): + SuperStructure_Dialog.setObjectName("SuperStructure_Dialog") + SuperStructure_Dialog.resize(1440, 900) + SuperStructure_Dialog.setStyleSheet("background-color:#FAFAFA") + self.label = QtWidgets.QLabel(SuperStructure_Dialog) + self.label.setGeometry(QtCore.QRect(10, 35, 244, 691)) + font = QtGui.QFont() + font.setPointSize(10) + self.label.setFont(font) + self.label.setStyleSheet("background-color: rgb(240,230,230)") + self.label.setText("") + self.label.setObjectName("label") + self.pushButton_6 = QtWidgets.QPushButton(SuperStructure_Dialog) + self.pushButton_6.setGeometry(QtCore.QRect(350, 10, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.pushButton_6.setFont(font) + self.pushButton_6.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton_6.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton_6.setStyleSheet("background-color: rgb(240,230,230)") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/Dismiss.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_6.setIcon(icon) + self.pushButton_6.setAutoRepeat(False) + self.pushButton_6.setObjectName("pushButton_6") + self.widget_2 = QtWidgets.QWidget(SuperStructure_Dialog) + self.widget_2.setGeometry(QtCore.QRect(350, 35, 778, 624)) + self.widget_2.setStyleSheet("background-color: #fff9f9") + self.widget_2.setObjectName("widget_2") + self.label_20 = QtWidgets.QLabel(self.widget_2) + self.label_20.setGeometry(QtCore.QRect(20, 10, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_20.setFont(font) + self.label_20.setObjectName("label_20") + self.comboBox_7 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_7.setGeometry(QtCore.QRect(140, 10, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_7.setFont(font) + self.comboBox_7.setStyleSheet("background-color: #ffffff") + self.comboBox_7.setObjectName("comboBox_7") + self.comboBox_7.addItem("") + self.pushButton_7 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_7.setGeometry(QtCore.QRect(350, 10, 190, 23)) + self.pushButton_7.setStyleSheet("background-color: #ffffff") + self.pushButton_7.setObjectName("pushButton_7") + self.label_21 = QtWidgets.QLabel(self.widget_2) + self.label_21.setGeometry(QtCore.QRect(20, 70, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_21.setFont(font) + self.label_21.setAlignment(QtCore.Qt.AlignCenter) + self.label_21.setObjectName("label_21") + self.label_22 = QtWidgets.QLabel(self.widget_2) + self.label_22.setGeometry(QtCore.QRect(551, 70, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_22.setFont(font) + self.label_22.setAlignment(QtCore.Qt.AlignCenter) + self.label_22.setObjectName("label_22") + self.label_23 = QtWidgets.QLabel(self.widget_2) + self.label_23.setGeometry(QtCore.QRect(191, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_23.setFont(font) + self.label_23.setAlignment(QtCore.Qt.AlignCenter) + self.label_23.setObjectName("label_23") + self.label_24 = QtWidgets.QLabel(self.widget_2) + self.label_24.setGeometry(QtCore.QRect(311, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_24.setFont(font) + self.label_24.setAlignment(QtCore.Qt.AlignCenter) + self.label_24.setObjectName("label_24") + self.label_25 = QtWidgets.QLabel(self.widget_2) + self.label_25.setGeometry(QtCore.QRect(431, 70, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_25.setFont(font) + self.label_25.setAlignment(QtCore.Qt.AlignCenter) + self.label_25.setObjectName("label_25") + self.comboBox_8 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_8.setGeometry(QtCore.QRect(30, 100, 140, 22)) + self.comboBox_8.setStyleSheet("background-color: #ffffff") + self.comboBox_8.setObjectName("comboBox_8") + self.comboBox_9 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_9.setGeometry(QtCore.QRect(30, 130, 140, 22)) + self.comboBox_9.setStyleSheet("background-color: #ffffff") + self.comboBox_9.setObjectName("comboBox_9") + self.lineEdit_13 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_13.setGeometry(QtCore.QRect(210, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_13.setFont(font) + self.lineEdit_13.setStyleSheet("background-color: #ffffff") + self.lineEdit_13.setObjectName("lineEdit_13") + self.lineEdit_14 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_14.setGeometry(QtCore.QRect(210, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_14.setFont(font) + self.lineEdit_14.setStyleSheet("background-color: #ffffff") + self.lineEdit_14.setObjectName("lineEdit_14") + self.label_26 = QtWidgets.QLabel(self.widget_2) + self.label_26.setGeometry(QtCore.QRect(340, 100, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_26.setFont(font) + self.label_26.setStyleSheet("background-color: #ffffff") + self.label_26.setAlignment(QtCore.Qt.AlignCenter) + self.label_26.setObjectName("label_26") + self.label_27 = QtWidgets.QLabel(self.widget_2) + self.label_27.setGeometry(QtCore.QRect(340, 130, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_27.setFont(font) + self.label_27.setStyleSheet("background-color: #ffffff") + self.label_27.setAlignment(QtCore.Qt.AlignCenter) + self.label_27.setObjectName("label_27") + self.lineEdit_15 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_15.setGeometry(QtCore.QRect(450, 100, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_15.setFont(font) + self.lineEdit_15.setStyleSheet("background-color: #ffffff") + self.lineEdit_15.setObjectName("lineEdit_15") + self.lineEdit_16 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_16.setGeometry(QtCore.QRect(450, 130, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_16.setFont(font) + self.lineEdit_16.setStyleSheet("background-color: #ffffff") + self.lineEdit_16.setObjectName("lineEdit_16") + self.lineEdit_17 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_17.setGeometry(QtCore.QRect(570, 100, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_17.setFont(font) + self.lineEdit_17.setStyleSheet("background-color: #ffffff") + self.lineEdit_17.setObjectName("lineEdit_17") + self.lineEdit_18 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_18.setGeometry(QtCore.QRect(570, 130, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_18.setFont(font) + self.lineEdit_18.setStyleSheet("background-color: #ffffff") + self.lineEdit_18.setObjectName("lineEdit_18") + self.pushButton_8 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_8.setGeometry(QtCore.QRect(300, 200, 190, 23)) + self.pushButton_8.setStyleSheet("background-color: #ffffff") + self.pushButton_8.setObjectName("pushButton_8") + self.label_28 = QtWidgets.QLabel(self.widget_2) + self.label_28.setGeometry(QtCore.QRect(30, 330, 161, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_28.setFont(font) + self.label_28.setAlignment(QtCore.Qt.AlignCenter) + self.label_28.setObjectName("label_28") + self.comboBox_10 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_10.setGeometry(QtCore.QRect(150, 270, 190, 22)) + font = QtGui.QFont() + font.setPointSize(10) + self.comboBox_10.setFont(font) + self.comboBox_10.setStyleSheet("background-color: #ffffff") + self.comboBox_10.setObjectName("comboBox_10") + self.comboBox_10.addItem("") + self.label_29 = QtWidgets.QLabel(self.widget_2) + self.label_29.setGeometry(QtCore.QRect(441, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_29.setFont(font) + self.label_29.setAlignment(QtCore.Qt.AlignCenter) + self.label_29.setObjectName("label_29") + self.label_30 = QtWidgets.QLabel(self.widget_2) + self.label_30.setGeometry(QtCore.QRect(350, 390, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_30.setFont(font) + self.label_30.setStyleSheet("background-color: #ffffff") + self.label_30.setAlignment(QtCore.Qt.AlignCenter) + self.label_30.setObjectName("label_30") + self.label_31 = QtWidgets.QLabel(self.widget_2) + self.label_31.setGeometry(QtCore.QRect(561, 330, 140, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_31.setFont(font) + self.label_31.setAlignment(QtCore.Qt.AlignCenter) + self.label_31.setObjectName("label_31") + self.label_32 = QtWidgets.QLabel(self.widget_2) + self.label_32.setGeometry(QtCore.QRect(350, 360, 51, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_32.setFont(font) + self.label_32.setStyleSheet("background-color: #ffffff") + self.label_32.setAlignment(QtCore.Qt.AlignCenter) + self.label_32.setObjectName("label_32") + self.label_33 = QtWidgets.QLabel(self.widget_2) + self.label_33.setGeometry(QtCore.QRect(321, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_33.setFont(font) + self.label_33.setAlignment(QtCore.Qt.AlignCenter) + self.label_33.setObjectName("label_33") + self.lineEdit_19 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_19.setGeometry(QtCore.QRect(220, 390, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_19.setFont(font) + self.lineEdit_19.setStyleSheet("background-color: #ffffff") + self.lineEdit_19.setObjectName("lineEdit_19") + self.lineEdit_20 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_20.setGeometry(QtCore.QRect(580, 360, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_20.setFont(font) + self.lineEdit_20.setStyleSheet("background-color: #ffffff") + self.lineEdit_20.setObjectName("lineEdit_20") + self.label_34 = QtWidgets.QLabel(self.widget_2) + self.label_34.setGeometry(QtCore.QRect(201, 330, 110, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_34.setFont(font) + self.label_34.setAlignment(QtCore.Qt.AlignCenter) + self.label_34.setObjectName("label_34") + self.pushButton_9 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_9.setGeometry(QtCore.QRect(310, 460, 190, 23)) + self.pushButton_9.setStyleSheet("background-color: #ffffff") + self.pushButton_9.setObjectName("pushButton_9") + self.lineEdit_21 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_21.setGeometry(QtCore.QRect(220, 360, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_21.setFont(font) + self.lineEdit_21.setStyleSheet("background-color: #ffffff") + self.lineEdit_21.setObjectName("lineEdit_21") + self.lineEdit_22 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_22.setGeometry(QtCore.QRect(460, 360, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_22.setFont(font) + self.lineEdit_22.setStyleSheet("background-color: #ffffff") + self.lineEdit_22.setObjectName("lineEdit_22") + self.lineEdit_23 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_23.setGeometry(QtCore.QRect(460, 390, 81, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_23.setFont(font) + self.lineEdit_23.setStyleSheet("background-color: #ffffff") + self.lineEdit_23.setObjectName("lineEdit_23") + self.pushButton_11 = QtWidgets.QPushButton(self.widget_2) + self.pushButton_11.setGeometry(QtCore.QRect(360, 270, 190, 23)) + self.pushButton_11.setStyleSheet("background-color: #ffffff") + self.pushButton_11.setObjectName("pushButton_11") + self.label_35 = QtWidgets.QLabel(self.widget_2) + self.label_35.setGeometry(QtCore.QRect(30, 270, 91, 21)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_35.setFont(font) + self.label_35.setObjectName("label_35") + self.lineEdit_24 = QtWidgets.QLineEdit(self.widget_2) + self.lineEdit_24.setGeometry(QtCore.QRect(580, 390, 101, 20)) + font = QtGui.QFont() + font.setPointSize(10) + self.lineEdit_24.setFont(font) + self.lineEdit_24.setStyleSheet("background-color: #ffffff") + self.lineEdit_24.setObjectName("lineEdit_24") + self.comboBox_11 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_11.setGeometry(QtCore.QRect(40, 360, 140, 22)) + self.comboBox_11.setStyleSheet("background-color: #ffffff") + self.comboBox_11.setObjectName("comboBox_11") + self.comboBox_11.addItem("") + self.comboBox_11.addItem("") + self.comboBox_12 = QtWidgets.QComboBox(self.widget_2) + self.comboBox_12.setGeometry(QtCore.QRect(40, 390, 140, 22)) + self.comboBox_12.setStyleSheet("background-color: #ffffff") + self.comboBox_12.setObjectName("comboBox_12") + self.comboBox_12.addItem("") + self.comboBox_12.addItem("") + self.line_3 = QtWidgets.QFrame(self.widget_2) + self.line_3.setGeometry(QtCore.QRect(10, 250, 761, 16)) + self.line_3.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_3.setLineWidth(2) + self.line_3.setMidLineWidth(2) + self.line_3.setFrameShape(QtWidgets.QFrame.HLine) + self.line_3.setObjectName("line_3") + self.line_4 = QtWidgets.QFrame(self.widget_2) + self.line_4.setGeometry(QtCore.QRect(10, 500, 761, 16)) + self.line_4.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) + self.line_4.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_4.setLineWidth(2) + self.line_4.setMidLineWidth(2) + self.line_4.setFrameShape(QtWidgets.QFrame.HLine) + self.line_4.setObjectName("line_4") + self.buttonBox = QtWidgets.QDialogButtonBox(self.widget_2) + self.buttonBox.setGeometry(QtCore.QRect(430, 580, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Save) + self.buttonBox.setObjectName("buttonBox") + self.scrollArea = QtWidgets.QScrollArea(SuperStructure_Dialog) + self.scrollArea.setGeometry(QtCore.QRect(10, 40, 241, 681)) + self.scrollArea.setAutoFillBackground(False) + self.scrollArea.setStyleSheet("background-color: #fff9f9") + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents_2 = QtWidgets.QWidget() + self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 239, 679)) + self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2") + self.label_36 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_36.setGeometry(QtCore.QRect(0, 0, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_36.setFont(font) + self.label_36.setStyleSheet("background-color: rgb(240,230,230)") + self.label_36.setAlignment(QtCore.Qt.AlignCenter) + self.label_36.setObjectName("label_36") + self.widget_3 = QtWidgets.QWidget(self.scrollAreaWidgetContents_2) + self.widget_3.setGeometry(QtCore.QRect(0, 30, 221, 357)) + self.widget_3.setStyleSheet("background-color: #fff9f9") + self.widget_3.setObjectName("widget_3") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.widget_3) + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.pushButton_24 = QtWidgets.QPushButton(self.widget_3) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_24.setFont(font) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon1.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled (1).png"), QtGui.QIcon.Normal, QtGui.QIcon.On) + self.pushButton_24.setIcon(icon1) + self.pushButton_24.setCheckable(True) + self.pushButton_24.setAutoDefault(True) + self.pushButton_24.setObjectName("pushButton_24") + self.verticalLayout_2.addWidget(self.pushButton_24) + self.widget_6 = QtWidgets.QWidget(self.widget_3) + self.widget_6.setObjectName("widget_6") + self.formLayout_2 = QtWidgets.QFormLayout(self.widget_6) + self.formLayout_2.setObjectName("formLayout_2") + self.pushButton_25 = QtWidgets.QPushButton(self.widget_6, clicked=lambda: self.openFoundationWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_25.setFont(font) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("C:\\Users\\saans\\AppData\\Local\\Programs\\Python\\Python310\\Lib\\site-packages\\qt5_applications\\Qt\\bin\\../../../../../../../../../../Downloads/play_arrow_filled.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.pushButton_25.setIcon(icon2) + self.pushButton_25.setObjectName("pushButton_25") + self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_25) + self.pushButton_26 = QtWidgets.QPushButton(self.widget_6, clicked=lambda: self.openSuperStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_26.setFont(font) + self.pushButton_26.setIcon(icon2) + self.pushButton_26.setObjectName("pushButton_26") + self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_26) + self.pushButton_27 = QtWidgets.QPushButton(self.widget_6, clicked=lambda: self.openSubStructureWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_27.setFont(font) + self.pushButton_27.setIcon(icon2) + self.pushButton_27.setObjectName("pushButton_27") + self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.pushButton_27) + self.pushButton_28 = QtWidgets.QPushButton(self.widget_6, clicked=lambda: self.openMiscellaneousWindow()) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_28.setFont(font) + self.pushButton_28.setIcon(icon2) + self.pushButton_28.setObjectName("pushButton_28") + self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.pushButton_28) + self.verticalLayout_2.addWidget(self.widget_6) + self.pushButton_29 = QtWidgets.QPushButton(self.widget_3, clicked=lambda: self.openFinancialWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_29.setFont(font) + self.pushButton_29.setObjectName("pushButton_29") + self.verticalLayout_2.addWidget(self.pushButton_29) + self.pushButton_30 = QtWidgets.QPushButton(self.widget_3) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_30.setFont(font) + self.pushButton_30.setIcon(icon1) + self.pushButton_30.setCheckable(True) + self.pushButton_30.setObjectName("pushButton_30") + self.verticalLayout_2.addWidget(self.pushButton_30) + self.widget_9 = QtWidgets.QWidget(self.widget_3) + self.widget_9.setMinimumSize(QtCore.QSize(203, 21)) + self.widget_9.setMaximumSize(QtCore.QSize(281, 21)) + self.widget_9.setObjectName("widget_9") + self.pushButton_31 = QtWidgets.QPushButton(self.widget_9, clicked=lambda: self.openCarbonEmissionWindow()) + self.pushButton_31.setGeometry(QtCore.QRect(20, 0, 183, 21)) + font = QtGui.QFont() + font.setPointSize(10) + font.setBold(False) + font.setWeight(50) + self.pushButton_31.setFont(font) + self.pushButton_31.setIcon(icon2) + self.pushButton_31.setObjectName("pushButton_31") + self.verticalLayout_2.addWidget(self.widget_9) + self.pushButton_32 = QtWidgets.QPushButton(self.widget_3, clicked=lambda: self.openBridgeTrafficWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_32.setFont(font) + self.pushButton_32.setObjectName("pushButton_32") + self.verticalLayout_2.addWidget(self.pushButton_32) + self.pushButton_33 = QtWidgets.QPushButton(self.widget_3, clicked=lambda: self.openMaintenanceWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_33.setFont(font) + self.pushButton_33.setObjectName("pushButton_33") + self.verticalLayout_2.addWidget(self.pushButton_33) + self.pushButton_12 = QtWidgets.QPushButton(self.widget_3, clicked=lambda: self.openDemolitionWindow()) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton_12.setFont(font) + self.pushButton_12.setObjectName("pushButton_12") + self.verticalLayout_2.addWidget(self.pushButton_12) + self.label_37 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_37.setGeometry(QtCore.QRect(0, 387, 221, 31)) + font = QtGui.QFont() + font.setPointSize(10) + self.label_37.setFont(font) + self.label_37.setStyleSheet("background-color: rgb(240,230,230)") + self.label_37.setAlignment(QtCore.Qt.AlignCenter) + self.label_37.setObjectName("label_37") + self.textBrowser_2 = QtWidgets.QTextBrowser(self.scrollAreaWidgetContents_2) + self.textBrowser_2.setGeometry(QtCore.QRect(0, 418, 221, 221)) + self.textBrowser_2.setStyleSheet("background-color: #fff9f9") + self.textBrowser_2.setObjectName("textBrowser_2") + self.verticalScrollBar_2 = QtWidgets.QScrollBar(self.scrollAreaWidgetContents_2) + self.verticalScrollBar_2.setGeometry(QtCore.QRect(220, 0, 16, 641)) + self.verticalScrollBar_2.setStyleSheet("background-color: #F0F0F0") + self.verticalScrollBar_2.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar_2.setObjectName("verticalScrollBar_2") + self.scrollArea.setWidget(self.scrollAreaWidgetContents_2) + self.pushButton = QtWidgets.QPushButton(SuperStructure_Dialog) + self.pushButton.setGeometry(QtCore.QRect(10, 10, 188, 25)) + font = QtGui.QFont() + font.setPointSize(10) + self.pushButton.setFont(font) + self.pushButton.setFocusPolicy(QtCore.Qt.StrongFocus) + self.pushButton.setLayoutDirection(QtCore.Qt.RightToLeft) + self.pushButton.setStyleSheet("background-color: rgb(240,230,230)") + self.pushButton.setIcon(icon) + self.pushButton.setAutoRepeat(False) + self.pushButton.setObjectName("pushButton") + + self.retranslateUi(SuperStructure_Dialog) + self.buttonBox.accepted.connect(self.handle_save) # type: ignore + self.buttonBox.rejected.connect(lambda: self.show_warning(SuperStructure_Dialog)) # type: ignore + QtCore.QMetaObject.connectSlotsByName(SuperStructure_Dialog) + + def show_warning(self, dialog): + """ + Show a warning window when the Close button is pressed. + """ + warning_box = QMessageBox() + warning_box.setIcon(QMessageBox.Warning) + warning_box.setWindowTitle("Confirm Close") + warning_box.setText("Are you sure you want to close without saving?") + warning_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + warning_box.setDefaultButton(QMessageBox.No) + + # Check the user's response + response = warning_box.exec_() + if response == QMessageBox.Yes: + dialog.reject() # Close the application + else: + pass # Do nothing, return to the dialog + + def retranslateUi(self, SuperStructure_Dialog): + _translate = QtCore.QCoreApplication.translate + SuperStructure_Dialog.setWindowTitle(_translate("SuperStructure_Dialog", "Dialog")) + self.pushButton_6.setText(_translate("SuperStructure_Dialog", "Super-Structure ")) + self.label_20.setText(_translate("SuperStructure_Dialog", "Components:")) + self.comboBox_7.setItemText(0, _translate("SuperStructure_Dialog", "Deck")) + self.pushButton_7.setText(_translate("SuperStructure_Dialog", "+ Add Sub-Component")) + self.label_21.setText(_translate("SuperStructure_Dialog", "Material Type and Grade")) + self.label_22.setText(_translate("SuperStructure_Dialog", "Rate Data Source")) + self.label_23.setText(_translate("SuperStructure_Dialog", "Quantity")) + self.label_24.setText(_translate("SuperStructure_Dialog", "Unit")) + self.label_25.setText(_translate("SuperStructure_Dialog", "Rate")) + self.label_26.setText(_translate("SuperStructure_Dialog", "

m3

")) + self.label_27.setText(_translate("SuperStructure_Dialog", "kg")) + self.pushButton_8.setText(_translate("SuperStructure_Dialog", "+ Add Material")) + self.label_28.setText(_translate("SuperStructure_Dialog", "Material Type and Grade")) + self.comboBox_10.setItemText(0, _translate("SuperStructure_Dialog", "Cables")) + self.label_29.setText(_translate("SuperStructure_Dialog", "Rate")) + self.label_30.setText(_translate("SuperStructure_Dialog", "kg")) + self.label_31.setText(_translate("SuperStructure_Dialog", "Rate Data Source")) + self.label_32.setText(_translate("SuperStructure_Dialog", "

m3

")) + self.label_33.setText(_translate("SuperStructure_Dialog", "Unit")) + self.label_34.setText(_translate("SuperStructure_Dialog", "Quantity")) + self.pushButton_9.setText(_translate("SuperStructure_Dialog", "+ Add Material")) + self.pushButton_11.setText(_translate("SuperStructure_Dialog", "+ Add Sub-Component")) + self.label_35.setText(_translate("SuperStructure_Dialog", "Components:")) + self.comboBox_11.setItemText(0, _translate("SuperStructure_Dialog", "Concrete")) + self.comboBox_11.setItemText(1, _translate("SuperStructure_Dialog", "Steel")) + self.comboBox_12.setItemText(0, _translate("SuperStructure_Dialog", "Steel")) + self.comboBox_12.setItemText(1, _translate("SuperStructure_Dialog", "Concrete")) + self.label_36.setText(_translate("SuperStructure_Dialog", "Input Parameters")) + self.pushButton_24.setText(_translate("SuperStructure_Dialog", "Structure Works Data")) + self.pushButton_25.setText(_translate("SuperStructure_Dialog", "Foundation")) + self.pushButton_26.setText(_translate("SuperStructure_Dialog", "Super-Structure")) + self.pushButton_27.setText(_translate("SuperStructure_Dialog", "Sub-Structure")) + self.pushButton_28.setText(_translate("SuperStructure_Dialog", "Miscellaneous")) + self.pushButton_29.setText(_translate("SuperStructure_Dialog", "Financial Data")) + self.pushButton_30.setText(_translate("SuperStructure_Dialog", "Carbon Emission Data")) + self.pushButton_31.setText(_translate("SuperStructure_Dialog", "Carbon Emission Cost Data")) + self.pushButton_32.setText(_translate("SuperStructure_Dialog", "Bridge and Traffic Data")) + self.pushButton_33.setText(_translate("SuperStructure_Dialog", "Maintenance and Repair")) + self.pushButton_12.setText(_translate("SuperStructure_Dialog", "Disposal and Recycling")) + self.label_37.setText(_translate("SuperStructure_Dialog", "Output")) + self.textBrowser_2.setHtml(_translate("SuperStructure_Dialog", "\n" +"\n" +"

Initial Construction Cost

\n" +"

Initial Carbon emission Cost

\n" +"

Time Cost

\n" +"

Road User Cost

\n" +"

Carbon Emission due to Re-Routing

\n" +"

Periodic Maintenance Costs

\n" +"

Maintenance Emission Costs

\n" +"

Routine Inspectection Costs

\n" +"

Repair & Rehabilitation Costs

\n" +"

Reconstruction Costs

\n" +"

Demolition & Disposal Cost

\n" +"

Recycling Cost

\n" +"

Total Life-Cycle Cost

")) + self.pushButton.setText(_translate("SuperStructure_Dialog", "Project Details Window ")) + + def validate_data(self): + """Validate input data before saving""" + try: + # Validate all numeric fields + fields_to_validate = [ + self.lineEdit_13, self.lineEdit_14, self.lineEdit_15, + self.lineEdit_16, self.lineEdit_17, self.lineEdit_18, + self.lineEdit_19, self.lineEdit_20, self.lineEdit_21, + self.lineEdit_22, self.lineEdit_23, self.lineEdit_24 + ] + + for field in fields_to_validate: + if field.text(): # Only validate if field is not empty + float(field.text()) + + return True + + except ValueError: + QMessageBox.warning( + None, + "Validation Error", + "Please enter valid numbers in all numeric fields", + QMessageBox.Ok + ) + return False + + def save_data(self): + """Collect all input data and save it to a JSON file""" + if not self.validate_data(): + return False + + data = { + "deck": { + "component": self.comboBox_7.currentText(), + "materials": [ + { + "type_grade": self.comboBox_8.currentText(), + "quantity": self.lineEdit_13.text(), + "unit": self.label_26.text(), + "rate": self.lineEdit_15.text(), + "rate_source": self.lineEdit_17.text() + }, + { + "type_grade": self.comboBox_9.currentText(), + "quantity": self.lineEdit_14.text(), + "unit": self.label_27.text(), + "rate": self.lineEdit_16.text(), + "rate_source": self.lineEdit_18.text() + } + ] + }, + "cables": { + "component": self.comboBox_10.currentText(), + "materials": [ + { + "type_grade": self.comboBox_11.currentText(), + "quantity": self.lineEdit_21.text(), + "unit": self.label_32.text(), + "rate": self.lineEdit_22.text(), + "rate_source": self.lineEdit_20.text() + }, + { + "type_grade": self.comboBox_12.currentText(), + "quantity": self.lineEdit_19.text(), + "unit": self.label_30.text(), + "rate": self.lineEdit_23.text(), + "rate_source": self.lineEdit_24.text() + } + ] + } + } + + # Save to JSON file + try: + import json + from datetime import datetime + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"superstructure_data_{timestamp}.json" + + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + + # Show success message + QMessageBox.information( + None, + "Success", + f"Super-structure data saved successfully to {filename}", + QMessageBox.Ok + ) + + return True + + except Exception as e: + QMessageBox.critical( + None, + "Error", + f"Failed to save data: {str(e)}", + QMessageBox.Ok + ) + return False + + def handle_save(self): + """Handle the save operation and close the dialog if successful""" + if self.save_data(): # Only close if save was successful + SuperStructure_Dialog.accept() + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + SuperStructure_Dialog = QtWidgets.QDialog() + ui = Ui_SuperStructure_Dialog() + ui.setupUi(SuperStructure_Dialog) + SuperStructure_Dialog.show() + sys.exit(app.exec_())