From b083de2a638d83e8e969cb301c9fda8c7981f21b Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Mon, 30 Jun 2025 16:32:31 +0530 Subject: [PATCH 01/22] Initial Commit 1. Contains a Custom Title Bar Contains a Menubar and the buttons options and Tab Buttons and resources --- __pycache__/Home.py | 178 ++++ __pycache__/ProjectDetails_ | 0 ...ridgeANDTrafficData_Window.cpython-312.pyc | Bin 0 -> 47171 bytes ...ridgeANDTrafficData_Window.cpython-313.pyc | Bin 0 -> 47780 bytes ...jectDetails_BridgeANDTrafficData_Window.py | 486 +++++++++++ ..._CarbonEmissionData_Window.cpython-312.pyc | Bin 0 -> 74484 bytes ..._CarbonEmissionData_Window.cpython-313.pyc | Bin 0 -> 75474 bytes ...rojectDetails_CarbonEmissionData_Window.py | 813 ++++++++++++++++++ ...ionANDRecyclingData_Window.cpython-312.pyc | Bin 0 -> 33667 bytes ...ionANDRecyclingData_Window.cpython-313.pyc | Bin 0 -> 34068 bytes ...tails_DemolitionANDRecyclingData_Window.py | 351 ++++++++ ...tails_FinancialData_Window.cpython-312.pyc | Bin 0 -> 36818 bytes ...tails_FinancialData_Window.cpython-313.pyc | Bin 0 -> 37272 bytes .../ProjectDetails_FinancialData_Window.py | 382 ++++++++ ...tDetails_Foundation_Window.cpython-312.pyc | Bin 0 -> 54201 bytes ...tDetails_Foundation_Window.cpython-313.pyc | Bin 0 -> 54909 bytes .../ProjectDetails_Foundation_Window.py | 597 +++++++++++++ ...enanceANDRepairData_Window.cpython-312.pyc | Bin 0 -> 41090 bytes ...enanceANDRepairData_Window.cpython-313.pyc | Bin 0 -> 41586 bytes ...Details_MaintenanceANDRepairData_Window.py | 420 +++++++++ ...tails_Miscellaneous_Window.cpython-312.pyc | Bin 0 -> 52237 bytes ...tails_Miscellaneous_Window.cpython-313.pyc | Bin 0 -> 52961 bytes .../ProjectDetails_Miscellaneous_Window.py | 566 ++++++++++++ ...etails_SubStructure_Window.cpython-312.pyc | Bin 0 -> 50769 bytes ...etails_SubStructure_Window.cpython-313.pyc | Bin 0 -> 51460 bytes .../ProjectDetails_SubStructure_Window.py | 546 ++++++++++++ ...ails_SuperStructure_Window.cpython-312.pyc | Bin 0 -> 52197 bytes ...ails_SuperStructure_Window.cpython-313.pyc | Bin 0 -> 52921 bytes .../ProjectDetails_SuperStructure_Window.py | 566 ++++++++++++ __pycache__/README.md | 1 + __pycache__/Warning_Window.cpython-312.pyc | Bin 0 -> 3407 bytes __pycache__/Warning_Window.cpython-313.pyc | Bin 0 -> 3480 bytes __pycache__/app.cpython-312.pyc | Bin 0 -> 1369 bytes __pycache__/form_data_storage.cpython-312.pyc | Bin 0 -> 889 bytes __pycache__/form_data_storage.cpython-313.pyc | Bin 0 -> 847 bytes __pycache__/form_data_storage.py | 24 + __pycache__/main_template.cpython-311.pyc | Bin 0 -> 16847 bytes __pycache__/main_template.cpython-312.pyc | Bin 0 -> 19956 bytes __pycache__/title_bar.cpython-311.pyc | Bin 0 -> 9285 bytes __pycache__/title_bar.cpython-312.pyc | Bin 0 -> 8565 bytes .../tutorial_widget_left.cpython-312.pyc | Bin 0 -> 11217 bytes app.py | 21 + main_template.py | 386 +++++++++ resources/AlataRegular.ttf | Bin 0 -> 348308 bytes resources/Group 4.svg | 80 ++ resources/arrow_down.png | Bin 0 -> 3992 bytes resources/arrow_up.png | Bin 0 -> 93 bytes resources/close.png | Bin 0 -> 6536 bytes resources/contact.svg | 6 + resources/country_arrow.png | Bin 0 -> 216 bytes resources/create_copy.svg | 4 + resources/edit_button.png | Bin 0 -> 11726 bytes resources/export.svg | 11 + resources/feedback.svg | 3 + resources/file_button.png | Bin 0 -> 1402 bytes resources/info.svg | 3 + resources/join_community.svg | 8 + resources/new.svg | 3 + resources/open.svg | 4 + resources/play-button-arrowhead.png | Bin 0 -> 6074 bytes resources/print.svg | 3 + resources/rename.svg | 4 + resources/save.svg | 3 + resources/save_as.svg | 3 + resources/save_button.png | Bin 0 -> 8849 bytes resources/tab_close.png | Bin 0 -> 9732 bytes resources/version_history.svg | 3 + resources/video_tutorial.svg | 4 + resources/window_close.svg | 1 + resources/window_maximize.svg | 4 + resources/window_minimize.svg | 1 + resources/window_restore.svg | 3 + .../maintenance_repair_data.cpython-312.pyc | Bin 0 -> 19943 bytes ...oject_details_right_widget.cpython-312.pyc | Bin 0 -> 24878 bytes widgets/__pycache__/title_bar.cpython-312.pyc | Bin 0 -> 8573 bytes .../tutorial_widget_left.cpython-312.pyc | Bin 0 -> 11839 bytes widgets/title_bar.py | 135 +++ 77 files changed, 5623 insertions(+) create mode 100644 __pycache__/Home.py create mode 100644 __pycache__/ProjectDetails_ create mode 100644 __pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_BridgeANDTrafficData_Window.py create mode 100644 __pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_CarbonEmissionData_Window.py create mode 100644 __pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.py create mode 100644 __pycache__/ProjectDetails_FinancialData_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_FinancialData_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_FinancialData_Window.py create mode 100644 __pycache__/ProjectDetails_Foundation_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_Foundation_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_Foundation_Window.py create mode 100644 __pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_MaintenanceANDRepairData_Window.py create mode 100644 __pycache__/ProjectDetails_Miscellaneous_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_Miscellaneous_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_Miscellaneous_Window.py create mode 100644 __pycache__/ProjectDetails_SubStructure_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_SubStructure_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_SubStructure_Window.py create mode 100644 __pycache__/ProjectDetails_SuperStructure_Window.cpython-312.pyc create mode 100644 __pycache__/ProjectDetails_SuperStructure_Window.cpython-313.pyc create mode 100644 __pycache__/ProjectDetails_SuperStructure_Window.py create mode 100644 __pycache__/README.md create mode 100644 __pycache__/Warning_Window.cpython-312.pyc create mode 100644 __pycache__/Warning_Window.cpython-313.pyc create mode 100644 __pycache__/app.cpython-312.pyc create mode 100644 __pycache__/form_data_storage.cpython-312.pyc create mode 100644 __pycache__/form_data_storage.cpython-313.pyc create mode 100644 __pycache__/form_data_storage.py create mode 100644 __pycache__/main_template.cpython-311.pyc create mode 100644 __pycache__/main_template.cpython-312.pyc create mode 100644 __pycache__/title_bar.cpython-311.pyc create mode 100644 __pycache__/title_bar.cpython-312.pyc create mode 100644 __pycache__/tutorial_widget_left.cpython-312.pyc create mode 100644 app.py create mode 100644 main_template.py create mode 100644 resources/AlataRegular.ttf create mode 100644 resources/Group 4.svg create mode 100644 resources/arrow_down.png create mode 100644 resources/arrow_up.png create mode 100644 resources/close.png create mode 100644 resources/contact.svg create mode 100644 resources/country_arrow.png create mode 100644 resources/create_copy.svg create mode 100644 resources/edit_button.png create mode 100644 resources/export.svg create mode 100644 resources/feedback.svg create mode 100644 resources/file_button.png create mode 100644 resources/info.svg create mode 100644 resources/join_community.svg create mode 100644 resources/new.svg create mode 100644 resources/open.svg create mode 100644 resources/play-button-arrowhead.png create mode 100644 resources/print.svg create mode 100644 resources/rename.svg create mode 100644 resources/save.svg create mode 100644 resources/save_as.svg create mode 100644 resources/save_button.png create mode 100644 resources/tab_close.png create mode 100644 resources/version_history.svg create mode 100644 resources/video_tutorial.svg create mode 100644 resources/window_close.svg create mode 100644 resources/window_maximize.svg create mode 100644 resources/window_minimize.svg create mode 100644 resources/window_restore.svg create mode 100644 widgets/__pycache__/maintenance_repair_data.cpython-312.pyc create mode 100644 widgets/__pycache__/project_details_right_widget.cpython-312.pyc create mode 100644 widgets/__pycache__/title_bar.cpython-312.pyc create mode 100644 widgets/__pycache__/tutorial_widget_left.cpython-312.pyc create mode 100644 widgets/title_bar.py diff --git a/__pycache__/Home.py b/__pycache__/Home.py new file mode 100644 index 0000000..6d5404f --- /dev/null +++ b/__pycache__/Home.py @@ -0,0 +1,178 @@ +import sys +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, + QHBoxLayout, QPushButton, QLabel, QFrame, QSplitter, + QToolBar, QAction, QGroupBox, QMenu, QLineEdit, + QComboBox, QSizePolicy, QMessageBox, QTextEdit, QScrollArea, + QTabWidget, QStackedWidget) +from PyQt5.QtGui import QIcon, QFont +from PyQt5.QtCore import Qt, QSize + + +class MainWindow(QMainWindow): # Renamed to MainWindow, inheriting QMainWindow + def __init__(self): + super().__init__() + self.setWindowTitle(" - BICCA Studio 1.0.0") + self.setGeometry(100, 100, 1440, 1024) + self.setStyleSheet("background-color: rgb(255, 255, 255);") + + # 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. + """ + } + ] + + 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) + + # --- CORRECTED ORDER: Create UI elements BEFORE retranslateUi --- + self.create_menu_bar() + self.create_toolbar() + self.create_window_tabs() # Custom tab buttons that control splitter visibility + self.create_content_area() # Contains the QSplitter for panels + self.create_status_bar() # Includes the persistent "Data" section + + self.retranslateUi(self) # Call retranslateUi *after* all widgets are created + + self.update_tutorial_content() + + # Set initial visible panels and button states for the custom tabs + self.handle_tab_click("Tutorials", initial_load=True) + self.handle_tab_click("Project Details", initial_load=True) + + # Connect QGroupBox toggled signals + self.generalInfoGroup.toggled['bool'].connect(lambda checked: self.toggle_general_info_group_content(self.generalInfoGroup, checked)) + self.inputParamsGroup.toggled['bool'].connect(lambda checked: self.toggle_input_params_group_content(self.inputParamsGroup, checked)) + self.outputsGroup.toggled['bool'].connect(lambda checked: self.toggle_outputs_group_content(self.outputsGroup, checked)) + + # Connect internal buttons to hide/show their respective sub-content + self.pushButton.clicked.connect(lambda: self.toggle_sub_buttons_visibility(self.gridLayout_3, self.pushButton)) + self.pushButton_7.clicked.connect(lambda: self.toggle_sub_buttons_visibility(self.gridLayout_4, self.pushButton_7)) + + # Initially hide the collapsible sub-sections' content + self.gridLayout_3_widget.setVisible(False) # Hide the widget holding gridLayout_3 + self.gridLayout_4_widget.setVisible(False) # Hide the widget holding gridLayout_4 + + # Manually trigger initial state for General Info, Input Params, Outputs groups + self.toggle_general_info_group_content(self.generalInfoGroup, self.generalInfoGroup.isChecked()) + self.toggle_input_params_group_content(self.inputParamsGroup, self.inputParamsGroup.isChecked()) + self.toggle_outputs_group_content(self.outputsGroup, self.outputsGroup.isChecked()) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", " - BICCA Studio 1.0.0")) + + # Custom Tab button texts + self.tab_buttons["Tutorials"].setText(_translate("MainWindow", "Tutorials")) + self.tab_buttons["Project Details"].setText(_translate("MainWindow", "Project Details")) + self.tab_buttons["Results"].setText(_translate("MainWindow", "Results")) + self.tab_buttons["Compare"].setText(_translate("MainWindow", "Compare")) + + # Headers for panels + self.tutorials_header_label.setText(_translate("MainWindow", "Tutorials")) + self.project_header_label.setText(_translate("MainWindow", "Project Details Window")) + self.results_header_label.setText(_translate("MainWindow", "Results")) + self.compare_header_label.setText(_translate("MainWindow", "Compare")) + + # Group Box Titles + self.generalInfoGroup.setTitle(_translate("MainWindow", "General Information")) + self.inputParamsGroup.setTitle(_translate("MainWindow", "Input Parameters")) + self.outputsGroup.setTitle(_translate("MainWindow", "Outputs")) + + # General Information Labels and Placeholders + self.label_company_name.setText(_translate("MainWindow", "Company Name")) + self.lineEdit_company_name.setPlaceholderText(_translate("MainWindow", "Enter Company Name")) + self.label_project_title.setText(_translate("MainWindow", "Project Title")) + self.lineEdit_project_title.setPlaceholderText(_translate("MainWindow", "Enter Project Title")) + self.label_project_description.setText(_translate("MainWindow", "Project Description")) + self.textEdit_project_description.setPlaceholderText(_translate("MainWindow", "Enter Project Description")) + self.label_valuer_name.setText(_translate("MainWindow", "Name of Valuer")) + self.lineEdit_valuer_name.setPlaceholderText(_translate("MainWindow", "Enter Valuer's Name")) + self.label_job_number.setText(_translate("MainWindow", "Job Number")) + self.lineEdit_job_number.setPlaceholderText(_translate("MainWindow", "Enter Job Number")) + self.label_client.setText(_translate("MainWindow", "Client")) + self.lineEdit_client.setPlaceholderText(_translate("MainWindow", "Enter Client Name")) + self.label_country.setText(_translate("MainWindow", "Country")) + self.label_base_year.setText(_translate("MainWindow", "Base Year")) + self.lineEdit_base_year.setPlaceholderText(_translate("MainWindow", "e.g., 2023")) + + # Input Parameters Buttons + self.pushButton.setText(_translate("MainWindow", "Structure Works Data")) + self.pushButton_3.setText(_translate("MainWindow", "Super-Structure")) + self.pushButton_2.setText(_translate("MainWindow", "Foundation")) + self.pushButton_4.setText(_translate("MainWindow", "Sub-Structure")) + self.pushButton_5.setText(_translate("MainWindow", "Miscellaneous")) + self.pushButton_6.setText(_translate("MainWindow", "Financial Data")) + self.pushButton_7.setText(_translate("MainWindow", "Carbon Emission Data")) + self.pushButton_8.setText(_translate("MainWindow", "Carbon Emission Cost Data")) + self.pushButton_9.setText(_translate("MainWindow", "Bridge and Traffic Data")) + self.pushButton_10.setText(_translate("MainWindow", "Maintenance and Repair")) + self.pushButton_11.setText(_translate("MainWindow", "Disposal and Recycling")) + + # Outputs Label + self.label_10.setText(_translate("MainWindow", "Output content goes here.")) + + # Menu and Toolbar actions + self.menuFile.setTitle(_translate("MainWindow", "File")) + self.menuHome.setTitle(_translate("MainWindow", "Home")) + self.menuReports.setTitle(_translate("MainWindow", "Reports")) + self.menuHelp.setTitle(_translate("MainWindow", "Help")) + self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) + self.actionNew.setText(_translate("MainWindow", "New")) + self.actionOpen.setText(_translate("MainWindow", "Open")) + self.actionSave.setText(_translate("MainWindow", "Save")) + self.actionSave_As.setText(_translate("MainWindow", "Save As...")) + self.actionCreate_a_Copy.setText(_translate("MainWindow", "Create a Copy")) + self.actionPrint.setText(_translate("MainWindow", "Print")) + self.actionRename.setText(_translate("MainWindow", "Rename")) + self.actionExport.setText(_translate("MainWindow", "Export")) + self.actionVersion_History.setText(_translate("MainWindow", "Version History")) + self.actionInfo.setText(_translate("MainWindow", "Info")) + self.actionContact_Us.setText(_translate("MainWindow", "Contact Us")) + self.actionFeedback.setText(_translate("MainWindow", "Feedback")) + self.actionVideo_Tutorials.setText(_translate("MainWindow", "Video Tutorials")) + self.actionJoin_our_Community.setText(_translate("MainWindow", "Join our Community")) + + # Data section retranslate + self.combo_box_lookup.setItemText(0, _translate("MainWindow", "Carbon Data")) + self.combo_box_lookup.setItemText(1, _translate("MainWindow", "Maintenance Rate Data")) + self.combo_box_lookup.setItemText(2, _translate("MainWindow", "Recycling Data")) + +if __name__ == "__main__": + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec_()) diff --git a/__pycache__/ProjectDetails_ b/__pycache__/ProjectDetails_ new file mode 100644 index 0000000..e69de29 diff --git a/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5efa48e219d39d3910af829aefd3409df27554b6 GIT binary patch literal 47171 zcmeHwX;2(#mRLa*C_x~pTStSCPzjJY#0@$?NZlMl7f9le)S{WD3o?N$)q%2#mZ(O3 z>~`#`y<_)`XTsgPp4gQpdPicrV@0pqZjEESt8h3Rv$JEHoT_nY%36o7umeZfZclf3 z9qT`SFSEYn3;9F^tLaHA@u{BWE;v5 zZ9|!2#=Ev5yO;@g2kLw%YsOKT6?@^LJkp^c*FnWKJ}!@qbjdD{e_|}t#8Utm>ip zge+$>+$<9ll9<7Z0T^*}nh?P7qW$AI-wfo!+{J_>Iyjf$2KK;>5+q%-Mt-xl5rfmL z&8@j-BCiA=xJQ4WpdccX&EHuSW+2sGa8#s%(bN2 z&aoLJZnUJLY?jBVOxmU@RXiimbsozTG*Q4{9;Tt|d@*0#ByJ{kP31t9WD8jmqVm&H zGioT`CRP@}+{IuXrdht{#OIfmFSv*CbvqfvL)JXp%$&`nnjJBVNe#7$mUSQJ#-Q0u zODf7Mw^~vW4P4OBr5hUbdR`#g?`=JRdNtmr9qG?YE21i943s zewiF7-6ysPlw6cnULdtnyR=laB#atrgi!|pvwkVPyJ=|})M)J=E4SvM6fGrr0n0nX zX0m7V(p)ShFJd%nTGI|Ot~_*G)HGB6M>rLOjGAtrwY!h9R34hB+juI2#6qU8Aols% zeK(_DK`WCw_ez$Nsh&Dn8V`+HzLNet(|wUsGALn0 znd!dF(s?Lm^HPB>`uP(Etx-$mn02h9mWtK{Rr^ivAIs>Es*%MBTBkhl3e!Ii#r$A4 zi@6D}cMq|rJfvmf?VPC=)BR;?Pc}ve&zvU@a~&|q%9BA?6qU8rDJsh9d(yJHGcBtV zGOd}_>zGELIjg_QB;(-Ec;>AB8n-rsR$z6C z%G&A_6=n6(w5(p1memQFxYeOmGc3onW3##G5nqbdeg&hRYR;cQObamPYG7$==s!01 zDC5e*71*AlqO7=&$-nITD6L0=Kj&oG=cd+UBP;2e>QQ7|dAI^SQdCqON_P zHg988dH3mZGBSWa66(MMYV*hU;rpwJU{{zjn`J%w4Ua1#Rvrre7Yephcvp zC_UDusYil8=VV#eQuiz^tfXtIM_BYtzE+?|ii*->9WVW={E^_#Iekq%wzBfBsUCxj zD-TzoM~aHlV?8hZs`NOlM`ynArx2UgxD|P#SH2zSsb(D^Gf5jgr;6-KMCr zSXp@}Ca$beRBOMoM&&|zQ6p*l8(ew@fkg`#%tJ;E{r)kQ3YZ}V^Dr&jH-YV+TiWk0 zlLMvu#FaHlE=nuStXv5_-7GH+gDkc3q6za&2J`_KsTC>*N-IZr`Je<8 z)jM2923f52{szP2p=B;K&V@@&e0FU_&gL#;i$7G9L zPuycDS*<;WqFUb`LzRf~m=kN}F~*Jy@R+~LJ%vGOc}%x>@`-y4C9Ac^P*m&NW2h2Q z9&>8VJmw7Z4e*%nGnj{IdCX~eOL?-hleDbX9z&IIeR~X5BFbaVkRJ2*xTP5cevo?P z+hfA~kik4m!(+~h?P8DEOTG_KIb`wN7$lzK<%1GXRFqcEb1Kv5E*-7(GD#r!-(xTj z)6&X;Wwk=(V67F3iqgsjQY$~;rD2ezubekw{yu|wn3h(uK`RHA`pTNlR8q29w=NWw zwN)r8$|`+pW|a%f0${(}4CY~4R_T5UR-t6IwhBdMZ54`&vPwT`l^=3#GRU&7^qVk0 zVlWTWu*!fqC|(pVEv+jc3zfq=JU0eeylMZ4;qj2`W0_p$I4*-sdqxP&x&Mga@z8?e z6OPHCB{Kk&1}F<(<|Q)q5jz=Q9$H%1*u4svf6VC^l!k?07O#jyPkgLJ$!cBiD5~{6 z)}l&8dCV(o<}oib-vE!9XD|=b@|f(W;4ze})*eGqt#6N^N z7?hUBw2R+*;%y8itF^~aRO{Pgs1i{g^Xi&;%noJm!G-8q67eiyWy^IZ)+uk>yKgoMxOn z08KpL*I z{h@Nm;<+&>X-BenO0t9l7Loz!eAbzrIjnoYK6+dS}PP4r4@4iGCzXS>Rwyb;E0d&F=r)(&$*5sJ#cNChpudL<=3yFI$r5Fl zSANp#5R|OeZ7xN%zS~@?M3l$4navaSAf@9mF6JHJG5?goJWR`DvYvv+P_kNk3`MoR zJ%%a~c-G8gjPHzr$NY2d zDGW->W4uqnV<=gzJ%*xM-yTDii1HZUnt6=Kd;>h@rwry{S{~zn3LZnrYV9!;)%x}r zszj8>OpzY*18!*sfghwE`AR0tpD~z+X?RRGe9Q0J6JH~zWVQAfiYkkjmO)AHt`rqz z6=lt=GR`Cc`~953JWR_f*-ybLl&scPp{T5_LQzpx39Ok_CYS}lD*uwfJWR_f-lt#{ zN>*#DP*m1dp{OXU1lP6d29wU>d#A@)emu0Gc!^^&DB)X8l)g_eOdifAe#@Dn`u7~0K?&c3 zq-{&p##yF~Rn^98ELZTN|G;1#rcoPPpF(Y}{|xsR9D_j#`BU6iTDlDPuNhAsCU8H*(;4Lcia|US-!6m* zSpxDa&F=rjX&ID|ugFq)xaxe3`6&~Rl=+%!&j02#408V;2Jz5r=^0z)JLh8Cs2@&| zZ$qmn{}=fAlThKPYwYTT;-B`3HDi8{Uukugj=S*RVlMDqY{c9Dgqu88tR98mHi6-5!Gj~NsVNVz56XUE)fXHdm3_mtwRI`}y8PFD9=}Tr)J=I@Gb1iV z@n0VqmpvXytex^rgnsz!%IBi=SG-KsiD(BC zqlXr;VN6Uryog?sn090lf0QRNttBDaT)+4ifU<}`Fe4YD`E%mtZ>V3~hEG0E=Fr@b z$e}SY+LRD&Xq10bbE>$b9hQHH(fpuvBiI4P178BCeT*4xK@r}8LXg#>yB`0=wlbF- z&}v7A3&`bHf#qTilD`Gzl^d{*FfVRi{M%Rn=IBQjFJNAG%xNu)e{PC4)-U=HpvJ{x<_As2gXYE0@Iiw1n`H;^ zGf)niggjz`TjVpC?IWZ&T%=*~k4zag*Be|88PMUyArned%i?dCqem9;HP2lB26Lp* z964x?95P1^%cGjfbsuYPSj0_ji`Z#}zW>ZL2wif(D+dBv6OhZBfW@<@fIOZ6ZjxhI zJLVYm`#qI!#_|JF@cgJ-8VjCtd8JskA_e4-q$LjoXFSrtq$C9u9G=7Pgz0rd>p3=Q zDL{Nu@_PYeM$uY?tiFC=726IE+NXp5ZfHR}jIJy8JS6KKOFw4g8uduA4Sjlwg4Gm$ zr>qA%4h4S$OL1UIR$n)CLa{s`z2KL9!2uL+1H_Evc8@&a^FlWj%j^rrHfqsMh)Rlr ztq;h33H)1c{}ilMnjEZa>vbs;vM&Hw7mVgpc|p(l^qAil%W;X~1^I^8H3hQ9f$Ve8 zqPjdW`}y(lSmt@3CXj}Wa&}Hi&>3OjHbD&a&?Sw#raeK8SG>3dx@g@-)2yIhDLD11 zaqIAV#8?i{UzX&FNgzc~>p){UnvVMY9!V)en%#KAu36cOw-UVVgPWFpuk4e((_T$& zS$$f7QuVrSuw#noW8nmncI&l@pJRgH<)~QrudHk)Q={ZFs{O6!@jO&UiDYv}0n2 zp4by_ONnAx(Wj6EOOL;i440BnM=4XZ)qWpi6HqV$a8O81VRmfsPq$J}R?g zj)3GDS8z}obBtrfb;wwWvhd!Z2aSPjcB-^qKB-HQUzdivq`=jn ze`@%AKy*!vTs&Rlb-^eOMuI3_7ls#KwB;Xr#t=+L62s!)2n>t1O@s5L$AjtD0o z?<|e(tW$T^E!1~~cb>TA`1a;ULf-qE-`gA&km{rEyFGV$=AWPMf3WqzSh%bsB6J!E zHL6f^Pl^c5M#?@_*tf7>&o|BmMY-1;5$Y3=ZhIm^)8ne9Xw^}*>S(y?*eypyCkO zwrJ%kwenQB;4~B^AG6GVFFz{mRfWAD?Y~=nr~2Nt`MvW44@$x%$0EY<$D6mk|HgZ7 z{Lal=4vlf4DinTL5)pPAl)F`7_lIIcC^b@0EemC*BEo4SsYDe@K00)_Tx9y(-ktKR4e#f9=8Eh|p$wazGUhEL0zl2roX~ zy+69UQQh4*-~9QpPmhInw9c-Kp4szNO@bYs0D-P;rq4yR$&_w(M%`_9FPPz3x?yWdg34NL{$ z*50!HgN@(a_`UpFS&xK#Y&d?9^#}RCn-A75A4EeGUPKjMwhyCTFWW~^uNY`CS&zwx za@9VCiC68fVlM zr-sy1LlNPXC#fBiWSYjlk=C+3S+VD>y>2y*dSj_q%KQ0q`4Qnz8Xl6Wu}Jh$bAG%L zSc7&rSwGk|o6@KzYbBBumP8Ay)I#Xf?;o2x7A|ao=4lh~#a1FJ-~iyG@w=Wop82K+ znc@9M^`2%056e`c?Di`Wp~0-}vw1G%+7Z_$av87Ff&}< z84g#N^)G)2(NTL@SsBls-;QY-8A17Z5~jY2g17tQ}}K>NZI*tYqS52?H0?5Ig|Y*8X51tkHl>`WRgwS1&WV<>tS?dv15Q;P7(1mHEc6XMC*m zY%JfRmbUz4|DRRtHNJt@6X5omHg*&^qi=|U#S=oq=W+KWASgK#h0$773(SM zwh6qfKV0%sL}*W^8TgwgElvj1s5@)!iTAy8-tf*gsAc4Qz}ERuXjzn``2gbWACvTV z{b!GNN}teXK=!D@p4-s2?!!8+rKBukQCYO8RxPT%7rcLS?q;}1o75=K8r=d_D2NI; z#`);d-QhdK^VmgoK72mB58KmJl0*fa7Te(4+akh#6F(dXEtDJ0w2p|0w{J#-gO5?3 zgK80*5-8H91Xd!CDGMAXev*AZXD%mN(WX|kMTBFfr#N%)$?0fyw_4pDt?p5)dwzZ; z+B>24PDFb>D*RQ$Jdf8zz=d0J8yT4IeDM6bsmIv2WqzH($5U-})|<4?fG6-V-sioa z_FBE2z%b`w2|2>k2XiZ~1q!@R{Xg%`R?&vT>?ORGmEmg{RR0w;o==i@>UY+7jcdEp zHgAWi;nD|za4C~pd#S}{dto0ABd{n&lK!@8vS9ICaq@JXY1E4re3n`UD`^|&l?RRC z5^Y*IjcM{VZpXfn*0coily&`AL@N0 z4=jyPL(i4#=jjZEpCx%ry+-Q!jAvr>i+%G-7l}v1W!h9wsxD)hm#g@^?#!L|QjPwu zp^qJK8Tpe#_gm&#q6c1553tVI^iqa%S&vh!MxD`Cqt4R-H+DGp&lhnI?_rzR0%a4}q&kg+^6~ufj*0&Z+Q>*KZK$7)e*qh`W(T(q-Dxp5fk*^bn$AMgm3-fsvVjv2iJeNrJqTPjR1L(v|eV_YC)Q zNeB=%@h3PWNQYbfk}gAknX{QDPK2aaVeguyj>(Lxybzb}Upn zLZ@@}4M|St0PL4?x}5PHQ_kz!ZX)OPNy+C7PD)N~yO>koNa>UV&MDY28Gv2l27M^M z)9)LX6|asOs)hZMPT1z|3{1y?>n>l=8T32HaOW9h0DIb}gU*2Kb=aZx;Ou(%6W; zgEOxW_SLhU@iE7>X*pPFSN<3|Y>bz8WYm8Hw~+;=VDG#XK<5R;DE|OLZ_n;~Seh<*!wtXeoUMTsV_twTo!q%uzqzXkpI2+wnr|!}#KcrzhbsN?#nhcDE zU3$w154Aq+jk|B%dFv-de^&hCVzvBaL^!2kYO{f_+)4Hg5g?8y~#&-M4vn529kE z;Nc*75Y;pVk1Uc0Zlg=OQt-$oc|4!Q0HP^)Y#@0U%>n0?f`>rzpc<0p?UFE4SBjs==F@R_a9{D5>*rb_!rQorN zE3Xj^t6D!~mixcsx(?s9hD0tt5~7 zRq@zH^1vPANmq)pY$tg%CozC%3LZO19*0)N;{}q(kyY_1AbGSBJeK#0XjYPZ9%&rr=Ssln0z(Ej_30_z%6HA7gdG2@7X@N4^uc{qS^G`v`DHy&}-4w8g64@^%<D3t34j0)QJcAf7WymH}$^VDE( zkMqLCj-Jz<&XStCy30+Sb#+~XUHXIO+WNY>6X!~tC6mG6RBK(`_3PJbuQ%2Dm5I8+ z{<{7Xoi&p|ucx`bE)Z1Yv0$wj6ibfhwqfG&wn;cZbG!`{=yLi%kH<=`0nQ|zcr0C-d5)TKmsj@8w3hb{z?%}N-`VAva5k17)t|zt zE~)iMZR5eIU_9lzc6_O|xxSvEUP<-Uz%iJF1O<=B=qWYVf;FQa|Jcix4%%JRrH}dsp zhJH9~?!*)C>r?#XW>lPp*2WL5V@*HIWkB<|?&`$_Ns;}cJmxfa1L#_TbypN~35@P< zeHKOcCdql)7np)`fAGf|-t}h|eLUgZ@A5)d+b>PJMrDtzpPJPgf%RA-7=LgWAp>d0eXLgn{;w-o333hom?@#UXKM^SNp7 zD0HI!ac2*_2*7KA*0DmfF)<0zKxkLH2%o}$MgzFUdnd^W7KT&cV^Zh^9b1NJcpTh! zyI?$9(cfJe${U2CGjc_QkiBAGWyn!+)ms_bP~r8~A&_06g{~tosr$|FpN;MR{}}$4 z9B=perom%Q`^MliNiJ{?*=M}b%HS2oIL9eYE86=iU?486nK-?Ec&iOg!s~_0IRjr= zG7TV-+;A~3bcl@JEnnsYshg5 z6CJ&H-%xR(^I~0BWoTFYXuLCiHr}bP7U`>6p`FXTnZx7Ca{bdCTi{Cziez3O$}v6_ zpge$S0sW&KKSt;Uyy49B@*kkU`BCbl68dK(aQW&K)L`xV5Xxc1P5khK#itmS{r18b zvKq(RB;4YwTl)71az~8vB(7`y2rxtYmRr|K_+r8z;=N zL+bWJw{o>l1K^JCxibrgdl$}6hO6a>;Kn6&Mf2(MBPA9}*20})}6dbZGwN0}nR73$$bf4KBgM7X@#XA9kz!ljoZ!pjt4 zN>LZOUE$Kvh%iP|j)Y5JiwG|I;j7`&k%;h`)x#vUJ}d|q8waxZGQXzPFKk;*W(>g& zmMvwkz}}NBIm7lZN^qw_vSs?i#c>5MdDIs!#*Za{R7)O>gp1>)S@LKoTnwi&hnG@4 zEDjgPzsQh8^l*E)SYPE#>QfaB;$y6Yz|X=2H&~!^QDE4$0h7s>N|Kqto%F%~96n z_{!$UIpNSYXe6;%Xdew1k3|HLD8=#;y$~)w9TCnDG+d&jj8c?u`FuP3OW1SGZ*Km%^B~5PS|kT_TK(;I!JvNGx|`#OoKQJqYKG zj9i1RK!2iqhAj)OgDco*%-J+zeRW*GRJ?75{{j5+(7U!@XSQc&b!GgzJ8w(Yb6=Jn z&Jr_p3dF+<@uMaqBj+l&#j-BU^aT%UO|kajBYnkwvzA)h7ytB6K-(G+%l2zuY|?U5 zeuk{4qFs{t1vM{{nBg z9Ih4QuZ1?f@W+*3K^ksq3A_)tFYFl^8Gp6ImT^F}RsE%{{HyHV4H;s_hvQ$_G~kQZ zHrX6`w?eMb+V=+670! zuX3E>oRYUY9yzjq`^-CM7B-hg9A%Fj1yRRt)v-I`*z?Gd_jc!RpL*xit%3Kiym#ey RUiqb?@Qckh$4*V){|l9kk+%Q< literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-313.pyc b/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89360983adeaa7f074c3520af8167cdea2a960de GIT binary patch literal 47780 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 literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.py b/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.py new file mode 100644 index 0000000..583f82a --- /dev/null +++ b/__pycache__/ProjectDetails_BridgeANDTrafficData_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_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 + + +class Ui_BridgeTraffic_Dialog(object): + def setupUi(self, BridgeTraffic_Dialog): + BridgeTraffic_Dialog.setObjectName("BridgeTraffic_Dialog") + BridgeTraffic_Dialog.resize(1440, 999) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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") + 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.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.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.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.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.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("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("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)) + 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)) + 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.setGeometry(QtCore.QRect(540, 520, 341, 32)) + 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.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.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.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.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.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.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("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("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("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.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.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.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.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.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.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(BridgeTraffic_Dialog.accept) # type: ignore + self.buttonBox_4.rejected.connect(BridgeTraffic_Dialog.reject) # 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): + """ + 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: + QtWidgets.QApplication.quit() # 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 ")) + + +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/__pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb9b8320244fd584b4bb597d78320f9c9854995e GIT binary patch literal 74484 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 literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-313.pyc b/__pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7cfd3b3a99259f4e7b0e5b730d885714a6b1092 GIT binary patch literal 75474 zcmeHw33MArdM0?PdFVbY>V!avhs665NlUUtQq)BqM3a&&(WXEWq*?+2ssZYtt+B`B z%*e;Y9v@?m$MMLH6G^kqnB&aGN-{g6%+BWd?Pm881SaremEC!}`|`rPyj|~jcJs2y z+qeJUjp}NEKhQu6LZn0xRrPmu{YU-ZU)9y!)!kn%Tv%v<-@o5odiva_7R!Ib4fe~6 z5MQ=uSuAf_M2nbp&~hM4&N`4SXCKIsbF5gFeK7Yxo}6d3thU%JV$K?im^+Z)oTKu} z3kC!+ul+HL#W7{U+W7|yhn1$Ddpbd}~x%pV}!-z`qFg+74&D>nMz$HF^e|}YDD1GRe(ScbDAS^t(KFyY@a=!Q9QSS z7ACLX;?nB&TkL2}%+FpFQvjPMi&>d{5upHGgblSk#Q7PO*i-h^tRBw|IEXG%y})8Iw2eYt;9NSb>$ok9ia`{EO!Q1^yk$=R z3w1RKP4->$xeYiTozKB0AIexyA=^2hr$2{T7?*3$d@gZ?As5vzS8^_0zwDkbKc-)5 zQxS-zee-LZ>X)lvRDDQXJ-=VZ(LqI032#pvF3JmQIG4@~^s$9(FXM+9gf^;!{sU?9 z%v!NPTqmxd?-^Z0rsH@+*=syvQCyUlH!v?n&B=P^`v_vq$jh2lQ1kG738vz2BY4>= zZo2DUrtH<)!`l*bRHwM89$@2Mj^i5&n^HrrUTztqV&(#rg$7)A+}kt{KwIt1E}ol; zgSFyjaZ3hz8)dI?98g^Syv2+%IC39j^>`i?0hAUhZpt{<9b>7@1_4VrO^^MV!m=E& z+?W9@DSM5vq`1>1p7)y#P{a8|3C@i<7 z5z88e8(=xWXr7zGvMNJZQuZ2SNpYngOG*ocWpx^{tYx?XmQOO8=ccf%0W51X&@U-_ zjj^P-(vKyjg~GCq#M0axxQ^ilSe{}u&rM-jFE+sOl9&U>(V@-1xLz@O21;2*d8Uzj zCXOzHxuiKR-K@nN3iYhzpnr(bJU59#Q(P1%9mXh7Toek;Q7BMc#weKJ(xEWIeaEOI z&IcP=T|i-!(L6VWLJRbT?c&d2SM$%p5x@dyTQ7 zxQwx(xF~EMO(QmK3zy`uy}+VPbeK<^=JxR*&a~T3+IH86_Sy@~)c;z~c3lokrh$I^)9A%+`Z z`3|FbZW7C;j9^LGYm6num3}NKEfkiIrxDA;3^%~?T}JcV6qYB%_Kb|Nl)c7SQe5fB zlF~w9c`}Vy9$~lvmhUl|=cce65TD3MzohIn#**SnKbDjh3d<+ch~-g+8(?{r(L6VW z<)G3p@BTUt%3fnEDK6t{a1>YiBY@IEMZl>vMu4`y2qNGE%eEBz5bX`v#(K}LYN`_Xm=Iz+&SjOMwi2pEF#JQHiVD0__~fZ{Ta0E&ykW;l)5 zhztk7=64y*b5q!iWC$C|USn)1E@NycE(#kbiOo9hQAQtFH5ACN0?`L zoL%D!15DD^V}4cZs?bpcuJSD*BDEREB#nfS|}_%X~gmb z!wsH`+bPZ~$z6%xIpQ!sZ;zDC3QGln!IhP+XK}&U4SiX^+9UWsXbNL(HKt$UF}E zf5K>Z9 z#lcX9MoP+F<2azWjN^dfqT=8ZkApkL#=&p^Y<|XQo}0pEBtzIx_8MbDaT#MnaZ%Vz zk=U%`9%WP#bM_%t7qIy`qj_!$n|&EN|DxfSDIl^!QEI(s3&rM-DAnwk{oixf`V=O7I^kYeB zp|Jc)8nJXT+yKje%V?gP!g5gQmv?_f5oND2mK2xq@sHw4e*{oks0er_jS(=)(1r;3 zGe+~=WCZNa$ciG$UgHR$xQru!;!1x6P+F)6c$SO+ZDi*7B{9z$W3?dy{sW_VZYlzX zU_8&nl^n`m;|QR*j3a>JqOkdD8nHReZ~$y(7|nB2*o)J(D0unL8O?K3UOtnd(TuX!*vk}`@n}YIQP_N)#Kzoc z=3+PiHlH(^=cce36Tgv>6-AW2#@J9?=^xD~EfkhlbXbO&-|g!e#dE-CAKzjWC!*{%Mv~%6Ka!Ld3dt837ExdFSjQ1yR1$M@5337U{%1z> z+!U5>=#`mRPej>kj3vdDek>_16qYZh5z7gN8({fMM)TYhmR7(r6YGg6dyTQAxYCa$ zrG>)sB@)XXjsT;Q=$GeMUBL3cGMeY6usjEI|9E%UDILc12#SmH%*)&}aoS^iXO+@r zz@@u;XKr+qna9EE|ITQhn?k`0Jt`CHi70!Gdlbc${vJhXq2lJ7JZ^MYj&KARl|+y7 zvbuofUox8Krm#E%Jt`Adp(uNev81?+`!vNxVe^|LHs(e9+;;&}I*g;7;-WnB2I-k! z^Y$0nal3_FML5+1Z9JHyH!< z;(Oxz^J5^c77Bx?F(Qt2lz%>8?bBbA2mWkbvmzuOeQ3!{2KeYX#;4THWm`N42@CiZ zqoA)V|CkprYM~~L)E+sp!eE9`NOpoVRDo>`UNUI=;xCYk4UgMO&9p_OwnN;eYZ z_UGEQZzQn&7K@I52QOum%_4rte5b2PXtM8OJUlnxc$C*0lwy}{hhmIEud4# ztmL`QUW`WyzfNA8QT@5h%19g&d?0NhtIzZ3qc*J(N*8kuMkT{Z_MP92v6bj^pm8bF z!*ivF5-sEMj7MCM%mr!`lK|t%3#?Glwv27@oNUuICP6h25+C@m(`q~ zQE409^7`}ctI-Wd3(6nVXpxtMAE~3>_ha~oxCARUGAwv5zQcPa)F65Vlv;UxM#V#! zkCI%j={Wxu({|6sInnEk;#~0@#g|MxP<&K8{0r0Z@Dax?<#-^hOKUv5cF)JdW@aJJ zlZpq5FPV6t_^5dJJ=5`U^bX>Iur96f@H+GIJv%;>F$;N~R6J08$;1Q2N5#VrOvl5= zcMuPRbvz!FaWR+siBT{L#hY;^m?b4S&tgh+Ir9w6_1<8`X^k(O4^%M8JWnbvC_W0` zY1WGPXU_@zN@(HoIQ|Ka>%3VS#Uy}V-(-bp_v=HKQ$?&N!{&5C8jm3~vlkaxsCf_qbU{r)7( z2Plh^=@S$mb$=}C9%USTX}?PuM-LTSe@w*!@Dq1ka85zpWoHs#?7A8i4Ue)do|}w@ zA44>}$L`TFeUVXy(n28-d#94(qdfGlxIW%nz(XCpRg6l)Lm#lZN`L8MTRb=Ap`XB< z>xlT%I3A+3P#(I)TN>9o;_R5ueC@v|!ByfCajCd$K7whT1;m>*>(}hWyX*2Zz3;)#RLheYIVrw+?mb2& zjw*WttH<*s>}%$=8D*d+;cPE%1W>&z_DDeafjWE0NuphQSZjD5btet40#W*?KK8F; z^l9IB<+AyjHynehFWiH^9$ueOP^x|Xj+I$p1s#+h<=c$X+Yv{Hoz>-e0v&z47Nhh! zfKy=QJcrU_e2Y=y&+~wcqYKL)V>NjWr6>3nqhc%|uA!K;V1Q}hxv>RtJdnJfvF%TC zjf~>0^!YsQ4$sB8LfD?eS=`*OA(4I`Do)vk9K&bFWY45~ zqu|We z1G^_Cx*R^oK#ynG;Tkw9d&XqPxOd>_CEsa}JCZfl*A4VYLjzvP=d7Lp-HtJ*ci^0_ zdC)O2;gW_OK4_VDpw~ArB)JD_YHAbwb$KqhT^`4Xx3)|2qEl)n++*G0!ih=m=?)YmVE-?2#B$}x0(&J!_w%I!OQ>^0FS80>BgXyeFOPzER z5q(QnrKNsu1G0@^8CTd)H}_mjN&Va`R_s-ZU6R|mXGHQ1Hf|3W@r_og4;A25K22$Z zMm0)5Lu+*P+jJXEbD}zj4lwYIG%9?bf=v-3^q^c5TDaKVkfq)A^_A0thg z!tM1^m(G$_El^q;HDh^$oAmUp&0v^7SBYIEJ(ZiMrHK_cA%JNv?g-eK9}a_yF# z4jBq8bN>_fUBfo4;k%A2!%e5h?Q^<)-od81zrdqdBr6`v>$QV`@FJ8YOVDSOnL;?b*B36_=aHo;=t&M&cp2;$eGd1C zLmp9Odc%30E|1q4&J`W!L3?ix&Sm6fphnI`U3tCTz#A?G&Z8d5?GwSc@B-LUiqUw{ zcgf`xPdlAHrMwS(7B1>Ns!cOwoDV3bb$f;s3b-8IQ0st}_f5^maz3A+j>AECAX2ea>;&hI_HSyIsg%& zAf2*|^E|l>6Fj?>@zhSndnJlB-*^ikf930~fo+c3dN zr@RvryvP<_0;*M245IwrZkQ`N7I zi048$Uz~KGb-InSW*2`<9u>V~1bLpw!K!dQ(nJ9c8nkjp7wMlsJ~ z@>S$4>OBVCSdDf0IV{0G$qsaf9P*gt_R3$w9oa`zs5(zO;Y0vrDS)Ed2-Zq>h{W~t zs_<@YM3#R8O5|@~F?Na$k831c0K{=8b}N-s4zl3_rOVqrVC)s-c@dMBF!?4Xzlq7K zkSOX8Np5L;a$Fe&@_LmF2c1KXi>z3F4H;g?14f@>^I`7;!BO zlk#`4KsXjThRff@&4n_Iad7tJa`>G6Qdn>d4?8D(&XMo}7!KSx! z1Vt*mz~>nogCnhW^3n{G52x>#YDUh1YoOIw15XOPXJFiU*7CWqJ|t}O3)^O@I)g%&vbotWY`)rey(uWv-7c|( zN*ZTN8fTjJ2TBfH$$fdz=R)x-i(Xh15^R3K_F>Dl9UttN&Y2c(mfn;Dr5!<`Q>(DW zFKqd6^qTa(6e?@?m$e6lhqZl`exdTC%Fxy>|JJV1)&u^n2mb8w(4jH^p|Q{*mmmJN zx`M*ER$VdaWKgKTy>dfn<(Ao%TV~204y@b(1{GqZ!WRld!Un&v;obA!d-|QHuXjvm zPj}v29@w}uDD1kuc*QGEz4+8im#^eP-H=e?7fRl?1%>q*=NiAT=51)NRV%Uj1?$YF zJwc&cD^e;o1ceP+30AuLP*AA6y{0s@rgnBs?M&UCz?yEbYB5$?{KDdpQ0f;-KkUBN z^Fhz_vT6Ixda@_b-LYclgV91cja2 zzO8;?>qjjgx7}z9ZF|(e?a`pHTib_7%#eiE3o#X`)KEswh8!72q4IhqOc8-qq& zbq0kht&UOxxHsNjy(zT1a&~p)%+^N&s~-h_F2qU;UsxCtHu;53AC_IK`k?B%ce>+d z&P_33Lw{-&(BU(i9|;PNMipI!=GQ8MD`r<$Twn3=`Wx#5tGCCKEl)R0%QqVWwp~Ht zk!Y{QM37?DdcUy#?UA5RO6Xj>IkdKBc5Tgd-^Z74Tn?<=c?CdS4HZMeYQL~LBw%!Z zc29)`(Gd89A0idcB{a!_d2RaBUl1%=I2 zS!%xW-s}k2(CLYcRuP=;w<&iPLZX z+4(1xerTH>=KWCDJJQs(?o(4rr=gqQ-O=fGDz3?dJ$NWe>WJZm8OO4lchIYd16SGy z#(yjm?=@5(*pqOMaL@I^^<|;;4YTVTrkg(9`NN%o^}BHnwMH43ab6x0B2(k1KX@7^ z{x^qjH3Z7LgTh{2l?uO5F;m$U6!ydvUf&24j@ubffl=2yyS90HD&AhTF|?{|c2(I`-?hu{UkC*k5+wLa-$?vxyN70PNx)RWoU!>UZcOXF;v>_FJGMR!Wq4AWZaeqN}P`&IBq$wg$hgA!KXy+t|EOsgHZFRs@A@|1=trxx>)C zcFcKTPvlZXoD5fk((mX22(~UQ?@8l8>zl?LQO}&#o zvZ4=i?&Jh7&6wP4F@pBs^*VWEhF}%63=(}vQpBaoFS-EaZM(!<)mWIU4|+Z zQ_wZqsbHdx#ZKz(;!S~6?S62%Fz(H(jEqhx4GDL2`SPE^(d~}>^I+cpVBT+d+xcFb z(><8?S6$EKyx*V1(u8|0Mlv*+OzB9Lf%A}5W9{=E1x_!D65X^L}+fAv>|r z7BSs(Hz@D5E|`J4nW-##91d(|YeSW_jn#gk`Z_HBYr?yEN{PBgk*(xYif~DzvJ5q< z(p6=J;MT^_)`w@eJ{;JpET=1nDj@;yjfR9qztH%j?oWF@>AAJ^*6v&0&pHE5M}tDI zu1d3CXr5`Y2L(~L$}Lp6&tJJOuzG*IHG$`*H^D+zTfkKmN-&^?3LRg(z z-iNDGOZstL>G7NaTx@zgX9yRYVy0|K>Xw96&gM*D;n|$?ScsXjHKlG%DXUYTg4L-p zN*+^`JdV3h=A1&FlR2ZvgPF25u5OKE6;F;23q3iCSj?2I%Q?To!pn-1uiz5ckf3X5 zGb|X^m6gv5<=4UA)w;6ES-2^`8P==n%J5#gvQR-OQ*lvwLly0_6?petS*@*x$;Z3C zzbjDE9u*{f5%sz)p>_3vb@kJmKdt$sCa_LfO;MrgvM{7`css^^MbAp(fGaBnH;^Df1^?OzC2Gw{k~ld7wP9q>hal z=nnl7N9BO4`RI0ORj71(pmh7q+)(Rrf9vr;>0_t_AA1T3)>*+C5~}<{)knuZ9{BzM zJkfRTmhIMXpc)?>(p9PP3pF#fF@?2$q4r1lpBDVEAXL{eTcU@9G zEUeGl9J1~9tM`jp*PUtH7g!Hd@aRc{uI;#lS-fRa+n#88sr1`ERZhZ*h|b5#b7^HC zSKfeU(;o3}jXXuf+>2)|AB}!2-H<{xo&Fm2i8*ajp*sIiP&lj;s0;+uD84ez^zN5z zx;V484jy<@M5!x3(PJS2=Z5fDRA_7W>{e}ot6_!6{Bss2+AVNa6%ra|1!XbqCp$xp z2WJ}(28ABI)kYQ7M*ug@3L9avsLWtP6>YN>Z9$>^wgBU`s;_!hsGh0m{Dsg3b9akn z7@noqez`#9!2d6Q0;O+SPHOw`@o4|#PYA0&>T6v5eL}#!fYs!=sJ*cb z-cXfgFW`F^C1yv-z^6N7emV<^V|Kx&ZttS`d#k~wDEUk0*GViPihKEdZq0x2B|e=b z!qRql?Aig(yq{dbq}W$6isyEGi&4yv>9AVHa>jBhbEn!0MR<1B>aa$hp0!?3o=de} zIPG*>eW#sP<>dla^(8e{$!nc}*Bf}@?H7ep3p+jTQAr+Gt4!6v)74gZZHLu686hq> z+&-(%V;#o#6M$fNPtv5%>UErlr*t1VT)9xjCv4@Lkc1Z;k377K&myaj#`Z}*c%b?B zu*&aXm7K?&-f;F|kMekZS-@e}fW= zB5fQT@?6BnjlC1_E+(fJLlqwLmEmmBBF|yPE%6`3)jeqZqilE@_Z_JJtmW5&{E6rj~Jf3_T>9d!ZnvK@+~VihL&&kFW($kUUo(JylD9wPrdcbx1I?U zmEA6087j8U&*s1-nXDPk+3y47Iz1j}yJAG6?h3`a? z{j)zC-nyOzeK4X;7ws169c0uy6(cfoNEt*wNFXDZltJ`s>gy0dh zm9iirMIfat(_F6+3EESj$%u@%iX~eA*F;zn$S5RbP$LI1c1GG#M9Nsr^)o7g9mS-K zHH?$z31loJWvt`8j7o^_B29`O-(;mQd>4~a@a<(Wzl65jnG|LV-6Cs@A+VXiw@bA4 z=wr>6l8mLKjLj*@SVqdg7q`Ux610VuQ4u2mHWSELPTH|G4Kk`ylCgrcqb3b9>Qa(H z`=cQx8FX8kQj$T(X-i5n=s1N}Y{dQ�TA$_E-rCV~jpt;fsZ0ehKWLj}iDvqL^O- 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 literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_CarbonEmissionData_Window.py b/__pycache__/ProjectDetails_CarbonEmissionData_Window.py new file mode 100644 index 0000000..816b055 --- /dev/null +++ b/__pycache__/ProjectDetails_CarbonEmissionData_Window.py @@ -0,0 +1,813 @@ +# -*- 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 + + +class Ui_CarbonEmission_Dialog(object): + def setupUi(self, CarbonEmission_Dialog): + CarbonEmission_Dialog.setObjectName("CarbonEmission_Dialog") + CarbonEmission_Dialog.resize(1440, 1000) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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.rejected.connect(CarbonEmission_Dialog.reject) # type: ignore + self.pushButton_51.toggled['bool'].connect(self.widget_2.setVisible) # type: ignore + # Corrected line: Changed self.widget_8 to self.widget_2 based on the traceback suggestion and available widgets + self.pushButton_57.toggled['bool'].connect(self.widget_2.setVisible) # type: ignore + self.buttonBox.rejected.connect(self.show_warning) # type: ignore + QtCore.QMetaObject.connectSlotsByName(CarbonEmission_Dialog) + + def show_warning(self): + """ + 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: + QtWidgets.QApplication.quit() # 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", "Componenet:")) + 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", "Componenet:")) + self.lineEdit_45.setText(_translate("CarbonEmission_Dialog", "")) + 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", "Componenet:")) + 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 ")) + + +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/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9955bfc867742049932f2e3e56c01e64a10127f GIT binary patch literal 33667 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 KEVeSj>i+|udEK1= literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.py b/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.py new file mode 100644 index 0000000..0e6119d --- /dev/null +++ b/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.py @@ -0,0 +1,351 @@ +# -*- 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 + + +class Ui_Demolition_Dialog(object): + def setupUi(self, Demolition_Dialog): + Demolition_Dialog.setObjectName("Demolition_Dialog") + Demolition_Dialog.resize(1440, 1000) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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(Demolition_Dialog.accept) # type: ignore + self.buttonBox_2.rejected.connect(self.show_warning) # 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): + """ + 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: + QtWidgets.QApplication.quit() # 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

")) + + +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/__pycache__/ProjectDetails_FinancialData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_FinancialData_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05f3853ea38d5168582b20d7621bd80f161c3408 GIT binary patch literal 36818 zcmeG_S#TWJaSQAM99$&D6C@5U7B`6-ya0kA34jm}fW$>ogkVAg%mA3xUYZ?<#0re* zL={u9Q}R`joH#MzuuRZW6``^#q7#+PN~IDzcH$lHI?HV6imUPyd_}Qb9&qqGcYgHWpihZW@T71EiYOG>zfwACT729)oDjJiJXx2YK_Gr z2LLzQl{1=?X%Vf?9i$MNiaE|*^o~I9LD~2l&DZGm>UZYrd$F6*&497VxZ5Ibah93! zbXeR>IA&PH&Cc?b7=#1X2xO{Qi6Q1Yt5@a{I?8`8doH_n(=wvgTBF&6O1D=QqgjK> zi?dQRXHdB!3DctDug!?&4fczEKR+#Yd2dXbS;uN``J@zb-?506(G0;dnki(wX&JQ& znQ*nC&PTK6Y_(a@-It{CUdh9ICMDk0!z=uF4*=fj$xIVZ4q!CTC*M1dEax+vq!1I5 zkU^^f7%_925Ww()^}QJ14CF%G#e^i-C>LcDdEi6|5`ObWdGnTWgVVglskvu7va=xm z9`BSbB+t2(Kr|Jyglu7xz`bYF^Gs50Cs-_(BqqI?IiKm=O)zOHWKmA5$hwDO6KFm& zC6%E8LSwNAIVq`%C^mtF+>}%->*rA_lii95SDMDz&9DLJ0{u1NmzLe~g#uyoGqxK` z*3@=mQ85CJvYDvC=^zkIS>c81eic13f=`N4AOI)+QkIg!2Nl|K_9ybTSd5lqowqvc zS3WmUH>`XrY$4Jq+sIQVqZDToc}mk16fG2!K%}#RD8}~NLj;qi^Hw6^s#HfPHi3k# zDXCbk@dZj{YK=z;SDMDP#uAbbwQ46YP19(N+aQW=7j~>{jjNQv@}QlxE~`+nG_Z?O znKZ!aN0@ab)X=vWtrkk}pxPi%8d}*c>=p`zJu9_h@^Qv+$7msI>swf|rjAi8suQHH zG))mbPEt$)rJ(2{cr*<_|DS@Yhhh^5sfhKomd2_P%VYLZs^8FKj3FX;OfQj(rfGRh zkx=~1J%%M~YL8)2ZETNW)rjRWCF|xf#*_g(<_z@|0;T0KrNX{v?lCM`Q+o`HYGZp0 zt41u3v9FuQR1)6+k2yzRnx^G3WkT6A_ZXI}sXc~8wXr>hRU?+iIM&T$s)%=h$Mg}H zrfGT1KB4@Xdkjm~)E>j4+Sne$su9a$DwrN~fm)hC;0KIRKB0z@Z;c6afWS0O!(%Fi zBB4sCUODmwSy&~o<^wfELTz-%qGD;KmQtBUcj;)QmS_NE9wIPJ)6z=ms#;-{kXkD& zDwbCEGqo~8YeS%vzH-2Xd6~d8O-n1gK`Xmg`pUY_l322)jxH>!)K+0pv8-}n-KGqxRcfoSs9091V_IdDYLh@I+lsM%1(;)mil%8;rCw+d z8il5nZDo}b#%XB;N-@)Zh2YVY>SL8s;`SwUIwhOXI~kgD3gjtGQ&3D%OaiS~0buEv zW#ML8BU2xdPBc77WC6|05SXTEShx~qbI(2+vSdx&?pRc*+Z~IFWtA4DRXD0m0)eiR zk7tZkS-^A>Dw?KYl~!2EJNV2;z${r)TZKinu}8qH8nHa)(7JidVd5R&F)jkrG%b%Q z{VjM5OV-pL!=l>Q9>b~;%VQ3&o5vV?IN&iJ>L~!5Kc06%_@OalT*x^u*bg)o`f3e*GcLE^_wR! zP1CGjxzM$;e(SnM!;&?1q+wC9yfXfj6pQNVFDM#cYoy z)L{FQL=M2bMPQnyW&0jj{Xf0Z_N$b@(tUhHV9CYON-wEbhQ*|n-=(!75L*&L4O%fy z9RTK=go>ul&r+B`(4w{fNWC?CvS?Y7zeviClf0PlrfEvadkF5jwzS)XD@~m#TAHc+ z{gjG8B)Z!39Arn9x+Uo5IWq}T*X=zm#MZxdXau7QfxKF$#xuG*@;NXsA)yDpTIDMm2*|CBB5 ztf`zY5tydTv(V?6rGJG|5eQ0W@Fdg_q0XBy|A@deWtQGY=+_Wsl9%2`N;0+b9VCyY zMk`;-$+S%8pHNBy#f>O4oqtO5X)2V`T7lNyrB4X7PLV2WK5GL-Dz+wAtv~+cIZJ=6 z7TF)Cb(RNyo#>yYLZ@lfp5gV*I~12dY@5kZIklK16i-+*$oDk&LEmusVw>wbfZvEUOQsW%a?dtj>^W z-K;)Lbo$&`{hLHGny$g>EUMI2XHl`NK9rW#htsk;Lndl7#Z_&CCh){zbFOj@; z)#JAaSDLOtk1Q%y8;YMJv8YndkyunLn_VKhdPN7rFus*XfR((^f8?B-xR-25Uxvp~zx4Q*|V zOjAM){&bT9KV(sq4@2s(?)_-S>RSL_C#)#z%)(6Yqtr{{(B_sF}(TQ40}gqI?m%*^-bLuu&BzY#t4+64OI|4nx>R( z{l96#stYTM#P@3~D%LC}{`(&+D&y=Z)k5697~3aVv1BGrd;dWB5r}%d7CFX9K24qf zNMQmwRRYlzzT@;$dPktR@>$%&1e2yr?iS}iQ49jbalcJ+Y3h7TVFC#fI`b-JFjf(JU7~A-a}pkoF_IpfLhp>24ppg*F~6jU^*)dkzJcZ+drdhs&ii~WX_8kYFR&U^#wH}sSVKc& zl7Btk>mHYv7yOMqlHV=){S7|PbZ<1*H|wA2npG69N4f^hOZo*=boswf;yo*CTRt^4 z)i%`@<*sWVXB%%>-hmawWy0m1aEWfx>OOvOc>uE(Oj(DPTQKYMrmVxucmkQw99jOk z>F%hs8KhapPHK4^B83Q&M6-3(M>oGN$_luE ztB;@a&MJXFdv5&8=i=wad0F;eAD@z3E>S>F3w-1GmCs(&T#GVB3ut2)Z$4yrIe5>M zS9a@)jzel`iob1M1`}E0^IDeC=J9#0%lOT}xV+}J_}jzFKSG=DL7S82c69l}6=VnF z$&SR|9*w_kl70{Q#ad~r^bNerQ^Xre7nlp8(=tZd<*iuqXFzObtLtsT|DrDt2OQSC zyNj2X@c>UW-=xIDQQL&q>#Frd3;d#Tal$E1Di?US7|oVNzZ4L)>|tfjB@WMsq9Wr6 z1U?1XhXGXHf;V|U_oC=^!^1gQQ(D&G5O9%g_%v&<7uriSACLyTl1CXvo}1uK%SK=X z>INT>95AAsj9+GpZq{%nXZ-_Sh@*4S%t62!R%9>4L@#7Vw*i7~@E%Eq@*vPg^M<79 z8D+%VCr&9)^w~+s%i#rKK)T`PebG&WF(CT_cnj}}S}#IyLLoFv)WE4(#XBVWL|y@G zy}`>EH)ZT1v}&AkNz)!TM1W|%2KI@(Ac1S<#O|PE{lVpKKoVuS7|AhM$YppbM@Bh= z-LZ%$S79a&A0e|Z77k_Xn&eu{+&ZX#15@t-Ao|I5n2qYWC`(X+*2P3~&v<1i0IG&s zp=O2^-Xrj`pp*HdS=}zLUyRy@d2ot)W!g~wI(6o~M&p-HK#qJ8v(c_yUY8Kf0pN;=o}q(U z<_oe0azUlAl7FZoHhv%Zqq9gO_t~(TeYvJd%6Xt$AG@WUwII-a$|aiCMl0#26s8uS3tYtCBHZ>U&ibL8Cn&LMO?fhUXr35 zKRGG-6j6w70?+n<(?_%QtSBdo*mU7hzJCVd`gLCRpjU2EywlSV;h~EYmCKS}LWSbz zS`}Vz?9pv8R>Ll@;_sTn#$THqwfRNYl#D~HsBH>cuuTHIXck_Z^`yn04Ug^G@4rWk z&ci+9pPd=I=n;El>2-0eNA$m{czt6R{Q^Hde(7wzn}?AQjHXcB9uPxv`4c|+dPVXG z-s=s%In9K#zDrVI-zr~*ZGc)K0>(ps1ls5=%VVxM!tGbN{lNpL!d#bjTds2DcX}2p z!d#>2@#!$v`($TnWM`$ivocuK9@=^Aw(aw|kGcG}bKlC1aHT3&`nA`;KL6GE#jd5y zrS6B@L;H@0xy~nBcD((@TW@^s=4~6`Mz}pHx943)m@6?T_p039cMpa+yOCp8IeV}S zOEj`Hq?RyOYUCi&o!w!s_Q~Gz$lgYEZ)32jC$#r8NVNr#w!F0^!eL$R_TKAT=v&&h zH1u%$!--G@Ry3(p8s$Ef+jqwn<|++dRVr6?_w{>o3v-d`4z;=?%pEtLp$gwW{GjcA zTjan=^}xw6cglEHsdAvOb7Ag$e5r45erxkrX5YE_#hV{nELCT%pI9t~=d3V1@3RhC ze~rl%D-63Z(eAG4ch?Z*6|0DuuUKEjOiZ*puYTu6lpLSc5MgCyP^dd%n5#2zv>aEM zYkg8!7AdS%3u}Y>J41yhK%WJORPa_ogez0Ivb$CH>K5u2{YzaBtq+Go4&8BL2&nL2 z#fdO?(xB1&398@tWVb!CyIS2{y=Z$-aK9k5`!Mj%N4)&Ez%$UGcX!<@UMOCiSaK{) zK5PksyO+u z^-*T1qC3p>th*RR6_KJtYSE!3;Y0Ta?od(BZRm^&!AcRXP~{3ET!qS2+`W8nY+-B( zov!=QwoqkHm^*DksaCn__p=}5+|P;Bbf`749*ZzIpmGP^KO3p*RqJ{qb$x1G-;W_A zPOJUXk$#s7|LVZxZWBA~0PoxkbBCUwa1Clv!=m!w=KY(YqT_27E}5E$Tw2GzBg|DL z6dkON`6)r9@?eiuqZZaI?s!mgza&(6Bw44G3E{+%GMs4LU5Uz-yeovcau7xD0I_?x zd-z`4LR+M~O)YQxsOtN*->r?b_p9))y#L2jKaqYYMJ@;`{3{1a(V(Q=;Qv`^yB}_Q zI23Z==qH(>bxr=S>-IU2_Gw10lL>LUUghc+VFY&&Jh%3Kq*plTCF)9+TFX_z>Z&f0j37qLS-0>lKRLhgAZ(f!SWZw+!?HqhGZV=9Z?-e z!rW2g86C~-+85bXt?q)cXH3XlC}bXD=Dn2{;c$q2H~U`xLjK}lZ2n@$`HO*o#RMHI zvhLJ`xdTs1t0SeYYH90I&xhwfI3FrKi5e{e+6Y&qazzoYLFF12Ut20$8hn@$=Gsk9 zYE=$K+U;TP*prf~NJ)!Y(z0~$!($&D3zfjwJIw7(rltso6Qc;H)f4Po^JMGJck;fN z_vM1yS&z8_^oy@$eZAnT1u(^^9>FQbo=Z46xMCf{8N(Ip1kM;R(e5PuPC}Gdtv<|r z)%rSSVxrvz^t*sI#drgz7zu^Eq!n@%AHQsU1!-QkP9Y5@+MQd!b0dn^s$izqsG2Pahe}SR zG$}gu7cS}EgHyyW$lwE}n*A@D+R&sXLxL5|+STI`wwOC*cdte&+tteUNM(;&+4D$< z^uDC_z7*!Jn%#Z9b(5&C|Ccps7+JH=t+!rRw8VSv1vm6*X5UCR6q;l;`zDFVi7nsM zQrDqGRtsUpskKN()??T)DQ+GhhbplrfvW3+tfUWVG&9xR)Dle!jIDjmFU-TLPlh(_ zsZKfVS!*COmBUio>MHub;w-DzkakQa6Rm@LuBAk}TDOK_-S*#Xy$R@U*g z)%~aS-HMe6ixrXm$JG7D!d!<|hH1cl7-nY?u0`c=#1DZt(t2KPJs;-!){OXs+e}8r z=t;qbuAgz;Fv_=BCSfn!_~Gs{?lyn&5#+vOQH*Eg5bDhg_8Hr0k?=Des>*O~ zN_Ysj34d5Hhk;@K^LQD~yp{!YJV4VpR$86VEz3|)~gnzgbd4^bcMaO zy!J`l4~GKaFxRYN_w%pAZs~~&wFNS6YRYc7L^oZDZKC2)yYOV6ukV9UquHzHl8@P+> z_rZZN(T}bTN|6_^sCVEJs91Fi)U@Aw1e$^Wj{*Ib+R46IeBDH86t3dVGP0+VJR&d!JtT^hZVCFa2(*TCJ~8z$0x0_VT@J3)f&+ z`*F5q$G*t+3Uzx$XnWOd?s4Asue|Zjr@#2=pXOCP$=?~tcc}S}NPe}NU;VTE+F#^v zmH!;Xi~Eb7sBx_+=R<>orVO|G&xz-95`L^k3xD5|*;q3z^YEueg-qrfpIicCxMelg z7!Tubg_@d=#mQ(CNqE?paxj``5*}Gh9=QFT@JqrYo5^Ea0t2{B!ebMYhY<=WuOvJ; zCJ#JInD9%&BZtYOFo6NwJ~fZMPsJmbDF+_?N%$p^gUtgE7AE|X@W^AzVNYNHw@G;9 zGkG|kibnyHN5xa|*v#aC=cN*UN$RqN$pep9CH#``*vjOwKY;<m4kj>w+ax@8F?k$*DjvI;JdQGW;5oL0U#xE^ zWb(jMZVA66bzyv!0M5tny`Ue#%GS>S28v_nrtGnUQ+9n+qK}>e+lEBmWrxG#lWd@zz4;Ng_K2qpLfJK(q`kmAVe zidTNsZ^z^G(cE4<%dZs~*fKopgX4$>UZ7xjc7onU3;LllqRYj5MDMIWu-SN5Kw}ly z(ap;fUXT4Wo|cF6pL*_|#N2K#R2It)?Cz5B@Sh#dM%qVYerigZ)F15Xhf`^AtQ-FH zxc(H*fbx6#>UeZ9CpWyqWIby8?Rr#-f+FO!7EQUjtn&p zo$jumQQWSBO^u*(X;Ntr6v5G%+ku W!vuw6g;=%iBGm;NyfswDy?vm_pz-Ss@R`A2y3Sv8spzE*8yziL(W^M-* z43wQ6je21dUT_92>$n5F!K2hq@ovdA*IwN}3?p^0i@nD+ZEvYQray&)pkn*chL%G< zC6;qtJ9XK9u&IflU;*tO?ey(2tenp%z^xqut%tLM?M=QL$Lz8D2`{|);Ev03!Rg+( z3>nXM>N)kVDfJUB@8qlXFm(W(v_r|L1LGE0J3vJ~-eZ)kRZSDVxd}y&y=KDmM< z0QOmrq`=>yx5LzeHU@MmP#Ew5efc(8P4spnl6JEpE29Oe4Jr8$yyauZSB=1>HlPmkDEeLEpM}xsoVt=^tD$ygl;q@YXcPM5WW4yN^ zBeO9;-IyKAh!1e;^LRwI8CCTZF$XZo@(zUReyWsCK`l=ilh_STYo63I^Cr5i5^?-BK39^>60 z{cRw8QRxiSNPFiewl1vC`*Oz(IpbG^zXS{em8-1?$GtUl7MFza4aZxSCp(KGJL}Y) zb&;Ki)t!fL!>cavt_&WUy?-uvr2hvuLUnkG1-5GRO*GtQTgVL_I2G*XL-vULuo_Mq~R7y_Jt@+8GP-$#uDv_J%Nn&kjmB=m?E~78B2OD~VyYH5gZ16P)6*fx%+EMU+hMo>FR^js<2Y9yACKmakGs9X ztPA1%@$uJYd6)h~{suNp++dNhP2IO>#QL@i?hwd$!S`JK_!j7!mS1Lywyd6vUtY-B zn)Sjb6$i4M89D{xVS)I1+xYl}+8xoXfw@8DkQRZocVYFdpeZxLXaLEGG`@Ce0W(8wt%8G@rI4vWE@Z}`+sh!{#CY^mEp{I zcj{Lb4fuE<&tl8J9r&qr&%f90g0{kc&asDb9Jjh2+p<4ipRFXh^<7mm4t2k9^3M7b$|BEXU^On Xe*5ZMS3mdi&un`>-eR%s)dc<@tLPR$ literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_FinancialData_Window.cpython-313.pyc b/__pycache__/ProjectDetails_FinancialData_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3a9c91a71e1b2f836fd28ad3620d837f7a2afcc GIT binary patch literal 37272 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 V>W5c<``S-!d%xIbv6btr{(m@J4pIOB literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_FinancialData_Window.py b/__pycache__/ProjectDetails_FinancialData_Window.py new file mode 100644 index 0000000..d333163 --- /dev/null +++ b/__pycache__/ProjectDetails_FinancialData_Window.py @@ -0,0 +1,382 @@ +# -*- 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 and do. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMessageBox + + +class Ui_FinancialData_Dialog(object): + def setupUi(self, FinancialData_Dialog): + FinancialData_Dialog.setObjectName("FinancialData_Dialog") + FinancialData_Dialog.resize(1440, 1000) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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(FinancialData_Dialog.accept) # type: ignore + self.buttonBox_2.rejected.connect(self.show_warning) # 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): + """ + 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: + QtWidgets.QApplication.quit() # 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 ")) + + +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/__pycache__/ProjectDetails_Foundation_Window.cpython-312.pyc b/__pycache__/ProjectDetails_Foundation_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6ffc0d04a75c318b8531fef4ddf8105c2a2c8ba GIT binary patch literal 54201 zcmeHw32+?Once^cfQ^eMNCF@M4hAF+k^qQ<7YJSec%L{ZiXcR2fNlUi9L#_{1CoG9 zTRzsNY-eTJUQ2S672#rq-7QaNNxiLi75S_y6yI|M$PMUqAdsetwPxe*fdriQzy0ipBCD@rL~} zBgCieX%@@d7SST6xhx}TJ!vE9J?SIX9%~xLrMoglGJ7&r+Um+0S=O^G&5~|8W)U;K zY7sLf;RCBCrzcy=5wosVSuDx~@GW!Y_T;8pB&)NKWynJ**ICT(81(_@fxn*RDqpE~ zXS{w8x#?L6DYEsuRnjWw7GpY%mR2%}X%=avvvPh4CIL$XI)U!7KWVvhTWp-g!uYuS z@nz%7s)adBT5Szyb@;pJY(b*XRAqm1;44xa;J zay!mQUawj^tC$Yg4ABbLOvIumYdo_$ zD_n3v?mOxkbAxQz?D zx+xcB%N$d#5}pU6#9UJ@%0KcrmjO4T&PL(3lI04vurZp4N#T|+E*JBc7&l5+bKEGd za$Z_S`E;u|v7*QPoE0$+8NEmB+Iu!+&I5{hL+*kdt^#i^Ol|BqP#M?N2a)raT_tpq@_O2((#bt zptdwrE)@19oGY;x(;myX0egMy4Jl7@tr(RQ_NC$manpS4>_XWV&`UEO;0- z+M!URMmxrtp2bOVUF111DyILXxGu4@JX`=5RX;W}8^*0wmw6tHg1SOz#U&Zqh(nBr zho&{HmZgQ1y^Q7|IWylv=6;29F^ZPSxS^iw<7!CxD&yiInR^A}UqCBEl!$Rd?)5Au zLoI*S{yg0&F{wO^8dIk`hxijliOZN?s&DkOcpj#xrOLdO`2w|67AIv1s`eXJaln@- zE~-XWMp^w;Zf!<6Wk&H(T<^mbEcNFkyx!?#PkBh|#M?PTEvEa+#Gbs1<@CApWEazn zhi0A(wxYPqQKz^l)T@$0y*eq>37t}gx^9ur9qMk5Hlr4RI>n`zk;#G4V{~1oxG2=O zCWZR8q);bxN*U^Vm?b`UsE;zuc(?%6DK2x=DJ}~2?Mb0tlN9QNP8@Y;)y{IeFv>}f z_!6}C3mEkzo?Gc_ekt@Hi&J4G;^6|Yr?@B-YokzHxE%@kT#{*@o7j%}+Ga}a2yet< ze+#f9#YNSj=s6O_bul(~izQ~ZLvc}<)p1KN%J&KRT#^B^L-RF-d=g`}kMTgt>x|~1 zbDYzRGQ72$U~xPYcZl`u9VFK>j`Z7{k5MMl8x|p*!l04sWUw4P!X)g$xM8lOuU$jg zzQDBSVG=8BO|a6m6UvvNNnv?G`^Mked_HDlRC!I-jpA5|s*};987eOd*!`9lg3v^Za9QhB(57Lnqj?6^BgI}-A_B-6Tt3gccR~{QC!EkjTrTeZ`P<> zC@~GFfU&mz24$FGRnjwzr)h;&@>my_N`G|ME~df zJ)Q%j*!#7(ZsQO~YLwS=K4~38!PYWw052@AW#J(6cW;?_sF)C`e%_%+B;`~0N zd6rZq0f(0oq3Y9aaT<>$sGAd?&EtQuEm!XEyYX^xv;5^d=N_v;kJWRp^+EfoX zJKqE1hGG{( z3{d=sjOJkyC|&>*Us`e$DP7G`q_|R#B9#k;;>DDq*v$|F6dy2}he@FL@^e9v($ySA ziYxUfQn^qlUP>8?7Z_rI;vX=Yhe@E=^ITA*bTvnj;z~V=R4x>Xmq`?p8TG%!5CarH zWHb+xK(SQpg?^D3^~cF!8PAPTV)Uq+(u=anD=D*z{&p2~{zptJ9wud#D{-wt^c}X4@*guU9wuRxSHV{tV$=NjK}u(cl&KW&nR4x?ugDJzlmuU`OGRt2y=* zmpS$nSL(5+a-pyvBC$`Vk9>vU4A}oOM)NQU?1!IAA4%zIjy=U?jy=Uip(3XYl`9MZ zK;=h_=3!E(JdZw-($yRlipv}oii<+UnKD%R7y^LGA2XVVNuY8S>WXX0`$$Sxb5tlU zb5tlU3YC$Rq4Fw208n|%XdWho%9ZDU3Z<(#DioJFDijxmikn2Glv|TgVC(NQnum-V z-hMh*ETsH1M)NQUR6OD+yq%-p%}_a*TZQ7Hta6Q&E9#wPGNbH%W&yCuzhpEIldwvu zs62;vGnB68zDjW&jwu!f&Lk*^M zHAj)+GOxiDSL(5+a-p#QLdvioW;g@(GmPe864-z7x%9!5uIAWNT&YKq%7sGlbrQuX zZhb}pHi_qn`Z*Yoa+Y!NFbNb7Kp(vH@8~F9%~7Pd%xf^kMWOPgl%e8e2mmTUM)NQU zRK5)D>e8=tQM#I=LUEa+LUB>3e1$|Mhg*|Trgi12A?05)nukfC^3~_k;!(Pqqe5|| zzQv<*p-}uC62)Xz##{_B!21cKd6)!>UlYF`cV&#q!Q8hgF3Ku*NUJ2%4~#GifK|eb z=3x?6IRGur()RFfw!U%pt#HtNP^3Nz<=hJW0YwfxNb=KUl`59BoO$H_+|0C z;`iq7z6M>W92S2n!GGiWF)GUTR4%_{DS2o(<&UtO^H)42qf9tHW9fJ}0sdvG+pl?g zMn#<&Oxum}!QbO~8T{gZF{wN>^$Y#9GD!Iw&c~=E{31*IzWBCykn{^GhyTs;;-SeN zg#W`+GK$-SF2}dYzdmKanXn!3q%-+ETtH5lJT;@lZ42O|+Ng!_WwTs(Xj(F2#mh`X z@|^B>IM*|J;@6n=w=7`!Ja)&!q&)9!A6(Avc$kC_wuwvsm&54u=#77Mh(3@0?{h1~ zEwSl508pVUmGZwp7AK!-jt-+t`j4$F9S;{^2dXASoema9Kk78dsK412hfhDjmune4 zT8P6(v@7C!G38suWipDNA*7zeM2xKBiv?s#a_gBm3+Di7=@ z3b|}XEg+9#mX?Qc%7J-oJJA2MUh~2{P7-jUdaZB~@pAD~?_m@QFyU=CHe`B>+ zj!qQzI|i-}Dfq`mYX&?nkJ4Oz^f3OL!$Hhk172`QF8>qg9w+ScQrncF{;Gzi9orik z)&Hx*mT;!a(J#5?5RSPY!DZrmZOy$GyprPW^*S7GZ|~vJ(RTQ=slBH?0}fa3dBroN zI7Ym^=f{1+9(N>Ytl!alTJG=l%08)P6qJO&t?KQ)=G)ol7#(%V1L~hf_4ao7di!N} zZ*6T|g8$k*H{33dL-f|Q%ia;$>#ZGi4~26^$GpR>V?Lk9J%{bc98PcKhgi;{`QPYl86SJ z_~GYQKU-COwF+`R2f43)XIRio-M2HGgMYnOGj!OPs9{I+ZT%d!`O$F=a|nENT;m+n zr|7u)y-`W(cg_6;B$>k&T`of4nKpao3h=?l^8_?S3)mffyC?c~hx`>38gZ~j`GyvK!S^+qyeslQ#W^N^B|D||desl-^3$J3X*rhsqt3#2mfH*k^3Mr4GuQS~n<}Rb! zkzAYRzJ+=GFb_=x14awTRPx!X1O zCzt?V+OeqJ@@_-%_sEY7cYEhVOqs)SgzER@=!nL-SrpiTv4Tr>OGiZ67tUc+UxVBY z^5z<~=h(jW)$f3m8dWd<8Ro{5HyY?BxfwK{lhh1#f#88Uy-VJX!heh@jAm$*Poh|s zOT*mvA%(nL%}Osyll(`@cL8skvJ2G-5_sspd zvG9B3UBD{;1o@3RHyK#=hI92Yg0&1c^-(-fFC+RzaWu?TsW#J#3O3W}UGhE5{{md* zY?v&&Uv0|KpKv8a{##T(1M`l@qZuG{4Foj6h-M6d5e04u8VJOOX)+ z_^LzpN_`4WY?OVNHoK!O(!MGO@qQVgeZnV=D2FjgJ6?|9J&yY5919wq;7@c$6qsS9bqriEw z0(AKtZqcELS~71qv(4r4O5uzy$8{;Z5)x>B#~x>O9ECBcq+CZ?1^5_;ZZ~~rB==aP zo~@2Jmf?w37m=}R*dcmugtNNF+*c*dGtqkBf4<@H3Xpe37I_5LCpAA{89C+k*mOav zJnt}!)NVKww+!RlWj@c)5RBPiEO=~Ff4}L_hl3---m&4{b8e|!k*`a=?UMJZ z&okP4&MP{G`YxQP8F9dn9ftj=Zab(UyS#mdS;|G(4YsTu9arn-*ysg$V&etbm_qY_ zS||@b#m3gV7wY8emM20{NT~D+l`~cQflCP6nh62}ORP=pB1dDA75K{X+3OO+mq?$DlqlWk-U-Q9Vjc z)DRR(^%zy@^+BQf7schF;yQnE-OP@|f#O!sY6T`*@y3dfQ0^DX?;X8=dh+zt>Z#7@ zHPiip&4+_RYeKD5&P{${)7^}qutk?u;TJ0IUB5p*IUcHP@mID4h5h=ot$tzahr1r_ zeYiKY?T~-lp`dVBf40RhfWb}%g;UYFzPbF3<=-27`}SM6Ke1RUPFO#+SPD;CKeJd) zTRW`3#^9p$ms+6SUD58YV3Jp@5+-@odKDuvQ13k2od=U-k6LvV<`)K)x+|%GM@*&0 zxPn6C)5X|xAg~!dL(i$kFVs8$xlMYER(v$N9);F=0Eq9Ydam)I43cDYUNAzm6-p z4wcNsl(}!@h6DueUe^7*$-D<0ky>TLTBRqzY-a59qV84&g>6quD?_D?{?f*&_K#0} zbShAK2raq+ctgSlzpx=B)cS?m2iK;`raGq6f*L6lOQ$V?#tt7}M5%LzDFJzcr>?cBF=zngz2^NEm;Rr9^f_w(P)2gk4M zM#nF@fG&H{+KYaC(b|uGjDdP5Yj-jxxoRE7$g9@t7>R*;H=*54sE+>{IDSkfFRPhc z!pE1buOiQ7>mc%Apx%vWcO#g@WA$OA$Eu3OK)t(-Nj%ouY9?R6+7(sMozNB;;;@Z< z0G6sT#GzFmYz_)_hPW!fQ1xI_P}r-+X}GA>K@3&w@mK7DCr=B@LWR}-Lg0P0?cugS z;Vy8Jd`SOh{u}vPBX<|tmpZ-XPGx+Y|51LR_&`)+$Sauv()}Op50o4>g%s<|w=Zbk zgALcW74QK=DgF+jPKbihx>}cjHRf*Fy-T4j&HgRTp)KwHE$xrR(9xIuM_&#KmyGV7 zYTLxxE5>7ftLyyMc0oRuYP+iPj5YE8$m9t0X-3~jwkb5|YQ&qh$gwrwP*YQoqgqR2 zypC$87NS|=Hfgh8*nID$`@NIBQ{_`iU<>vnP;{-qO85|mY+7q(D7&F18Vnd2k=>fS zHPt$uHr@C*Jy6*e6xtK@O$%o`l-#KhI9UgbAb)#v4<^GeX7te6ov#t9VnaXnl7LA z2DYH@8j_%U&s0W+*C9dU!^pe0gTl_IWm`jKJN;!lr^L{%F8{8sKv_3tf-@v-Iu;Tl z(~_2lEz{QNj>qYb+XCB;2Za-cB-{PM_L-WPNF2p~IQU3@D2Hkf`D-K7875U@VRIaH zoQmVUps*3-YJ+QRJR@^Fb;!)`SvhcI8x+b6nqbox6e>}MIs;QX>dhhhLBIWAP&lLy zf@Zev3lz3QjesJ9?d_A>A9$fDuugXc>}pHo-hW8~9`Exy@g=?LqsaGY`)M3Y!y`r!!zjdna^awNxd3q2wJg zD3nK)GhWg4L@HaqhKFVzJ?81gEuoF|{*B#en^*S*2}u1VKa z<8=CT+vC-NiuRyz#E?W=aM1l57Xh?YhzHAq!fwM{Z2<^8QRv%+CF~13ZG3|pZC^i1#ng`;ek%uFJVr;GAEJ|V1uf!~(U`set z!5T2NG30Z%rIfvhRteTirkAKyQm3&CGN0PNGxH^E9OJC%CA2hJqsm`c^`PKU$-|OB z;qLftbn2~vW6pcK?(d!48!F%9FUMs7y|v~;XPWDrm!!4L%m7 z#?rOWpEq=#Lyxg2%P*Y+;2Kh`e?#p9-=o_PZwEH)Pq2bij+0OrET}6;i5g(G`l7w@ z4+<6P9G$N$C0K6yyy^2CJYm7kk7I{)SO(A*suC>&#OVO}h<>qV#?_WBxZ1KrurIha ztX5Ys7c-Sz&>-aRI{zYjsHo9jgzwPLd~_yIg!@aDqvr4uRC~h>v$U4UmMLqb{N-#v zgqi>+n6Ne`Ps*W+W`6~H4Wd57x4|D)hqku+x3-72p7d`$`IEh&Qw3@t3#7Q!p_FZ3Q4<)qwzT=xcPKiuZ5H-P6oty_{blu`vOTk9Y$~d9md*;L z?_7&49I7#8v-o~BC~VdxY?_5l4A~*u4!>Sl-v|GJ91 zzWcW)ZwJ=xghFUrA-)m46{2%WTiCbZ!afSh#RT~F;KOx~iXIk)s*m`qBhv)!nSB;! z(=d~6^p`h=%A04)84xNb7V++?$RJpaDW8RxP_O`l?V~0Ei**n7ay0nu4I%rUSvxa> znjV*_v^^KBS(<5R3v6u1HaoIZI*Uu55qFrkRGO&rs&A}dy9vH-kAC%Dq{>wD-mI;h z!=y-!Guq;SzdB+k_7oTJKYZ!YrH7Y7HLd;{{SAZ0t4*j*2Zb|`A!DI3F*+nmr^ex$ z?$6cT5wk+^%!cjSCJ3#@pfkYI?<{UW+Z(DnG+V`9B&c2k;ZO=+%y9t1NcNgp}3JDFf0`6CV*M_0SQ?reyg2HLFFbhfe3jr#2F=!qV zYG#F+nc9Ot6%N536^ms6c1i0$+@g$ImOuRw#J+9u>CZlhB)Oe-E6s^1VtzjT-ib0| ze*E($;HEzM(?1x659QLF*)i;JE2c-u!0m}KKZ6E&F^}NZ@RazlGUlj(H0P?tN{G^K z&3ta%e(AT;4Z0ATUWMK7$|hd{lRcx0esOl=Ci>jRoIDo@N9Lyf}ksJ!6m%xVq}9V?rPTd ze0Iw|*k}Dam}Cl*ST9N5aQaz~x?wl6{Z`wt8_tw&N&|h`uKTT!D|FFGz5Mfo?F)xUttL>axq)Uc8Ezd{}G{s7XxZuv~G zthax6^6dURdnm8cpI7gQ&+Q z9Jnhu=10|KLmoLX$>G+7gQzhk97KIjR1Puiy{y}5KD`AH)8XSv_^1SbShu2TRvUa0 z2)mhW_*Iz=w`1F2`#Ji(WRukjf%R+1<>`00Y@)1wli^UtdCh|T*4glF3wS4f8IkWD z>+j>Y!nHA|R?}NS9j?nSk>uTb;&^0y;w+AIsO#riBh_9u&w9+7i-2%Ncg(t zlN?KN>H8(`mV}BL{6!6cqNXP$W$*XB+ZQU?<1g70C~1CDxDhu;h6=a(3%3Rew?8Q; zeEZB>XF>%P{(_1?K^5Mgd+S`NpvqrR6)4ym%m2&#Wh-;;WPG;LlD&Rr{niKVKNadV zzmH%8dRQ#F-&cX3K>wvi;)ObPg!1s)fHu`brCV;Li)qjcMRGH=_ZFw1XQLC&T%FX3 zhzu(!gYxV6GBQXRl&8m+kx9zHUG*`)1ZBx0Wvq@70JjNbEF)#;{U4W=K!!ldz;Bdd zehFk`lQM{p;S~GFlcTqlA=kU{NwkNg0P0C1VpQqjga-X#Z#@WF)FHw6`x*3ac}8 z%{xly62IT2@jbpM88kL07bSzPGp83NgZ8tti;_Xtne&U1LD${RMaiIjwR=%A==Kl? z-7&ue^`Gt|UXBp}x6VZVZqsX?v5#1&6z1<`q!j$!(|~mCDSX@Skd=vba750i#|s(a1Qw1f7$0!qSHxN8#(Yp($4zL@TFTnbwRK-Q zf5djY`^;(E`3tS5Pqf+WHFb3t8{6vY+PmAe2Tip*>gtZ1wcG5&KHq3_UEPfvH)?M* z)_Rnoy6(=p&LeF#!@dz$(~df?Pk~8st>_c&2Xb04@<7Y5L2A?3~!8-fx^>$m`ffg?w!(hW{@P0ccs2lKlLD1-Sn@hI61`Awxh>#af5t5p5 zQLE;L;uzg$yPeYl83X5mmO3r7evdeAQ=|6VVJh$T)eJgDWY>6e<(V#6JOf;8?XDqP zL*+j0DI9emHSeiy*g5Kp#N1F15NK}Nv4bVS44U2QsS5kBaHDz#BkFkuHSlS4^N!J* z`)rZ>eh(Z;G!hl%f>Rr!A{0EPK#Qrl=Bw#pdok6IZC}MPXl*>uI@WZ;@-{S&FI>Gi4=aZrSctS4y8(19 z?+aHHV+r(r@x?5P=1r3AgxfocM|tpu_r-%n8&B9O93#-xc1pvJe%U2!=hmo=z>8QS z7umS3e*|Btc;JhN+X?akpX@i0GyxzT~4u_;T zKE)yNL>?UJq`o4OeBNgD^vQ7c5r^U%z5%lh1y@2RR&}Kwt=Mc$xm6XKjcz5LPd&i^^2oUoIx2cyog%7?`VZ+0fWB7C}bLul=G z|Jv=LwY&Ulcin+gmSFD??&*10JkxaS$Ay9F&Y;kR8)Nh>`;$2{+YZbex)QMU1%+4f zk^aTuyFGB$)6D*Jfs*q zvxhNeoqjn`ax^F$Q`xkAw79o+vUq0u!A~qJThn^rEA7);b+1*~WY5fww#SzPTRMV5Cy1Z-Z05+zfi0JULXVoa_DL zXYy?7R8Q0C!qas4hsSD~X)#b5^Tn{Cc<}9E#J6JDW)55ol(Lem)kED}dw6nUrs2rU zv7SIB?3nDu>|!52el1W*%bXqzl(PMV(FHLmIZu!Cfzl}Ka2w{rOCLW=XQ1>%P&f%i zfL(X$m(-+Ph=&HBh$)6UP;6uhi}|GoH{`Ky*+rC(l_c{uG?6U$d-uKLyHyv&AAHy+3wO4HadF=mZt@%8nc ztzH+-JU`yy+o^Vd>ItRV-l|o&qeVSnu+QuBC~yk5f*VG{E7a7r9k6o?))#$Vbz@gJ z%cCBjs;Z*=PfTa^j(e35QW>z^bvs->uWVL{B;vj6d3TH?+WuNlb zz@q#uUj7O%xKZmjLW^de14Ez4`TQH;SNygexMzKGmIGC~VNk-o5ZC^kB brQ>(H-n{h2rEgyTX-3f}D=Znss?PrpwTOhG literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_Foundation_Window.cpython-313.pyc b/__pycache__/ProjectDetails_Foundation_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..435c012a1113428dee670e6c470c474323968b22 GIT binary patch literal 54909 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\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", "Componenets:")) + 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", "Componenets:")) + 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 ")) + + +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/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e5a638c8d6956610bc39f84180bca836c44ec88 GIT binary patch literal 41090 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 Q*MDg#dcMtMDOLsk|ItR*qyPW_ literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-313.pyc b/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bc78f59c10337bcaca51dd5f6070532a3ce6345 GIT binary patch literal 41586 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 E|BmyzZvX%Q literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.py b/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.py new file mode 100644 index 0000000..5b4d907 --- /dev/null +++ b/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.py @@ -0,0 +1,420 @@ +# -*- 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 + + +class Ui_Maintenance_Dialog(object): + def setupUi(self, Maintenance_Dialog): + Maintenance_Dialog.setObjectName("Maintenance_Dialog") + Maintenance_Dialog.resize(1440, 1000) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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(Maintenance_Dialog.accept) # type: ignore + self.buttonBox_2.rejected.connect(self.show_warning) # 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): + """ + 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: + QtWidgets.QApplication.quit() # 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

")) + + +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/__pycache__/ProjectDetails_Miscellaneous_Window.cpython-312.pyc b/__pycache__/ProjectDetails_Miscellaneous_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6406c8d6af4e09f1f61fecebe1c3ba1520ae3358 GIT binary patch literal 52237 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 literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_Miscellaneous_Window.cpython-313.pyc b/__pycache__/ProjectDetails_Miscellaneous_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1c1c7e25253bff863297342e102b027894ecd19 GIT binary patch literal 52961 zcmeHw3vgR!b{gZq^KA5ATB9c7A+VgFUf0x0CfS1 z5^b%$&URPcb=I3*Z|rQGtfX-wDYJ>_G_AGm&aRqvT4%eR5(IYOph{+E(&>bqPU6PS zq|J0XJ?CEh_%FZ%T<{v=O6fxO{r`LKIp;tB`Og2i|BIg$6%|zS9#qwQ?Xc4ncSdM2YS;w=L?BiC&YQtyQCvuMGD!DdGvBho?t(z=j&R|}% zRpV7w3<_dy`|}oyYsP}qc_;Ff{6VW@Jxhd#cFSPCVw|rI( zPrqv!v?y7kMadSk6swrM$)e<-&6Qj+2d;TyuCfACj66FaJ5S2KVae@wc_i>z>B4p`hVSpyb_>aX$Sj-vQ`yt}Z9J#(yKF&=`a=EA=`aYb{E_LQY& zvs_rq=x8rxD0{L8vK`MdS{{nITvif&OE@))4P+Z~ZDzDQ)a7yuf68KxZ7jw^!&Hb8 zV~~oSOJ^~|RAPuS4yYXj&ZWbEEPpW!tOHi6BQ?Zz- za#<)AiL1obi_2w_G$>D6!}F7bi?YHqoJ(hg7M2?xf)&X8GJdGBVxla%_=aFQ)8Y?a7)mW6i#x)kjMOk?Rvl7&_ybWk)mf>Lp5my9*Ubemkv?7RW~X(HYOLeKFH!c zG=*rPis+Wbh$czH*q)STls&hSmN8eO>|j;^tvgwqho&sER@^3TUxpfm(%0D6DXzmj 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 literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_Miscellaneous_Window.py b/__pycache__/ProjectDetails_Miscellaneous_Window.py new file mode 100644 index 0000000..8be13e0 --- /dev/null +++ b/__pycache__/ProjectDetails_Miscellaneous_Window.py @@ -0,0 +1,566 @@ +# -*- 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 + + +class Ui_Miscellaneous_Dialog(object): + def setupUi(self, Miscellaneous_Dialog): + Miscellaneous_Dialog.setObjectName("Miscellaneous_Dialog") + Miscellaneous_Dialog.resize(1440, 1000) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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(Miscellaneous_Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(self.show_warning) # type: ignore + QtCore.QMetaObject.connectSlotsByName(Miscellaneous_Dialog) + + def show_warning(self): + """ + 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: + QtWidgets.QApplication.quit() # 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", "Componenets:")) + 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", "Componenets:")) + 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

")) + + +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/__pycache__/ProjectDetails_SubStructure_Window.cpython-312.pyc b/__pycache__/ProjectDetails_SubStructure_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e58b5e19b130db9d43c370d438768002eb800345 GIT binary patch literal 50769 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 literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_SubStructure_Window.cpython-313.pyc b/__pycache__/ProjectDetails_SubStructure_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec6ae8a2869fcf10dcf696060e9336bcd023483a GIT binary patch literal 51460 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 Wed+C&zINfKIi(-3HRY74+WtSM6ZonC literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_SubStructure_Window.py b/__pycache__/ProjectDetails_SubStructure_Window.py new file mode 100644 index 0000000..bdad583 --- /dev/null +++ b/__pycache__/ProjectDetails_SubStructure_Window.py @@ -0,0 +1,546 @@ +# -*- 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 + +class Ui_SubStructure_Dialog(object): + def setupUi(self, SubStructure_Dialog): + SubStructure_Dialog.setObjectName("SubStructure_Dialog") + SubStructure_Dialog.resize(1440, 1000) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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(SubStructure_Dialog.accept) # type: ignore# type: ignore + self.buttonBox.rejected.connect(self.show_warning) # type: ignore + self.buttonBox.rejected.connect(SubStructure_Dialog.reject) + QtCore.QMetaObject.connectSlotsByName(SubStructure_Dialog) + + def show_warning(self): + """ + 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: + self.buttonBox.rejected.connect(SubStructure_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", "Componenets:")) + 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", "Componenets:")) + 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

")) + + +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/__pycache__/ProjectDetails_SuperStructure_Window.cpython-312.pyc b/__pycache__/ProjectDetails_SuperStructure_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c37cd94fe05c1c252dee2a3cfef87f6807ee4131 GIT binary patch literal 52197 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 literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_SuperStructure_Window.cpython-313.pyc b/__pycache__/ProjectDetails_SuperStructure_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..788e6bdddda97f899484e7576a9e30d17cc462f9 GIT binary patch literal 52921 zcmeHw32+-{b|y%WRFfhlS(izj5J>Toc$1PyQL=c5qA2PhniMUIHVlGIvMmsx8=xf8 z*4X1oX5<-XJcnn-nK(O=*C|OU*O*SSYb7<6QKnK^Z#LN?2)LldDyd|5Yb#7`?Rv+a z&0e*&?{(wH-vA%bAPh0mWFWi$zq{Xi|NGzf{-ghkpA{7qnBe#O$4e)F@tVo>AMu9$ zawCz?+OkZh@0moCn03n3m!)L&Wh>czX2op5XW6H6`f`cUTHHqeJCNXCuuf?qK zDr-grF}Lk$lgTk>Lh8Iz`AYtX*|wP_!b7WRB;U4`-?LZ?q>ewO5csxfasAb5YKhz` z>qge-?$(d2x9!nWYVnbEOrpg!vd&hzoPx=N_xZN68GOEf9jVwPeSv$vU)9JINTE9Ss8Ps~-;V2a`5A=xg@OiPOBQ)ZmL8AY;p%MO=k zVre~yac1jM5Fn0leRfpA1ctI~EJ+=an8h;%6cKoJ1mFmwxg{dkf>N0K=!nD|F6Ck# zdtpNj;(k_;z-Wg|uBfablTCHlSaMrY>^yxTlvwlyrL(D64e ze+%XR38tZrl1Rzcyqw#B-K(|k1ei=WsSr%pm zNdFj%^Dr&T6hp07B5qr58J$OxU8@zgS(LuUH5SE1S$R9N6126v7tqOV!(!lTMpm{m zTA=J^aUP~+beV=Gf!R0-J4t(?F%6gCz^t`pq4ET->I=n$oQ21baM zF{z++FN^aqEkv=muv6T#9MJ@67~7N5jIyVd+f!$m^lFs7%nG1&AB*!aEz1;(rDEAC z)F_m`#=cH*o#r`aF@xU88AiuLj!FVsQdsWgTwfT=N~Q~>e~!g@m=>1hfaQ)=z>?C} z7)y#P^H@@;P*_%E5X<8(?W;aUQ0HWhG!)wF+2L`WjI`C8&2R%O z2U(nlX<@l7!h1sh6VV{9lcV{9lc3Y&u@HtBt5Mn_Wtn^BGl zi=~ClAt*Pkt59wzeT}i9xQwx(xH8|`QmIfL(3U|DXk=J}2aK^e57YF3##QkEN?&6S zpty`Zfa1!$2T-X{9&niSfb`nVCWbb6z&MNZFf9*gU!}H_(%0AnC@y0UptvY(j${y< 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 SzH#xBoRUx1n{u|Ps{ViP#;qU# literal 0 HcmV?d00001 diff --git a/__pycache__/ProjectDetails_SuperStructure_Window.py b/__pycache__/ProjectDetails_SuperStructure_Window.py new file mode 100644 index 0000000..22ec97b --- /dev/null +++ b/__pycache__/ProjectDetails_SuperStructure_Window.py @@ -0,0 +1,566 @@ +# -*- 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 + + +class Ui_SuperStructure_Dialog(object): + def setupUi(self, SuperStructure_Dialog): + SuperStructure_Dialog.setObjectName("SuperStructure_Dialog") + SuperStructure_Dialog.resize(1440, 1000) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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(SuperStructure_Dialog.accept) # type: ignore + self.buttonBox.rejected.connect(self.show_warning) # type: ignore + QtCore.QMetaObject.connectSlotsByName(SuperStructure_Dialog) + + def show_warning(self): + """ + 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: + QtWidgets.QApplication.quit() # 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", "Componenets:")) + 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", "Componenets:")) + 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 ")) + + +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_()) diff --git a/__pycache__/README.md b/__pycache__/README.md new file mode 100644 index 0000000..89a73e9 --- /dev/null +++ b/__pycache__/README.md @@ -0,0 +1 @@ +# Osdag_UI \ No newline at end of file diff --git a/__pycache__/Warning_Window.cpython-312.pyc b/__pycache__/Warning_Window.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b69865239e2d005513ea04bd0f8e300a4aa05128 GIT binary patch literal 3407 zcmbVOO>7fK6rQ!$_QW_21Wa%si5tj|4WXq#iqeFNBx)#10&a*}-Uw2qCK3{$aNx+XJ-3H!ZM5k&he|!=h8m=Qb((uH}l^6 z=Dm6I-pntRm0p0`^MhRzKYIZ9iwtOU6^Zp@3;>S+4|paCQcR3VIbx2KGv;J~1M~sj zaToAT#4b54`Irm2dGe={NSJoAFuMyF( z({M-C9!W-qE2{Qo1fwFVUJ>|lq$-JWtfISM{Q#bQ1Q3WZJcv1X<}Qdi$@a$BtTW6S zdxnMh6;4hI>G624z$K;egrf`xVw%KYeU>FKIYy+wWkNh-+lnQMF)I_m@#LLLMO+4` z^b`QmT`pzOxe{6q#PO=dSs)TyHZ^hArU^V$E9so*r^x3syWg<>cIY43vXpuc`p1-S z_cnT2-kh@eloCIol--wmo(18mLBqeXSP9?a9bJ2bHP*<1TI2Cg7$$QeOc@np8C8|i z-O}_L$;vfyF$ryi-9?6^J{YbfH-?hoLq?^7)Qe*xN~nWe3K?!0DZ&hrNwG3^N3G)6 zP4Q$?cNbRaLsANv#A5ytA z&&j-nsTiJKK}q1T!47d#c>QQUF-+czVP#SaF!twI+wfuCOOlXQhp>5u2an9@N*P+Tq1!cRm2;fgp^Jbqukb%0cAwI#4PM1p^RP80i<%4^G0=1ekduaN_UoQ z2EKM!1tqV`6~qqqIyOTg?uJ64cc1lij1DVERz_13N@im8VjA_z!W0_qMam6TnjF2T z@Z5NO_=~m_C!|Md{H_RTUb=aFGHZH}nH&~of?MvQXu-zQiX|QfSv2d~gTULXOdhnzcJo$1kAMBZ9zw@s^-$Va>zYZHU*!ZOVY3E|+ zOUKKeS9@QLN4>D43sTBQHi z5q)4>8yMFIk{bRZ$pTE3#EF3mR}1jOYM@>ZG--jRg>*h}8r$O~GT!@M9oB2G{)ed_ zbB}Y+x=o9!^9`K^*tJ@<@8PZcw;tS{W3j9bgBlFZHx^)BNwZdiwezP6FjSfeX)v@< zQ-I+=iH6BPTYwS5H+^r=V1o`@G}!XYueWw-tz89p_P@ES5bqg5(qWqh+n%Lg!>)B4 z2e9FRKrV3ef$#zokAO-9%qEsUBj{Y|N-Iv_4wqm?#MjW~<^VbTTHzh^*4A1F&_0Jx7)}v#m>0$66X$+9lW*=Xz=6_PAu)a2 zvE1Cgd?uDZc=a_Ll|RHlgJG8ec`x!^urf8lGZxwl>z05lka-$(oRE8Z3#W;X>0W5tlnK zm~Hht*l9U=R_?`R8^M)4oK0UB_An%RL_N)Y&zaP;9&03&2A84H&{Jo+!UvX%*dxdr1yKv{i u+}8!Reub^p*&2CsRC@RY3RSbIFGFW literal 0 HcmV?d00001 diff --git a/__pycache__/Warning_Window.cpython-313.pyc b/__pycache__/Warning_Window.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..edc00450bc11ffaa9201215f3824083ad17b3ede GIT binary patch literal 3480 zcmbVOL2nz!6`m!RX*xY)LHBlkF`pAbTQ#03aFS6``aaaJmAMfp`8zw#>M09^S)ky zUyhU`+2|G%&4y_-Z=ExUN+Wsey{bk#^41k?zN}hisTHf?F4`7g_C6{i!bymPB#wmH z_7YwSLmQEJ5@q1@wSp#JQHZYTWqC?dDn@xL37Vx)3n&Jfg=G6K&oB#|=&#ZuByoY^ zAkhe6ivWaI3J*7M1vCd(-X}(VD(Ew?ijP-7m8NU~iOiZ^pLk%`1PtdA{+!4h+V#O< zgQV9(g^)N9aC}PY z47}XO-V?rT&=*3l)Y@xDlRLDs0iI6Xc zwBuUUvJ8FFxV^=4bBo+e+G(oRrE53Uk~OO=s5DAcQ(I99 zyUXaoyjLqHeLTX>Gd%z*@2SRuYLR8aDj*bV5ddhCVG&K$EydCdordxjjh{D&wqn3k zg?0m9vJ`z@A@d%lNn=x*S%NX;C1nZL&tGJQkwXAWSj__LUw}4EfbL~O(=7?iqcIq{ z)13sYP;|4RSZYC|SSgj%MN6Hhv67+d%&4e0MKMvCm%sua8AT7|XH-k^oTuFld8uMp z=HxQl47iPWQ>|PlYy&BO9n3J9yMiFliPC7XV5-C{nhGpmyripBL|ameQ%0$}06Sn7 zFB9Wt@sc^Ol;y%Ni+%*JX!^Wy>-6HX>qB)Bj83;*PN%^=#$wxT{vIN?iZ<|J2fu0K zH=hj**YSuuJZ9r#4@&E2>iEQFI^(3zJWro_GFwZZTjM`VZs5ed(R;}dl}*%TPC@xYhYA88LYXK>UW9IfM@HF;SZXTQoixhXq0<>W5f zxr_g}>dchwnX)revEk2E>Ug0k&J0|itK+vfQ+-Zq;CX6bU9Y8v!JasiiQkDkxX;FY zpD%s!(O*7#Jn350UF$zv$77pauiyLd?uVcLc8v#F2d8bEevql-V@=Io8}~kVr;df@ zj9_D7{YV{WzhN3Kf4q)!jPLs1Z{vOkpRnmtPyNO}X7?OjBY&`T> ze}TugAr3nq3+ys?9}8araUabw0rwMIpA`OFTYfm>{ZPR-v&BLA|Y6!Y$3ZVN@-_RF>eq^z&@g zOY(zx_Ar}RT&!p%FTXpQspqD?ceLNvtcvR9uY1DZS8rR{FgXQ&dh5pJrk0;ggcWUa z92(l+o(nrNoC%pz(5#}()EiFfEj#s=lNzy8BWrNNAVt_=et7fQ+Y^6(w>EgOj%S)@ zg~jyGqt6CMp1u3)+8c8(aFMVQpAETH&9!(o8>3j3bx3|$rg2$bFy^ZjMki$XH&vz5 z;B?9Ib&Z%-MblN?kY&Q|7teu|ESs$Sm1M=Thz6xtHD&p4$X&~GltrDehP~L#PH6s! z{uAnsjDL4B9QlCrBpILCvY=?XEYD`UY2@;9-Wqme;Z{bkMn?^|c)NJyr%WkqE}Jxw zZw(Z$kn$1@D~pRX)~M-TFhW($wH*rG4dWK&S^Q~4y{(pHG7eyY)fAugHe;Mz00z_~ zWPS|I%P_}r-ycTYDI4X!jrMRKa1Wy2BG$hgL40Cur4~+q%_leFhn%?ZJT9ysderl< zrxwqxp4(tWV(@utu$DTxI{VL#-de}e)yWM$`pNXi(`&!0^L-n9x5FQ?`6G2cy}`#m bIsb?AYtp@|cdmXq_kvHq>_U96YvO+a@>h#A literal 0 HcmV?d00001 diff --git a/__pycache__/app.cpython-312.pyc b/__pycache__/app.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57aa43aa14360fcd6549fed555998a1d87ec399e GIT binary patch literal 1369 zcmZWo&u<$=6n?Wi>s=c=X;Ua^B+4cdExx!nkRU=tj!LVNf(cQpmBnbgJB`=fA7*DZ ziK84+DN<|-N)bZg$Q9)u;D6vkpjFeQs7Rc+1*H;IPt2^n*b-0LZ{C}EGw+-4&HhxW zlt9=Q&L^#X1>jE^hI~Cc(P+<&Ee%~d(SGCqv-!Tf8iFqF;%f0PnOarS za$}*>@jb`pUKFM;HI{5Iyy1mzv@RA;HR@4J#}kwWucEZF;*n9}b!WWn@aAwLDej7X zP_U2-mf|8y?ZIv2Dm#XSd$55lZ5gU+T3+go4%@7kOktn4s2K<weYuRpRC~d||wo`s5YXZJS`@pkl34OE$Uy0|Fi-dsZkLka; zEg*7tW?RrMty)pTsT-c)kr!j7ncWG^QUIGf$>o|K?H;f2kiC;#>BMMJKH*rlHI_xvN_GOLLC3c_9rQqDT{hryMy8ZSI~F*Py;}*V>8lG1 zWHAc)aw}TD>O>*C8ZOZ=VJWub)wNU=f~SSS(-hqkNhziq9+ya+A7DUV-u8ttKTz$x z_<{(f3~XJ%v5F9S3U5D!OTWX#-=Opi>*)Obcb|b6j;8>Zdz-(g=l;O6KjW7l;n_nx z`~BRbx%tDn`5);6y!g0u_CDL+_NfSZEX@3 z2`8A-%ylG-yZTggS)^J{=~S3Ot>~kln%LI8hPf9ijr2t`6^Ue9))`)BFCf{!b=w!p zLY_oQbah-?WcprPSbml>Z*F0nEbM^O$qEkEL>~_(rKNV0JQ^*2G-J2VcI)A>mBxli z$M!qnVJi2GI+x*|v}aED!b2O0xOu$)I1wrh7e$-aX(M7;>kTFq?A%dvs>xgiA`OU-fTsouV zUu3{EOfcJ%E!9@78QlMgiFwbyNtYN!Mluo+jwss+90Qjk!~YS(sZu7Bo*OJ}2vcpA zY6rX2Pos=N9vJ-oNiLR-Q{&-8*qFJ4<}#R<5HEoZ!M1B~jmWPmSzUYEc++^d@!|Aq ziGR*>QvlBa&-tpJA%jWR#5;V^+roCDqkdQJm@>Gqo5jE8CPyj#zFHZr{LB~V=CoR* H4}I2e8J^z< literal 0 HcmV?d00001 diff --git a/__pycache__/form_data_storage.cpython-313.pyc b/__pycache__/form_data_storage.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0a335fca1f1c99df8da927e249300915fe65f1a GIT binary patch literal 847 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 literal 0 HcmV?d00001 diff --git a/resources/Group 4.svg b/resources/Group 4.svg new file mode 100644 index 0000000..cf7290c --- /dev/null +++ b/resources/Group 4.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/arrow_down.png b/resources/arrow_down.png new file mode 100644 index 0000000000000000000000000000000000000000..e4d726673d77e74c768a594ec717ae65f7c5b92c GIT binary patch literal 3992 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 LzpH!){rrCc2U1_u literal 0 HcmV?d00001 diff --git a/resources/arrow_up.png b/resources/arrow_up.png new file mode 100644 index 0000000000000000000000000000000000000000..b6eadea534c93810449b5ebc2fd8eddcd560227a GIT binary patch literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`DxNNmAr*6yQxblhci>D|8 literal 0 HcmV?d00001 diff --git a/resources/close.png b/resources/close.png new file mode 100644 index 0000000000000000000000000000000000000000..45e82a40c041d1f617d873e625a9a724f982b396 GIT binary patch literal 6536 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 literal 0 HcmV?d00001 diff --git a/resources/contact.svg b/resources/contact.svg new file mode 100644 index 0000000..5b860be --- /dev/null +++ b/resources/contact.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/country_arrow.png b/resources/country_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..f43b06bf05465d1f96cfbccc1151afef45bc42bf GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|CV9FzhIn|t z4RYjbP~b@p|N2Jwq3;Km^RG(N+=P-6W~&@Iy+dHqrFCx)@Y)&7xIeRD#s=3b@_{{t zObutN_ZR4BE|nF$E^sLFu&H&0-8qJWEiNV!+fw=*oc;Qa=&lmXJQ>QwqQ&{LwQa3G literal 0 HcmV?d00001 diff --git a/resources/create_copy.svg b/resources/create_copy.svg new file mode 100644 index 0000000..7a1c424 --- /dev/null +++ b/resources/create_copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/edit_button.png b/resources/edit_button.png new file mode 100644 index 0000000000000000000000000000000000000000..6c30c3f6c47bd739ee53753e8ca8b1746e0736f0 GIT binary patch literal 11726 zcmbVyRaYEL*X-c#?lw3K6Wn2NXCOfE4DO!bGB^Z>;1WEz^Wg5mU4w<-1Pv}<-hXh` zx!AQ=_f_|*-d$aLbu?5%5eJhJ6953a z1p*)|hy33pf`_)E44`I;`sm*P*#@Ex0RZX~u%69O003?aWjTnBH^Nx}Mk4jW=gp_5 ztfS1;T$59fXSy&3K0SKKSM<$dT&NaM5jT~MkV2$pMYjT|r=l_>N}c|DC5+mryw$Ot zi@l+k*eY9x0~^XKuL5LVB~?x#1rm}fUk>-7NE5F3-R3mn_1wL_ogZc98o&&aXX~Dl z;_{A;{v74Lo!oVMJ=Y>=D>J9+{C_}n3|vRsK)oM4v3T;gt2OY0V>SlHSK`UH-Vo`- zN*xYAwR!(Y{9V$myRDFQRQu-k9Uq)JF3wLTcT0|fPxQ_$u5@^WK+@tXJ_2e-s^E8D zdRw#L!RIc+lVWa0eb?f){FM0jl40KC5fF5AYJFqlu;@z+sr2apKOdh5q083k9k*6O z2B4fa&h>kIEA9V^JX`!g93W9zwX>r$N<`8Nfj{FTMG+4dqEoglseR8E9RwWK zZ_oT9BO>yY31Qkeji?13G|j!D{1hLMA$53YSGi>rHFDLn3Fq?FdKT&oUQ3mA>toWJ zwG8AD2)|31(fi>ojvGz0Sy@@QtgNi(COv^KC5iOP0%~LsSK-z*{o1Exbb^hLS{KY` zm>43xCS_=o3^Hz;)N2N!x*EF4JH(sN$XF6YjA#aIRLl@0ja*Tb`PR)&AHLJpn>p9# z2?_>m3(M&|i4^5o-tq_p+Ea0N%=Q$Q;pLY6|5{O-htLuGhnMrymqLqg6GdfQl41DA zx!WIP-tdI)TZd8VLjc?1@KApgA2k3L78bNdXFK(SX>f2KWOjZYiLbG-C+g`Z21Zhl zHXH&bS5;L-It(_Bb{Ugp(6rL5HaxlZ*R-vMAgOx=ob+){s+Rpn5esOGyFo=lAftz~ z!E6Z#2KNkuWMCD#mD@t%U~VjpxVXMQoVCaU{F~8q-{B7iSh#_x&4Vli$jbsD+a~`x zZNbBC%sY#s^LfGO^uBf?dW9A!(%!C%+kE)m>XfoofQU5=IdX-I8JkGbuHtTjEmY){ zUNqH}x-i4<6IrbOoIs^lEr7MXeZ_Zuu0Jx)m!6+2qFp+hjX??Y!dy~`@Uq>XoIVlM z85)F_`0dve`0DhB%k){#vb4yY8L(p6g}cGOs@HK*dJ}f8Kc(sY0s*-8LUb*Q7ZR@$ zAfOTF!*}6@wlj zS&1jQ6%`i>78Fby5+01ir#^?!8z)#>@dc(aOAkid;)l5#+pdcQM)n~SujCwlgM=c= z-F;(rPHPXuEi76(@}@ygehBwrOI2|_oWU#5W=C@8qVMl;SO)QH_3i#6*g&nT7*yDV z$q_$1*?k}d5i~*VmdO{6+)%FB9sJsifmces}Rsza2+A0?-_e1E)zm99dZsrH*rtMivIhE|m71kJDEgW2%uzL;oM=Scb z5ONU+0$B6{m%8Ppsp|SiCx#HzjnoE|!mMYLg?kf^bF;Ib2s5cvy6racS&Qv^NcJIv zM^Al^;)(oFb^SygSh@YeZP4hD7xE+Hzbq9@X`FM? zE2e5UQCab|%+9>=|497wMapiWi*8%DCv0t>bz1Unqw+lH?MYLs966*E0V8n`0ri?g z5BHpF7ksGEg1d`}LWtEQk4?|wz|dmyXQz>pFZVNPQm`QgB~HBcT)F&7V&p*~r%Aa^ z&}%5&^Y#g64Fdy=;T^r%ZhOs`TL$S@w&=iLb>o1@%I-6k9g(xO$K1-ZEt7$;>dmA= zdEN$W;yBu{Fx6dt>@_!{U!d7D@}Je0Wn3>mXbLvNcbeMPO-z1ijQr=nG`Y>+;w7Lg zXU#`}3(}UOU`r1#2#RLNyrDj%QA$%?fDodQV^0}4PleC8P}veJpI$8)E8DJc z+otKo2T$zx*~9a3g_|B`Sf!&cci;@3!2jCMIKZP#o4ce=Tj{ktTEQ1TjZ-qi*O6^YF zUCCeO34IjkC_<3#U>ub5lqD4C_9Xm@v_aNZ4fJXI`RAipGb?TsDhM%9z6&=1Nnvz!3Na zo)m^TZ7{C{2IMmZ*u0m*4t1uV8ksdg=3*mdoBGx0{HMst>S{;#BP%5rgY^M}*_HLp z`LqrTD{F0q9;cLG*eaKr_Cvb06K0vaV&4;a1y`S&W7hJ(A7LjZuhfdma^wb`tjNnT z3SkdlRE;h{++RfV7q!j-R2!duHP9pmFo3@OkC;KQi6|kl8+^>Z-GdETA9>T zEl15-_n z$-a$e-ml}j?kf07-tn`J^W>qbaWS<@VaSlyJlh3_DO5Qt{T*@)@{*O2y_17k(=;XK|_S;^mf0w7DFtmD* zeCQCftgz3%R%7oII>!|jiD%|&h0$+Fa@rk;Us`fIB%Vp8luxfzG3wMGE-yZXOr&nz z+G)A4DU7}}B!>3r;y?2K`Y!Or40-&CcC}@?{r9Zm^oiOiaB09*x$xE+b?NWGhd=Fj zuNMh;z0yGhK2bxkQF^up17sA$;vSyywWy|eGeIIWasHe^$F8>kWHJ(?r#w6gak3$4 zHNHP)T?Q`7rmgZ?>wf17=&0MNr4L=fs^Ys;{KwneJi#BTL*j{_lp-V+M1i!v?^Rhx zVvl-WHMUJZ|LhU|o2f~=_1q@b^6fk`QJKqSba>0=0@wfe+%)9#OBeLeEwgORHkk_< z*N%9=_EE;T@+6c?j*!Di+Ikqyveoy-#qUJ?v>k1s(DA*bhRHNy6{2UGwU06|arcJl zI}$G);2a^rm~CtgFJlpwgQM#7#B6ZTU#S9?V2q$}>IwPD!?oEYc^M~{6T2QJH;f_N8%6%ahUwzA_F-1o4Q};r+k0=t?Ltu~M1T)v>c*~4&{?Mbs$XP-lvdSVed zLdPNnKTel~&q|vI@qbPoq_<}s80X&9l#MZIct*StCbij3{rwx_6B?QW+cWrF&$T?E z4{g)ErI|wX(yxpbeA*rN&6EyghC~S%GEpjQv1)03{qyKDygzJLMnAs}-2r1XzjnGy#$#WTk$)$)SNpgyA z~u~aS;t)xu2W?#FOb7)#jm%(4XM%|nX|K$%f|3X z)a-gZR?f)Ggq-n?ZaEc|f)B{H3#nWp+Paqeo|n7PnRsCMwJ!M0suZ2Ry6zNEeP)j@ z>;w>a3YD-5*9M|6m0-4u`0J?9t@y=Hiya@_aNvw8;e+sZdJu06q$MHnCVh2@2(Q3L zj8PJ)UUnvqi36c*?DEQnv_4Fmt)C50MqQC=;3WB-al-zw5>{h*af6S`#UY)RPG)93 z3r`||E$2mv1ou|@L3>R$65c3dyKZH+CL(-)K6yzeXH)PBAJ`!xC*QC2BVfr{G8pt*yj3!hS+Bz7p<5e$i(NX7eLOHxN+yCns9d6=MD)!H9{>s|^v$8|T_127McaZE}aV1Kp!F z@Yb8A`!@PrSrila!`u6nH48gUgHpB&%C0sMw!&NXmmL%EL#_+t5~~L-e2C&gLGZ^3 zqz-@QudQ=SFl+$3Wt2>&F(_B$`UBou_N)i@`U8dPm0B{pab5&*$Ly~o1VRZ6oCP&V z`!8chwr9_0GUQbGxeWI? z8UdXC^T;C$m}xr}OV2A z|JGajz4W|Rhx2@y()!aU^8d~{NqoU{mW_929)!sPh|CP!jQQlJ4cJE{1%`E_1Nfq@ z9Kexi-pv^mxcnVmfdfd?_oV+@&=nQ8ymCsK2@zupTy^AsxlKF`_3n`aw2;=3pK5jovC z5Jjc-PuPL!BR!YPqX{|f6nrtmj7SMKyR0<=D~g7U%uZ$pyzRzJK9HFTPXO6+(-%xg zNl6=<-guk; zlV?;^%JhQ&bdZX?B#f7@oiaqO$y^np(DXg^nhb>>LSQ( zA|LW$#Zv|&Iub|1&BNZ;ufSauXMyE4*mIaRz6f^yZ?Jo(TPqyWXSU$n3fgn~8Hyki zyR`4w_ucswaia5zi<5}8hc>h!eB~p=rwTq#X!;p~azqKeG#SOc?Z>%YHanVtWN-yw z!>)rO91F-|QO%KR)yb`)9I3;1XX|%9$B`a>i1x7Ee*&5>lq66TAC3vo0RoeXEg1u>ekU*-n)`W=UUb{^b@svdOwDai(cU2Q}!r*QT# zx*{HcXajp4ULU8{i|^*SwicQ}qEdrBjT{uVc#rQi^E*uVpA3F@NUwk5XP&HZkL|1p z9lm~Y`$n;^RiB!dUap)HJ|px6HF@ut+CGg^(K{4M z=r&M#(NoQPvG-_mA*1z6KZ94_)?3jHv;j%6m_f&UbyX9dSxib|sD)l}jOW#yb-` zn;2m+q#)--+quTOT%VY&NjX$!j2v58s*kkREzDE`-D zk;bvmSEK!mh4=grv3yr#K&e>!onVqM|EqNKT(7ZaE>;Ia%U}etbj19Cbar zOg{e%-<(`}2vW{o)3sx}KWJw-MgLIS2uT<~OO3aQ8tO?JPwR(}42nj?Cg?gkI!bfk z$hYwr5PM@9v1pYQ8nP-wO&b^F&LdYq0IQauIgV(bPnM1h{6<&eqCt{iN?m2)Bbp`& z-lsQ{i&Y*K5*3+83wo@?>mAg=K{KWI>I?RK+Q$hjV+gU|AftBr%PGZbmE$K<-+e@28AMf;zIz%kv4 zR&f>DW+JMB3EnyNvnVe1${9v9$BcziH^V>3YtJ?j+3rV$AxiHt?8GcvUtd(7`(huf zj_Brt(I~EBv8zqOz8sp%wHfyxx@R`4=J!J6KF~{tz+Y(}8_~H?fbB+L1wM@FoHamE zXk3}K!}@Koc=ofej(pH4hMI=@!af04%s;Di zWsl7{0LPGZx+}xPL$JhwNILX;eH&C0*okNQ!E#x1G5_uL6J|!U5-7d#K*a-)Z1}P{{W@--r_>IbtWQqAFg^ZXqiwPuXeBLSD4-*n1pSHg&$^Jo*E$fD}`T z;?sQAf9aj-7Y;&!B-R@04W)d!h${~aEcLvOT$U6z0nzG7(+`F4wlC5+axjE7A^Npg z3OmMjJ)U7_L34gT%;+0mayI@}><~akQ()&$pcWj$)93uKgHQc^TCM->{`7lWF)T1J zuNjd{zWKlxyS@-CmbV%)y2l?Hz>8mI>+JtX;Erc;#H=59`U_oKB`VpIqJKq`L4^dr z=aN#FrQPw1xZRi0&w|Pw84G!_Q)+F@krto`i7#JUFcTzVL~1%mmR(lkxn9)tN6wv- z*?RL0_w!AF1vg)XVAEEPbuM_-B38F_Nz1a%B-;@MtZEXE$Mq?fEw?fUKqVFi&>IdZ;3^dT@;y zY(y~U(}IW)oo#K|F7uVYrt7c~tC{hK(z`hoH7lzIWsoPuZP+F{H&Mi3pByoq!3bzb zRd9Vr&cpM4Wqd```tk@+HuoS5XutjO_-4yYY;PGafi?9Q${nq0Uj+0&-EU1eNBo|{ zrj}eo>Js@lX_)`=yR+=_G8#)}0Eh`jkBWDi3*RLZ95!{tT)GVN*ZtDlh;ox-pW!ui90|9E+`!*<=?Z)(4muO*;o2MF z{r7uuDCCLr@>O170l69%NGR&h2@4u zaQAwqloC}s{t?}JGTVLnYyuMy%HIN^Ei|e2Yqt!rl|fA$s44Z#W;s1H;45bZj|y29 z$6LUnPjJnE8*tXDMBPws*tl1^!B`9@=w$<3#p!)Yl9|&`ryI{+RJ0cD3(^)+ri4*{ z2|w(cI=Dj5u<_9j(A3ORC$uA)!jn&R<6wp*`Xp0^R{jC^>yeeOxoV4fGCnfTZ;3)5-tE=!MT)ucwZw!FC41>$k98cDkGiD%2fGAm-}ld;t5|1T2LkxH%ix5q5RVDr8S?@_1; z^Y_@Y7{}9Mo-Nu2asTNiV^>j8k-{cD?$^hvt~{ z77N>bJ=J}gCA6)@`5PY2%?f`HqKnTYRG$s+=g0!n(JH^J4bRln zHyF){j)he8rvFaUfhF9Z z0P@Lggj3S(I53_USZ@E*RgO`dtFv`<>G^p+?)5b~_9~_x{wUIHwohY?jEs}jvzse^ zUwWX`lh5S$sb|oY_>;M1*JrOEkEHQ?jQWXt+NwIdirrb^W2?3m#Y_#Va%4YkRbrpY z)Z=L<`+Fl!wsSw=yo1}R#Kx@+S|^`e@S!^LD>jt^XbEXKl+_U$_3FoPo0P>p=U$`C zD1z)XnC#eF_zR1e8$V=m>SQ__+(_4xAmCb)#~mw=I<+H(bpM>mc;+r17@*F-j&7N0 zjALsXwG`UZMwuUD{}YmAh^z)TIP&$Oz;Nc-cv%4(2b2$4^M0G=)91<(lQ(4k#nGUe za$yx@);A4RS-LLoP%i8QO5I%37`aFjVe|h6+OzXy38c*))Mp}T33dz&O#TvKugG9R zJkgDZ!EW_N9Un*+NLj(pJ#62j>qO?*0iq@m5^O3=`1;R)J}qvG_Ws}j3o80LRQj0h z?I~0XcN!5Q^Wi|{ab~MRj!^Gf(;3)~H0B`da*SZzA)AZ%+Pb*rNj<@~ zjBcpvc3GZ+Q&MDiODDSL7B}>pk@c$#4C7NSi0rdXTga5+5$8-$&Nh z1Hasu<5$vCiMB{N8LBjOA(0nS#MfzKHJ>iuMwk&TEOIGn>Wv*?I2FVaqp9-j@+_ha zNuXU^8=$9s1Grp{%&^g*1lM$Gtzhu*Y{ZTY$%}u?o2Hmdk%2>HflIs-7Qo1zsoYN< zqhl&xw^We497w+0%w@%}to!bJ?gBaGa{}If&9@KK_{=5wwJ78gN)kHG&PA`vtB%)I z`VRLI^6CUT*g|Sc5>?Zq@%HINbhbvk8+Xg4A^H5CkI8oq(~TSvBUGNiBycl@gAc_Q z_!ni+j1YyKnhsTs6=zYASkv< z%h%gLDcL2-v3KRmFo#}}M0PUn@_??$hAen%Zel-eYuJ;nF2`FdmSsY7jKZ;`2~YIC zne?wzddR9egyOyA4$_KeWLAcB>}>V5(1~LgrPquo)sNYs2&ygwRMcY!iveDjyh6WG zA+uJMmvab4K03_-7p_S93QdlyRG){exMhRIP_Y&V4wIq3wcoePYCx5{fmq;-VVq^y zk8IhVh^=-UBjSz%vyrwyz49Z1o*H#>xgINjFG?H5P@>fXoHuQ3C*LhuF*liz=42T}j% zBnfK}O$f6OYs(37h_w=+SIpCmdq-1#61|8r#DVx8_u3jbzXqy$il?cJoAOPl*$DQX zGH$S-5`Vt<7-BIc`*Aj;_H9cl;(MSgopFyB85amPh}*d6PFZt&!)aMBB6XBT#bJG~ z6)NX!@XNJ2o@)R|1k@3cC*oq{z>AZUKGv(%y|9@Wl^mW$Wj_NEe%fhe3|m}rSurDP z?@%ovJYDzVejj**S z^KlMqHbaSLdbNM|IONsbvk7!?ig3h8A^bftmXw!n(LS7vTfwM5`9XopM*baKx@*kpy(4|QsU%+L@BkItQ=c}-qHh?KF==H&Wc#N3Jw1}IEW?ZhtGHY7H zQ%uJ3bLHX=kT-*DkwVtbT&IO4`ghr03^`QV#1)(CY#9W?PWehc*I_lDRvowFZ?Z`Os&KXPZsOf)HQ$Lz@L23kkP%aw#La~e2IV@ewt1zc%V13=w+OJxQwuwamCJP)sllF=oFmYH69~)AScao>^euvu2{NvJDZYsrPU9 zhY=zoBC*-#RIMv-PF#sGtTkcuu4g)jSm2row$fCpMgMfuA z6HK5MHd@v`y66RSIhT3Bon=@}o6+ev@V=gqIFC9`j{NiYZ-I{;iV4(Rm;*-$3hFq= ze`QP~NgS|hS@Vt{_Dzxi_A15Tc6erE3qyLW&vq*?wuN@3fOd$vXxA z0?!WYhC!$2(oE{)b?)niqyC^Y2w@a9+=CSq$=(Xs>qLmiDOh*zsP?_sh8$dfdwcfu?;#TU z80mGhNHMY+C;qE2Hrwsj_AIm{P_0O-y}Es%B6qI}~qbjzJuIAZpN_K0G+f znhLq1Q5TT6DO6(+7RB+rr~soW1r$9!+ow%E%Koj`$)fVzhcjm*W!&U*`|eh`TzDPu z@(1zw(}Dm5iLkcoDY4~;i2C}B@h+6}UWa#ton%5T)2*wJP7m&i%+%kB=xn~uMb!-> z&!@*+np!2x*eoUGyOiatHY|tFh7zQTh(A6Ac*_RV87msAbmK5WA2|lNz2@{{X)~+V zi?uvE)-KAV-lZBib{!VRXvp+4&%%#bK0f^T;D~*L(^2F1&OZADUu`)f?K2AQ%&;1Z z(Vif^W4~o2YIOn7)zP(lM%j+p3A5S9A%WE40^e08F!{?g(fa+WihXzU2<*z}=Nr^- zok4E1k?_HF1F#{(!~YEC+Qa%A+!wqN4khw{AX*Ewoz!&rK7WzQIvO)Jt@T@ObKd9J zqew4^peQYkso^~M;nwxVOl~^9)Yk}+sOvzIUxi6P?6YBK5AZ-?4}l=E;QQ)T|MbV? z=3d$s>%Y=cqcO#qk;xmQ+{AXy?^ldY1KeOR7@jfUDghV#Gc0ee-D&+lNbHm2)WP+K zqNP^UzF-*UfBB_`kudP6|ColBHs6>G+t1cft!Y!SUbulet?yxhTDcBoIY^hMneVpQdGlus%b^PO zjTW{4M4?#He!-(L%j#epaXvN;a;$uQ^Fc8J^LlEgyE_$aV_mCsTtt6&ci`d4iN}RE z9tfa};6duMm!$YuT3QN{0d$cFj0-pm`h6}j>_Tpy#m8+>pYC?{bu>UtE@~o(MkVxl z4DKdCy2aAtYh-k#a6b`4g-%n)Kn2cG<0{zxa2Xl(4TP%|#NT1kNH0%>d_FnN(& zkO`qHj>obqQ5&{nSYng>;}9Pki(hVUj2@N_M46bD#-$d9O4snmK3i&b8TL?4ZU~V7 zYIA& + + + + + + + + + + diff --git a/resources/feedback.svg b/resources/feedback.svg new file mode 100644 index 0000000..d164afe --- /dev/null +++ b/resources/feedback.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/file_button.png b/resources/file_button.png new file mode 100644 index 0000000000000000000000000000000000000000..a6b579f2f748ebc18b4f1b8a4e4d6ed0dba70722 GIT binary patch literal 1402 zcmV-=1%>*FP)U43UqJg8s4Hw;bXc*F zh7yfi%FYc*_a<^#y0O!swX~gkyr)T3Na@R+nKREY-%mo)uz)Up^PJ~-PH(RW00000 z000000000000000000000000006-%n%0wfR1?gNYNDai_eH9C8N=0Cz&&SW0z+5$A zhtlt;CSh4)#muJvZftCFud*yNafuLBqoIL`zPLFY1i_^HQhYI{lsv7B5u9uq7MO#V zC$5UI-$-M`iOBVM;>&5lwuI6E(UtTFGE$PpDWsp23&oSCW$xjR4+LB0JuuP8%_s=X z?exo<_A1%f;o`|t^K%b>`bDrMfp`{lgg3-z&$79%T`wir60bds8<~UL(wyj1PL`4r zG{T$nT4BO)X;^eBm|S>wbq9nEc@2yWlOa*c?SRticSEotzdejK+CRb94qo}5&}_hO zg)z1XN`5j*XkN_wl++wtXvQD8eUac9ZGsnx(ofUfgO|t4?mkNDEWJycO>FW&`}W!E zSbgBi&61JeqF^I&TKG%9OV zkX%>K>Yr+~wY6V7`O*2);ud=*iIWw3Z|eL`?^|I4Q$t}}yA19>BD?k-max5DYAn6D zUA?@G(py{!LNju9>W^3|$=DC)c9z5wj-(nH+;>FY{NQ8Redu^S!m>V_h9?Ln&rQvF zo1zFzm0VRhH1xjgJ@&T0pghM~J1U-VBvm2T*(0x?cu%@|TjwV`sW2J2%F=3KaR^LC zN8cH4xv)3{CWF_G4z*-h90IdVuO50scI`hbt%|^GgA0orn_C%E(iXX6sFf?r8UnKk zx8mQOR+ir4fS?Hj)0N)MxQ4T%vs>z^Bfa~{_YKzl|7kW&5tusP(O?_Y2gcpec)g#& zhExZJsYD;OV>MyXNo8RAcSnRpDV2e7V=Q!17Z_Jva$StFTImbSo=BjVy1=ke)lF4k zs{P4&)St4z(7R}-Dli?LR2c23FxVDVf$7c{1-hvUOqk<>Q9E5n5Wy zrYp~TA*xUoPt*j4{v|srQukjxy+%!7*ui9HtjvoiY68O!CcDJgSUgb^7?jQI98#4k3*n4-8=}jt2~+Yp zQafIcr0GwJ<)?oy+!EBp^0Ih;Rk8OeDaiMXisw=AWn`@#oBpgAi)Y>g + + diff --git a/resources/join_community.svg b/resources/join_community.svg new file mode 100644 index 0000000..79f1447 --- /dev/null +++ b/resources/join_community.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/new.svg b/resources/new.svg new file mode 100644 index 0000000..97b5791 --- /dev/null +++ b/resources/new.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/open.svg b/resources/open.svg new file mode 100644 index 0000000..4c57e2b --- /dev/null +++ b/resources/open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/play-button-arrowhead.png b/resources/play-button-arrowhead.png new file mode 100644 index 0000000000000000000000000000000000000000..70e877065d054343b6218548c8dff6510d4ca500 GIT binary patch literal 6074 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 P`F+^#sBPf^%8ma68G@5X literal 0 HcmV?d00001 diff --git a/resources/print.svg b/resources/print.svg new file mode 100644 index 0000000..89a50fc --- /dev/null +++ b/resources/print.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/rename.svg b/resources/rename.svg new file mode 100644 index 0000000..43a0af7 --- /dev/null +++ b/resources/rename.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/save.svg b/resources/save.svg new file mode 100644 index 0000000..4f1ac42 --- /dev/null +++ b/resources/save.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/save_as.svg b/resources/save_as.svg new file mode 100644 index 0000000..b51e900 --- /dev/null +++ b/resources/save_as.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/save_button.png b/resources/save_button.png new file mode 100644 index 0000000000000000000000000000000000000000..f035e0bc244de07dc4939029a87aa8c8669f0352 GIT binary patch literal 8849 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 AGynhq literal 0 HcmV?d00001 diff --git a/resources/tab_close.png b/resources/tab_close.png new file mode 100644 index 0000000000000000000000000000000000000000..394d5b1afe7012f3e13014fff50471123bb16022 GIT binary patch literal 9732 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 + + diff --git a/resources/video_tutorial.svg b/resources/video_tutorial.svg new file mode 100644 index 0000000..c90e743 --- /dev/null +++ b/resources/video_tutorial.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/window_close.svg b/resources/window_close.svg new file mode 100644 index 0000000..eeafc65 --- /dev/null +++ b/resources/window_close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/window_maximize.svg b/resources/window_maximize.svg new file mode 100644 index 0000000..6adddd1 --- /dev/null +++ b/resources/window_maximize.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/window_minimize.svg b/resources/window_minimize.svg new file mode 100644 index 0000000..9c2d49e --- /dev/null +++ b/resources/window_minimize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/window_restore.svg b/resources/window_restore.svg new file mode 100644 index 0000000..07a15be --- /dev/null +++ b/resources/window_restore.svg @@ -0,0 +1,3 @@ + + + diff --git a/widgets/__pycache__/maintenance_repair_data.cpython-312.pyc b/widgets/__pycache__/maintenance_repair_data.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6c7d4bef0d19f2fc46de8c69236540b63808cca GIT binary patch literal 19943 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 Bch3L- literal 0 HcmV?d00001 diff --git a/widgets/__pycache__/title_bar.cpython-312.pyc b/widgets/__pycache__/title_bar.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d0dc31d938d79074fe030bd5afaa25545a459ea GIT binary patch literal 8573 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 L<~s(L1jl~?ghc7{ literal 0 HcmV?d00001 diff --git a/widgets/__pycache__/tutorial_widget_left.cpython-312.pyc b/widgets/__pycache__/tutorial_widget_left.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65eb895ae88911293febf2f55f4388642ec9cca9 GIT binary patch literal 11839 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 literal 0 HcmV?d00001 diff --git a/widgets/title_bar.py b/widgets/title_bar.py new file mode 100644 index 0000000..a19aefe --- /dev/null +++ b/widgets/title_bar.py @@ -0,0 +1,135 @@ +from PySide6.QtCore import (QSize, Qt) +from PySide6.QtGui import (QIcon, QPixmap) +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QWidget, QLabel) +from PySide6.QtGui import QMouseEvent + +class CustomTitleBar(QWidget): + """ + A custom title bar widget for a frameless QMainWindow. + It provides window dragging, minimize, maximize/restore, and close buttons + with custom styling and SVG icons. + """ + def __init__(self, parent=None): + super().__init__(parent) + self.parent_window = parent + self.setObjectName("custom_title_bar") + self.setFixedHeight(30) + # Set a single stylesheet for the entire title bar and all its children + self.setStyleSheet(""" + #custom_title_bar { + background-color: #45913E; + } + #custom_title_bar QLabel { + background-color: #45913E; + color: white; + } + #custom_title_bar QPushButton { + background-color: #45913E; + color: white; + border: none; + padding: 0px; + } + #custom_title_bar QPushButton:hover { + background-color: #55a04c; + } + #custom_title_bar QPushButton:pressed { + background-color: #3d7936; + } + #custom_title_bar QPushButton#close_button:hover { + background-color: #E81123; + } + #custom_title_bar QPushButton#close_button:pressed { + background-color: #F1707A; + } + """) + + # Main layout + self.layout = QHBoxLayout() + self.layout.setContentsMargins(0, 0, 0, 0) + self.layout.setSpacing(0) + self.setLayout(self.layout) + + # Left: Icon + self.icon_label = QLabel() + self.icon_label.setFixedSize(30, 30) + self.icon_label.setStyleSheet("padding: 5px;") + # self.icon_label.setPixmap(QPixmap("resources/osdag_logo.svg").scaled(20, 20, Qt.KeepAspectRatio, Qt.SmoothTransformation)) + self.layout.addWidget(self.icon_label) + + # Middle: Title (centered) + self.title_label = QLabel("My Custom App") + self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.title_label.setStyleSheet("font-weight: bold;") + self.layout.addWidget(self.title_label, 1) # Add stretch factor of 1 to center the title + + # Right: Control Buttons + self.btn_size = QSize(46, 30) + + # Helper function to create a styled button + def create_button(icon_svg, is_close=False): + btn = QPushButton() + btn.setFixedSize(self.btn_size) + btn.setIcon(QIcon(QPixmap.fromImage(QPixmap(icon_svg).toImage()))) + btn.setIconSize(QSize(14, 14)) + if is_close: + btn.setObjectName("close_button") + return btn + + # Control buttons + self.minimize_button = create_button("resources/window_minimize.svg") + self.minimize_button.clicked.connect(self.parent_window.showMinimized) + self.layout.addWidget(self.minimize_button) + + self.maximize_button = create_button("resources/window_maximize.svg") + self.maximize_button.clicked.connect(self.toggle_maximize_restore) + self.layout.addWidget(self.maximize_button) + + self.close_button = create_button("resources/window_close.svg", is_close=True) + self.close_button.clicked.connect(self.parent_window.close) + self.layout.addWidget(self.close_button) + + self.start_pos = None + self.start_geometry = None + + def set_maximize_icon(self): + self.maximize_button.setIcon(QIcon(QPixmap.fromImage(QPixmap("resources/window_maximize.svg").toImage()))) + + def set_restore_icon(self): + self.maximize_button.setIcon(QIcon(QPixmap.fromImage(QPixmap("resources/window_restore.svg").toImage()))) + + def toggle_maximize_restore(self): + """Toggles between maximized and normal window states and updates the icon.""" + if self.parent_window.isMaximized(): + self.parent_window.showNormal() + self.set_maximize_icon() + else: + self.parent_window.showMaximized() + self.set_restore_icon() + + # --- Window Dragging Functionality --- + def mousePressEvent(self, event: QMouseEvent): + """Records initial position for dragging.""" + if event.button() == Qt.LeftButton and not self.parent_window.isMaximized(): + self.start_pos = event.globalPosition().toPoint() + self.start_geometry = self.parent_window.geometry() + event.accept() + else: + event.ignore() + + def mouseMoveEvent(self, event: QMouseEvent): + """Moves the window based on mouse movement.""" + if event.buttons() == Qt.LeftButton and self.start_pos and not self.parent_window.isMaximized(): + delta = event.globalPosition().toPoint() - self.start_pos + new_x = self.start_geometry.x() + delta.x() + new_y = self.start_geometry.y() + delta.y() + self.parent_window.move(new_x, new_y) + event.accept() + else: + event.ignore() + + def mouseReleaseEvent(self, event: QMouseEvent): + """Resets dragging state.""" + if not self.parent_window.isMaximized(): + self.start_pos = None + self.start_geometry = None + event.accept() From 2342d7347cf6a432e013de34a2430c70e5e85d04 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Mon, 30 Jun 2025 16:38:15 +0530 Subject: [PATCH 02/22] Added Tutorial Widget that gets triggered by Tutorial Button --- widgets/tutorial_widget_left.py | 282 ++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 widgets/tutorial_widget_left.py diff --git a/widgets/tutorial_widget_left.py b/widgets/tutorial_widget_left.py new file mode 100644 index 0000000..8beaa5b --- /dev/null +++ b/widgets/tutorial_widget_left.py @@ -0,0 +1,282 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, QSize, Qt, Signal +from PySide6.QtGui import (QIcon) +from PySide6.QtWidgets import (QHBoxLayout, QTextEdit, QScrollArea, QSpacerItem, QSizePolicy, + QPushButton, QWidget, QLabel, QVBoxLayout) +import sys + +class TutorialWidget(QWidget): + closed = Signal() + def __init__(self): + super().__init__() + self.setObjectName("left_panel_widget") + self.setStyleSheet(""" + #left_panel_widget { + background-color: #F8F8F8; /* Light gray/off-white background */ + border-radius: 8px; /* Slightly rounded corners for the entire body_widget */ + border: none; + } + #left_panel_widget QLabel { + color: #333333; /* Darker text for content */ + font-size: 12px; + /* No border or padding here, managed by layout margins */ + } + #left_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + /* Styling for the scroll area */ + QScrollArea { + border: 1px solid #000000; /* No border for the scroll area itself */ + background-color: transparent; /* Make background transparent to show widget's background */ + outline: none; + } + QScrollArea > QWidget { /* This targets the widget *inside* the scroll area */ + background-color: transparent; /* Inherit parent background */ + } + + /* Scrollbar styling */ + QScrollBar:vertical { + border: 1px solid #E0E0E0; /* Lighter border for scrollbar area */ + background: #F0F0F0; /* Light gray track */ + width: 12px; /* Width of the vertical scrollbar */ + margin: 18px 0px 18px 0px; /* Space for arrows */ + border-radius: 6px; /* Rounded track */ + } + + QScrollBar::handle:vertical { + background: #C0C0C0; /* Medium gray handle */ + border: 1px solid #A0A0A0; /* Darker border for handle */ + min-height: 20px; + border-radius: 5px; /* Rounded handle */ + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); /* You might need to provide these icons */ + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + #left_panel_widget QPushButton#back_button { + background-color: #FFFFFF; + border: 1px solid #D0D0D0; + padding: 6px 8px; + margin: 8px; + color: #000000; + font-size: 13px; + border-radius: 8px; + } + #left_panel_widget QPushButton#back_button:hover { + background: qlineargradient( + x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #BBBBBB, + stop: 0.26 #E8E8E8, + stop: 1 #EDEDED + ); + border-color: #806C6C; + } + #left_panel_widget QPushButton#back_button:pressed { + background-color: #FFFFFF; + border-color: #606060; + } + + #left_panel_widget QPushButton#next_button { + background-color: #FFFFFF; + border: 1px solid #D0D0D0; + padding: 6px 8px; + color: #000000; + margin: 8px; + font-size: 13px; + border-radius: 8px; + } + #left_panel_widget QPushButton#next_button:hover { + background: qlineargradient( + x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #BBBBBB, + stop: 0.26 #E8E8E8, + stop: 1 #EDEDED + ); + border-color: #806C6C; + } + #left_panel_widget QPushButton#next_button:pressed { + background-color: #FFFFFF; + border-color: #606060; + } + + QPushButton#top_button_left_panel { + background-color: #F0E6E6; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #FDEFEF; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + QPushButton#top_button_left_panel:hover QIcon { + color: red; + } + """) + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) # Add some padding inside the red area + left_panel_vlayout.setSpacing(0) # Spacing between major sections + + top_h_layout_left_panel = QHBoxLayout() + self.top_button_left_panel = QPushButton("Tutorials ") + self.top_button_left_panel.setIcon(QIcon("resources/close.png")) + self.top_button_left_panel.setIconSize(QSize(13, 13)) + self.top_button_left_panel.setObjectName("top_button_left_panel") + self.top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + self.top_button_left_panel.clicked.connect(self.close_widget) + top_h_layout_left_panel.addWidget(self.top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + bordered_spacer_widget = QWidget() + bordered_spacer_widget.setObjectName("bordered_spacer_widget") # Give it an object name for CSS + bordered_spacer_widget.setFixedHeight(50) + bordered_spacer_widget.setStyleSheet(""" + #bordered_spacer_widget { + background-color: #F0E6E6; + border-left: 1px solid #000000; + border-top: 1px solid #000000; + border-right: 1px solid #000000; + } + """) + left_panel_vlayout.addWidget(bordered_spacer_widget) + + scroll_area = QScrollArea() + scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_area.setWidget(scroll_content_widget) + + scroll_content_layout = QVBoxLayout(scroll_content_widget) + + middle_header_label = QLabel(""" +

+ 1/4
+ Welcome to
+ BLCCA Studio
+
+ """) + middle_header_label.setStyleSheet(""" + QLabel{ + font-size: 17px; + } + """) + middle_header_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + scroll_content_layout.addWidget(middle_header_label) + + + middle_body_label = QTextEdit() + middle_body_label.setReadOnly(True) + tutorial_text = """ + BLCCA Studio has a lot of features to offer. In the next few minutes, you will learn how to use BLCCA 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 BLCCA Studio and enhance your productivity. + """ + + middle_body_label.setHtml(tutorial_text) + middle_body_label.setAlignment(Qt.AlignmentFlag.AlignJustify) + middle_body_label.setStyleSheet(""" + QTextEdit { + font-size: 14px; + padding-top: 20px; + border-top: 2px solid #806C6C; + } + """) + scroll_content_layout.addWidget(middle_body_label) + scroll_content_widget.setStyleSheet("background-color: #FFF9F9;") + left_panel_vlayout.addWidget(scroll_area) + + bottom_widget = QWidget() + bottom_widget.setObjectName("bottom_widget") + bottom_widget.setStyleSheet(""" + #bottom_widget { + background-color: #F0E6E6; + border-left: 1px solid #000000; + border-bottom: 1px solid #000000; + border-right: 1px solid #000000; + } + """) + bottom_h_layout = QHBoxLayout(bottom_widget) + back_button = QPushButton("Back") + back_button.setObjectName("back_button") + next_button = QPushButton("Next") + next_button.setObjectName("next_button") + bottom_h_layout.addWidget(back_button) + bottom_h_layout.addWidget(next_button) + left_panel_vlayout.addWidget(bottom_widget) + + def close_widget(self): + self.closed.emit() + self.setParent(None) + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addWidget(TutorialWidget(), 1) + +# self.main_h_layout.addStretch(4) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file From 2b7311d6de1cd4260050fd8d075ca958e33f8380 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Mon, 30 Jun 2025 16:42:35 +0530 Subject: [PATCH 03/22] Added Project Details Widget that gets triggered by Its Button --- main_template.py | 2 + widgets/project_details_right_widget.py | 497 ++++++++++++++++++++++++ 2 files changed, 499 insertions(+) create mode 100644 widgets/project_details_right_widget.py diff --git a/main_template.py b/main_template.py index 4c794c2..f280a68 100644 --- a/main_template.py +++ b/main_template.py @@ -4,6 +4,8 @@ QMenu, QMenuBar, QPushButton, QWidget, QLabel, QVBoxLayout, QGridLayout, QLineEdit, QComboBox) from widgets.title_bar import CustomTitleBar +from widgets.project_details_right_widget import ProjectDetailsWidget +from widgets.tutorial_widget_left import TutorialWidget class UiMainWindow(object): def setupUi(self, MainWindow): diff --git a/widgets/project_details_right_widget.py b/widgets/project_details_right_widget.py new file mode 100644 index 0000000..4b1287b --- /dev/null +++ b/widgets/project_details_right_widget.py @@ -0,0 +1,497 @@ + +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, QSize, Qt, QPropertyAnimation, QEasingCurve, Signal +from PySide6.QtGui import (QIcon) +from PySide6.QtWidgets import (QHBoxLayout, QTextEdit, QScrollArea, QSpacerItem, QSizePolicy, + QPushButton, QWidget, QLabel, QVBoxLayout, QGridLayout, QLineEdit, QComboBox) +import sys + +class ProjectDetailsWidget(QWidget): + closed = Signal() + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName("central_panel_widget") + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + QScrollArea { + border: 1px solid #000000; + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + } + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + QPushButton#general_info, QPushButton#parameter_button, QPushButton#output_button { + background-color: #FDEFEF; + border: 1px solid #000000; + text-align: left; + padding: 3px 10px; + color: #000000; + font-size: 14px; + } + QPushButton#general_info:hover, QPushButton#parameter_button:hover, QPushButton#output_button:hover { + background-color: #F0E6E6; + border: 1px solid #000000; + } + QPushButton#general_info:pressed, QPushButton#parameter_button:pressed, QPushButton#output_button:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + QPushButton#top_button_right_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_right_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_right_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + QPushButton#top_button_right_panel:hover QIcon { + color: red; + } + """) + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + # --- Top Section --- + top_h_layout_left_panel = QHBoxLayout() + self.top_button_right_panel = QPushButton("Project Details Window ") + self.top_button_right_panel.setObjectName("top_button_right_panel") + self.top_button_right_panel.setIcon(QIcon("resources/close.png")) + self.top_button_right_panel.setIconSize(QSize(13, 13)) + self.top_button_right_panel.setLayoutDirection(Qt.RightToLeft) + self.top_button_right_panel.clicked.connect(self.close_widget) + top_h_layout_left_panel.addWidget(self.top_button_right_panel) + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + # --- Scroll Area Setup --- + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + scroll_content_widget = QWidget() + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + scroll_content_layout = QVBoxLayout(scroll_content_widget) + scroll_content_layout.addStretch(1) + + # --- General Information Section Button --- + self.general_info_button = QPushButton(" General Information") + self.general_info_button.setObjectName("general_info") + self.general_info_button.setCursor(Qt.PointingHandCursor) + self.unactive_arrow_icon = QIcon("resources/play-button-arrowhead.png") + self.active_arrow_icon = QIcon("resources/arrow_down.png") + self.general_info_button.setIcon(self.unactive_arrow_icon) + self.general_info_button.setIconSize(QSize(10, 10)) + self.general_info_button.setLayoutDirection(Qt.LeftToRight) + scroll_content_layout.insertWidget(0, self.general_info_button) + + # --- General Information Form Widget --- + self.general_button_active = False + self.general_widget = QWidget() + self.general_widget.setObjectName("general_info_form_widget") + self.general_widget.setStyleSheet(f""" + #general_info_form_widget QLineEdit, + #general_info_form_widget QTextEdit, + #general_info_form_widget QComboBox {{ + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background-color: #FFFFFF; + }} + #general_info_form_widget QTextEdit::viewport {{ + background-color: #FFFFFF; + }} + #general_info_form_widget QLineEdit:focus, + #general_info_form_widget QTextEdit:focus, + #general_info_form_widget QComboBox:focus {{ + border: 1px solid #DDDCE0; + background-color: #FFFFFF; + }} + #general_info_form_widget QTextEdit:focus::viewport {{ + background-color: #FFFFFF; + }} + #left_label {{ + margin-right: 20px; + }} + #info_button {{ + margin-right: {self.general_info_button.width()//2}px; + }} + """) + grid_layout = QGridLayout(self.general_widget) + grid_layout.setContentsMargins(30, 20, 30, 20) + grid_layout.setHorizontalSpacing(10) + grid_layout.setVerticalSpacing(10) + # Company Name + label = QLabel("Company Name") + label.setObjectName("left_label") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 0, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + grid_layout.addWidget(input_widget, 0, 1, 1, 2) + # Project Title + label = QLabel("Project Title") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 1, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + grid_layout.addWidget(input_widget, 1, 1, 1, 2) + # Project Description + label = QLabel("Project Description") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 2, 0, 1, 1) + input_widget = QTextEdit(self.general_widget) + input_widget.setPlaceholderText("Enter project description here...") + grid_layout.addWidget(input_widget, 2, 1, 1, 2) + # Name of Valuer + label = QLabel("Name of Valuer") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 3, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + input_widget.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + grid_layout.addWidget(input_widget, 3, 1, 1, 1) + # Job Number + label = QLabel("Job Number") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 4, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + input_widget.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + grid_layout.addWidget(input_widget, 4, 1, 1, 1) + # Client + label = QLabel("Client") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 5, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + input_widget.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + grid_layout.addWidget(input_widget, 5, 1, 1, 1) + # Country ComboBox + label = QLabel("Country") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 6, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) + valuer_combo.addItem("India") + valuer_combo.addItem("USA") + valuer_combo.addItem("UK") + valuer_combo.setStyleSheet(""" + QComboBox{ + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + QComboBox::drop-down { + border: none; + padding-right: 5px; + } + QComboBox::down-arrow { + image: url(resources/country_arrow.png); + width: 18px; + height: 18px; + } + QComboBox QAbstractItemView { + border: 1px solid #DDDCE0; + border-radius: 5px; + background-color: #FFFFFF; + outline: none; + } + QComboBox QAbstractItemView::item:selected { + background-color: #FDEFEF; + color: #000000; + } + QComboBox QAbstractItemView::item:hover { + background-color: #FDEFEF; + } + """) + grid_layout.addWidget(valuer_combo, 6, 1, 1, 1) + info_icon = QLabel("ⓘ") + info_icon.setStyleSheet("color: grey; font-size: 14px;") + info_icon.setObjectName("info_button") + info_icon.setToolTip("Social Cost of Carbon varies as per selected region") + info_icon.setCursor(Qt.PointingHandCursor) + info_icon.setAlignment(Qt.AlignRight) + grid_layout.addWidget(info_icon, 6, 2, 1, 1) + # Base Year + label = QLabel("Base Year") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 7, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + input_widget.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + grid_layout.addWidget(input_widget, 7, 1, 1, 1) + scroll_content_layout.insertWidget(1, self.general_widget) + self.general_widget.show() + self.general_widget.adjustSize() + self.original_general_widget_height = self.general_widget.sizeHint().height() + self.general_widget.hide() + self.general_widget_animation = QPropertyAnimation(self.general_widget, b"maximumHeight") + self.general_widget_animation.setDuration(300) + self.general_widget_animation.setEasingCurve(QEasingCurve.InOutQuad) + self.general_widget.setMaximumHeight(0) + self.general_widget.hide() + # Input Parameters Section + self.input_param_unactive_icon = QIcon("resources/play-button-arrowhead.png") + self.input_param_active_icon = QIcon("resources/arrow_down.png") + self.input_param_button_css_inactive = """ + QPushButton#parameter_button { + background-color: transparent; + border: 1px solid #000000; + text-align: left; + padding: 3px 10px; + color: #000000; + font-size: 16px; + } + QPushButton#parameter_button:hover { + background-color: #F0E6E6; + } + QPushButton#parameter_button:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + """ + self.input_param_button_css_active = """ + QPushButton#parameter_button { + background-color: transparent; + text-align: left; + padding: 3px 10px; + color: #000000; + font-size: 16px; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + } + QPushButton#parameter_button:hover { + background-color: #F0E6E6; + } + QPushButton#parameter_button:pressed { + background-color: #FFF3F3; + } + """ + self.input_param_widget = QWidget() + self.input_param_widget.setObjectName("input_param_widget") + self.input_param_widget.setStyleSheet(""" + #input_param_widget{ + background-color: #FDEFEF; + border: 1px solid #000000; + } + """) + self.input_param_layout = QVBoxLayout(self.input_param_widget) + self.input_param_layout.setContentsMargins(0, 0, 0, 0) + self.input_param_layout.setSpacing(0) + self.input_param_button = QPushButton(" Input Parameters") + self.input_param_button.setStyleSheet(self.input_param_button_css_inactive) + self.input_param_button.setObjectName("parameter_button") + self.input_param_button.setCursor(Qt.PointingHandCursor) + self.input_param_button.clicked.connect(self.input_button_toggle) + self.input_param_button.setIcon(self.input_param_unactive_icon) + self.input_param_button.setIconSize(QSize(10, 10)) + self.input_param_button.setLayoutDirection(Qt.LeftToRight) + self.input_param_layout.addWidget(self.input_param_button) + self.input_param_option_widget = QWidget() + self.input_param_option_widget.hide() + self.input_option_active = False + self.input_param_option_widget.setObjectName("input_param_option_widget") + self.input_param_option_layout = QVBoxLayout(self.input_param_option_widget) + self.input_param_option_layout.setContentsMargins(8, 8, 8, 8) + self.input_param_option_layout.setSpacing(2) + button_labels = [ + "Structure Works Data", + "Financial Data", + "Carbon Emission Data", + "Bridge and Traffic Data", + "Maintenance and Repair", + "Disposal and Recycling" + ] + for label in button_labels: + btn = QPushButton(f" {label}") + btn.setObjectName("parameter_button") + btn.setIcon(QIcon("resources/play-button-arrowhead.png")) + btn.setIconSize(QSize(10, 10)) + btn.setCursor(Qt.PointingHandCursor) + btn.setLayoutDirection(Qt.LeftToRight) + btn.setStyleSheet(""" + QPushButton#parameter_button { + background-color: none; + border: none; + text-align: left; + padding: 2px 10px; + color: #000; + font-size: 12px; + margin-left: 40px + } + QPushButton#parameter_button:hover { + background-color: #F0E6E6; + } + """) + self.input_param_option_layout.addWidget(btn) + self.input_param_layout.addWidget(self.input_param_option_widget) + scroll_content_layout.insertWidget(2, self.input_param_widget) + self.output_button = QPushButton(" Outputs") + self.output_button.setObjectName("output_button") + self.output_button.setIcon(QIcon("resources/play-button-arrowhead.png")) + self.output_button.setIconSize(QSize(10, 10)) + self.output_button.setLayoutDirection(Qt.LeftToRight) + scroll_content_layout.insertWidget(3, self.output_button) + left_panel_vlayout.addWidget(self.scroll_area) + self.bottom_widget = QWidget() + self.bottom_widget.setObjectName("bottom_widget") + self.bottom_widget.setStyleSheet(""" + #bottom_widget { + background-color: #F0E6E6; + border-left: 1px solid #000000; + border-bottom: 1px solid #000000; + border-right: 1px solid #000000; + } + """) + left_panel_vlayout.addWidget(self.bottom_widget) + self.general_info_button.clicked.connect(self.expand_general_area) + + def expand_general_area(self): + try: + self.general_widget_animation.finished.disconnect() + except RuntimeError: + pass + if self.general_button_active: + self.general_info_button.setIcon(self.unactive_arrow_icon) + self.general_widget_animation.setStartValue(self.general_widget.height()) + self.general_widget_animation.setEndValue(0) + self.general_widget_animation.finished.connect(self.general_widget.hide) + self.general_button_active = False + else: + self.general_info_button.setIcon(self.active_arrow_icon) + self.general_widget.show() + self.general_widget_animation.setStartValue(0) + self.general_widget_animation.setEndValue(self.original_general_widget_height) + self.general_widget_animation.finished.connect(lambda: self.general_widget.setMaximumHeight(16777215)) + self.general_button_active = True + self.general_widget_animation.start() + + def input_button_toggle(self): + if self.input_option_active: + self.input_param_option_widget.hide() + self.input_param_button.setStyleSheet(self.input_param_button_css_inactive) + self.input_param_button.setIcon(self.input_param_unactive_icon) + self.input_option_active = False + else: + self.input_param_option_widget.show() + self.input_option_active = True + self.input_param_button.setStyleSheet(self.input_param_button_css_active) + self.input_param_button.setIcon(self.input_param_active_icon) + + def close_widget(self): + self.closed.emit() + self.setParent(None) + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) + +# self.main_h_layout.addWidget(ProjectDetailsWidget(), 2) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file From ddc736cdf72bf62b037891e31f81ecd1a821f446 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Mon, 30 Jun 2025 16:44:17 +0530 Subject: [PATCH 04/22] Added widgets for Carbon Emission Cost Data, these widgets can be tested separately --- __pycache__/main_template.cpython-312.pyc | Bin 19956 -> 20078 bytes ...oject_details_right_widget.cpython-312.pyc | Bin 24878 -> 24979 bytes .../tutorial_widget_left.cpython-312.pyc | Bin 11839 -> 11940 bytes .../carbon_emission_cost_data.cpython-312.pyc | Bin 0 -> 12280 bytes .../carbon_emission_cost_data.py | 293 ++++++++++ .../carbon_emission_data.py | 518 ++++++++++++++++++ 6 files changed, 811 insertions(+) create mode 100644 widgets/carbon_emission_data/__pycache__/carbon_emission_cost_data.cpython-312.pyc create mode 100644 widgets/carbon_emission_data/carbon_emission_cost_data.py create mode 100644 widgets/carbon_emission_data/carbon_emission_data.py diff --git a/__pycache__/main_template.cpython-312.pyc b/__pycache__/main_template.cpython-312.pyc index f00829eade4dd80e6142641d0861f109775d126b..127717c941246fc8f6387dd92c643f1cbdc43b07 100644 GIT binary patch delta 865 zcmaiyOK1~O6o&7uCTX732O{Rt8ig7`Z9@}N+o?%SQ%$C^+SY1?h9omKsS{G}Of1R9 zEb7izIk@qGT`I0L8*$-M1a%<_LZBO6D1w4mL3E+_%(PJ4n8nALd;asEb8pVdE&BB~ zb-i#pZG`_m&*cgMVbQgnK3eR&6qJZ$QamN^l$R17n-yQFeX5<37BWmE%L0*F<&Fm% zu#k|comFeV_VAg9?hVl9)=^fTmGfF$*0M!Koh(W-vc}I{;}xyUidn@tX}qW3g0ui{ zdrBte;Vy6@}!?;pjed z7{dtEqHq`+Hm$0xiP>toq8azmN71i`qq7)lEP17vKQBxALj^XmBbey`F*fZNFmKP5 zrD{eoT#jRM0$~`y%h(PYhmSEowKr^oDI34~DKQvKpd^m)tpRsZwQ?+w?=2ThrZi=` z3I*waAD)O$FYcb#cT3$rXKY+XGFC3biZcZ*V`TL|xanDwYO71Qn$0NkIZa=`rn#KM zwKYaq;%$tEIM%P${!&@0DDnWi$3OX#6E#WT{z%xpOoo=p!Ih2kCYq+#!#{`~=%s%F DEO*;n delta 745 zcmaDihw;m7M!wU$yj%=Gkd>R1vD#)Lp9JHqiR!IVObqS}DXc9FDQu}StC>Lx7#N~t zE7>(UHr|tA+^oX%(TFi^vY2VEz%9Oz(vtk5%*34V%#`%hlF1#WvOEqz<;9FZqM^uX z@KPo>AdhAdtdf5P@m~ie`{>AdsjsoP5zlZt@yy zVa9^V2dpDejdBGkb0fkidyr8EV57opg&4~xXW9ad+I-kHn2{Uo1tE|js+$$43~b0DVpfh0pg1l)jPK_E9l0Sy2no7YK+ zk#nS{Obvm9et=F&2SUffV>@%1wUYv(JDn zKM5425KRk!5)dUIgMM+?DR)Br$*(B_g(^prV!4{~P;X-$lY zkhp~#(ddGOD`GSzxNxcB!f4nTjk9sVg2+Y}x?$x#gHdMj&3XLy+(LAAs`LG({BWje7+HtoM<6{mc&4}zABYLFJQzzWcFnmMB9ru0* zGw!hbncnGXMJ^RhS=TIIX0Rx0Iaw3+**I&}W)eDmiaU6YY>o_| zcjRRLFgH&l*W)2PMMm>2H;1W*uL61iLljnkc9k`Y*c>h@WeM9gB}l*kQku9EPJ(_C zkOhnbCMYbo0cY_&YTMTr@Eqi3$dCL9^m4V4&p8t&L^=U>1q@a(k3JDuIL5*R@FH0fjt{^B zV*-Gg;u(MefP+>*5K2nXFiMi52tpj;yEJO|bbhC&29N?c!WCL2yD+%%eOBGIN>&!7 zQ*9YDCySGkX<*n_?4q!O|C6wg*bN!jDC_}v0UQ9{X9fPN(`A`FDzsk=L(*QLC@7pp z#jxM8!eILmF!60~x2lU}MLLIl;(}!W0&Z`y(4n%`exW2Q@zI K`pv-KF7gLORQtF9 delta 976 zcmZ9KT}TvB6vyw`-I?8WoAoP21*z1^O|URiLJ2?CU6)L^))eQ#aorhoY{s!Wvycnn z2L(Y;$)ykKu^y5j4C$q(GAbx~=%I%eBIqT0iJ-#hA-ZQa5}JqK{hxEsJ?GpzhdqV+ z8xVdaNkxQzU#2FfZw{`6tGaUBx=vK0f!Su);(<^&TpX@UQSpeMg7^3Ny{jT;J z0W&BE%_6zT49TID*@k)`9ug<2?;KGDt@xf7D-a@A%!?8KXOt0P<>%|$&!GoO*^AzW zXpsA87*WlU_uHD2$XK&9fL7?QmC|z>#a#I^?ionWTUjTnQ!V4@Jk-bbM~{lzFo(nz zqxBGCccQzYn7xcXhB9`suNg|%qrRpcCnUU&4>_3IgBP?U~M@>fy*sijpzXww=)oLs23?7rEEH zV^^VeC59qIc5utY-vMcJ|vBJ(Tm24#Uxb4&_iIfE@^gcR%Wtgc4Li4 zQSfLPP(+Vj#G5_n&4WkrBq%IM4xYq|2TM^9oVO}<55JxFpFjV%@9pa=PuEg!lgT)e z&)w_mgAcXsRBq|EtR5m0nV2Gm31$jWFw_cFS%`#Kn1oq`M3_djKv!v$#YikThbY>< zn!Cm_B*S!~vnY3QGEBnLfSiP{xt7=NJ3}6W zD)F~kwQpHG0!mgY=X+ykO)ViiC$?Xl7x(ucu^`G%z!f`o#*VEmf5oGje*Cf z#Me>*>mpsQb-dp3y0*Dtc`k?lz(W##YX1@z&BJg5i{cspzQs@O zdzvF#yjd)ASP<6%S#YTC*bTZrLp*2=qy3DYWR5waR)cqC2{XILB)+K_;|V%3L5=sQ f@d@Sk6pWAVA&FhUXB7V)+EVe{{q8P;cM$m-SU#{O delta 688 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 Date: Mon, 30 Jun 2025 16:47:23 +0530 Subject: [PATCH 05/22] Added widgets for Structure Works Data and a GitIgnore file --- .gitignore | 84 +++ .../auxiliary_works_widget.py | 526 +++++++++++++++++ .../structure_works_data/foundation_widget.py | 526 +++++++++++++++++ .../sub_structure_widget.py | 526 +++++++++++++++++ .../super_structure_widget.py | 527 ++++++++++++++++++ 5 files changed, 2189 insertions(+) create mode 100644 .gitignore create mode 100644 widgets/structure_works_data/auxiliary_works_widget.py create mode 100644 widgets/structure_works_data/foundation_widget.py create mode 100644 widgets/structure_works_data/sub_structure_widget.py create mode 100644 widgets/structure_works_data/super_structure_widget.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9006474 --- /dev/null +++ b/.gitignore @@ -0,0 +1,84 @@ +# Python bytecode and cache +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Sphinx documentation +docs/_build/ + +# Jupyter Notebook +.ipynb_checkpoints + +# PyCharm +.idea/ + +# VSCode +.vscode/ + +# Qt Creator & QML +*.user +*.pro.user +*.qbs.user +*.qmlproject.user +*.autosave + +# MacOS +.DS_Store + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini + +# Misc +*.log diff --git a/widgets/structure_works_data/auxiliary_works_widget.py b/widgets/structure_works_data/auxiliary_works_widget.py new file mode 100644 index 0000000..2f9b1a4 --- /dev/null +++ b/widgets/structure_works_data/auxiliary_works_widget.py @@ -0,0 +1,526 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtGui import QIcon +import sys + +class ComponentWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + + self.material_rows = [] # To store references to widgets in each material row + self.current_material_row_idx = 1 # Start index for material rows (0 is header) + + self.init_ui() + + def init_ui(self): + self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) + self.component_first_scroll_content_layout.setSpacing(10) + + # Component Label and Dropdown with a remove button + component_header_layout = QHBoxLayout() + component_label = QLabel("Component:") + component_label.setContentsMargins(0, 5, 0, 5) + component_header_layout.addWidget(component_label) + + self.component_combobox = QComboBox() + self.component_combobox.addItems(["Earthwork", "Concrete", "Steel", "Wood"]) + self.component_combobox.setContentsMargins(0, 5, 0, 5) + component_header_layout.addWidget(self.component_combobox) + + # Add a remove button for the component + self.remove_component_button = QPushButton("x") + self.remove_component_button.setFixedSize(24, 24) + self.remove_component_button.setStyleSheet(""" + QPushButton { + background-color: #FFCCCC; + border: 1px solid #FF9999; + border-radius: 12px; + font-weight: bold; + line-height:12px; + color: #CC0000; + } + QPushButton:hover { + background-color: #FF9999; + color: white; + } + QPushButton:pressed { + background-color: #FF6666; + } + """) + component_header_layout.addWidget(self.remove_component_button) + component_header_layout.addStretch(1) + + self.component_first_scroll_content_layout.addLayout(component_header_layout) + + # --- Material Details Grid Layout --- + self.material_grid_layout = QGridLayout() + self.material_grid_layout.setHorizontalSpacing(10) + self.material_grid_layout.setVerticalSpacing(5) + + # Header Row + headers = ["Type of Material", "Grade", "Quantity", "Unit", "Rate", "Rate Data Source"] + for col, header_text in enumerate(headers): + label = QLabel(header_text) + label.setAlignment(Qt.AlignCenter) + label.setObjectName("MaterialGridLabel") + self.material_grid_layout.addWidget(label, 0, col) + + # Column stretch factors are removed as all input widgets will now have fixed widths. + # This allows the grid to size columns based on the fixed widget sizes and spacing. + + self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) + + # Add initial two material rows + self.add_material_row() + self.add_material_row() + + # --- Add Material Button --- + self.add_material_button = QPushButton("+ Add Material") + self.add_material_button.setObjectName("add_material_button") + self.add_material_button.clicked.connect(self.add_material_row) + self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + + + def add_material_row(self): + row_widgets = {} + row_idx = self.current_material_row_idx + + # Set fixed width for all input widgets to 80px, as requested. + # Note: This might make wider text truncate or appear cramped depending on content. + fixed_input_width = 80 + + type_material_combo = QComboBox() + type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) + type_material_combo.setObjectName("MaterialGridInput") + type_material_combo.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) + row_widgets['type'] = type_material_combo + + grade_combo = QComboBox() + grade_combo.addItems(["A", "B", "C", "X", "Y", "Z", "N/A"]) + grade_combo.setObjectName("MaterialGridInput") + grade_combo.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(grade_combo, row_idx, 1) + row_widgets['grade'] = grade_combo + + quantity_edit = QLineEdit() + quantity_edit.setPlaceholderText("0") + quantity_edit.setObjectName("MaterialGridInput") + quantity_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) + row_widgets['quantity'] = quantity_edit + + unit_combo_m3 = QComboBox() + unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) + unit_combo_m3.setObjectName("MaterialGridInput") + unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others + self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout + row_widgets['unit_m3'] = unit_combo_m3 + + rate_edit = QLineEdit() + rate_edit.setPlaceholderText("0.00") + rate_edit.setObjectName("MaterialGridInput") + rate_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(rate_edit, row_idx, 4) + row_widgets['rate'] = rate_edit + + rate_data_source_edit = QLineEdit() + rate_data_source_edit.setObjectName("MaterialGridInput") + rate_data_source_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) + row_widgets['rate_data_source'] = rate_data_source_edit + + remove_button = QPushButton("x") + remove_button.setFixedSize(24, 24) + remove_button.setStyleSheet(""" + QPushButton { + background-color: #FFCCCC; + border: 1px solid #FF9999; + border-radius: 12px; + font-weight: bold; + line-height:12px; + color: #CC0000; + } + QPushButton:hover { + background-color: #FF9999; + color: white; + } + QPushButton:pressed { + background-color: #FF6666; + } + """) + remove_button.clicked.connect(lambda: self.remove_material_row_by_widgets(row_widgets)) + self.material_grid_layout.addWidget(remove_button, row_idx, 6) + row_widgets['remove_button'] = remove_button + + self.material_rows.append(row_widgets) + self.current_material_row_idx += 1 + self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget + self.adjustSize() # Adjust the size of the component widget + + + def remove_material_row_by_widgets(self, row_widgets_to_remove): + if row_widgets_to_remove not in self.material_rows: + return + + row_idx_in_grid = -1 + for i, row_dict in enumerate(self.material_rows): + if row_dict == row_widgets_to_remove: + row_idx_in_grid = i + 1 # +1 because row 0 is header + break + + if row_idx_in_grid == -1: + return + + for col in range(self.material_grid_layout.columnCount()): + item = self.material_grid_layout.itemAtPosition(row_idx_in_grid, col) + if item: + if item.widget(): + widget = item.widget() + self.material_grid_layout.removeWidget(widget) + widget.deleteLater() + elif item.layout(): + layout = item.layout() + while layout.count(): + sub_item = layout.takeAt(0) + if sub_item.widget(): + sub_item.widget().deleteLater() + self.material_grid_layout.removeItem(layout) + + self.material_rows.remove(row_widgets_to_remove) + self.current_material_row_idx -= 1 + + for r_idx in range(row_idx_in_grid, self.current_material_row_idx + 1): + for c_idx in range(self.material_grid_layout.columnCount()): + item = self.material_grid_layout.itemAtPosition(r_idx + 1, c_idx) + if item: + if item.widget(): + widget = item.widget() + self.material_grid_layout.removeWidget(widget) + self.material_grid_layout.addWidget(widget, r_idx, c_idx) + elif item.layout(): + layout = item.layout() + self.material_grid_layout.removeItem(layout) + self.material_grid_layout.addLayout(layout, r_idx, c_idx) + + self.updateGeometry() # Call updateGeometry on self + self.update() # Call update on self + self.material_grid_layout.invalidate() + self.adjustSize() # Adjust the size of the component widget + +class AuxiliaryWorks(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName("central_panel_widget") + self.component_widgets = [] # To store references to each ComponentWidget instance + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + QScrollArea { + + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + border: 1px solid #000000; + padding-bottom: 20px; + } + + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + QPushButton#top_button_left_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + + /* Styling for component_first_widget (the container for the nested scroll area) */ + #component_first_widget { + background-color: transparent; + margin-top: 10px; /* Add some space above each component block */ + } + + #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + background-color: #FFFFFF; + padding: 10px; + + border-radius: 8px; + } + + /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ + QPushButton#nav_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + min-width: 80px; /* Ensure a minimum width */ + } + QPushButton#nav_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#nav_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + /* Styling for QComboBox */ + QComboBox { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + QComboBox::drop-down { + border: none; + padding-right: 5px; + } + QComboBox::down-arrow { + image: url(resources/country_arrow.png); + width: 30px; + height: 30px; + } + QComboBox QAbstractItemView { + border: 1px solid #DDDCE0; + border-radius: 5px; + background-color: #FFFFFF; + outline: none; + } + QComboBox QAbstractItemView::item:selected { + background-color: #FDEFEF; + color: #000000; + } + QComboBox QAbstractItemView::item:hover { + background-color: #FDEFEF; + } + + /* Styling for material grid elements */ + #MaterialGridLabel { + font-weight: bold; + color: #3F3E5E; + padding: 5px; + text-align: center; + } + #MaterialGridInput { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background-color: #FFFFFF; + } + #MaterialGridInput:focus { + border: 1px solid #DDDCE0; + background-color: #FFFFFF; + } + /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ + QPushButton#add_material_button, QPushButton#add_component_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + } + QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + """) + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Auxiliary Works") + top_h_layout_left_panel.addWidget(top_button_left_panel) + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + + self.scroll_content_layout = QVBoxLayout(scroll_content_widget) + self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setSpacing(0) + + # Create the Add Component button and connect it + self.add_component_button = QPushButton("+ Add Component") + self.add_component_button.setObjectName("add_component_button") + self.add_component_button.clicked.connect(self.add_component_layout) + + # Create the navigation buttons layout + self.button_h_layout = QHBoxLayout() + self.button_h_layout.setSpacing(10) + self.button_h_layout.setContentsMargins(10,10,10,10) + + # Adjust these stretch factors to control the position + self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + + back_button = QPushButton("Back") + back_button.setObjectName("nav_button") + self.button_h_layout.addWidget(back_button) + + next_button = QPushButton("Next") + next_button.setObjectName("nav_button") + self.button_h_layout.addWidget(next_button) + + # Add the initial component layout + self.add_component_layout() + + # Add initial spacing before the navigation buttons + self.scroll_content_layout.addLayout(self.button_h_layout) + left_panel_vlayout.addWidget(self.scroll_area) + + def add_component_layout(self): + new_component = ComponentWidget(self) + self.component_widgets.append(new_component) + new_component.remove_component_button.clicked.connect(lambda: self.remove_component_layout(new_component)) + + # Temporarily remove button_h_layout and add_component_button for insertion + if self.scroll_content_layout.indexOf(self.add_component_button) != -1: + self.scroll_content_layout.removeWidget(self.add_component_button) + if self.scroll_content_layout.indexOf(self.button_h_layout) != -1: + self.scroll_content_layout.removeItem(self.button_h_layout) + + # Insert the new component + self.scroll_content_layout.addWidget(new_component) + + # Re-add the 'Add Component' button + self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) + + # Re-add the navigation buttons layout + self.scroll_content_layout.addLayout(self.button_h_layout) + + self.scroll_area.widget().updateGeometry() + self.scroll_area.widget().adjustSize() + + def remove_component_layout(self, component_to_remove): + if component_to_remove in self.component_widgets: + self.scroll_content_layout.removeWidget(component_to_remove) + self.component_widgets.remove(component_to_remove) + component_to_remove.deleteLater() + self.scroll_area.widget().updateGeometry() + self.scroll_area.widget().adjustSize() + + def expand_scroll_area(self): + self.central_widget.layout().invalidate() + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) + +# self.main_h_layout.addWidget(AuxiliaryWorks(), 2) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file diff --git a/widgets/structure_works_data/foundation_widget.py b/widgets/structure_works_data/foundation_widget.py new file mode 100644 index 0000000..79ad1f0 --- /dev/null +++ b/widgets/structure_works_data/foundation_widget.py @@ -0,0 +1,526 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtGui import QIcon +import sys + +class ComponentWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + + self.material_rows = [] # To store references to widgets in each material row + self.current_material_row_idx = 1 # Start index for material rows (0 is header) + + self.init_ui() + + def init_ui(self): + self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) + self.component_first_scroll_content_layout.setSpacing(10) + + # Component Label and Dropdown with a remove button + component_header_layout = QHBoxLayout() + component_label = QLabel("Component:") + component_label.setContentsMargins(0, 5, 0, 5) + component_header_layout.addWidget(component_label) + + self.component_combobox = QComboBox() + self.component_combobox.addItems(["Earthwork", "Concrete", "Steel", "Wood"]) + self.component_combobox.setContentsMargins(0, 5, 0, 5) + component_header_layout.addWidget(self.component_combobox) + + # Add a remove button for the component + self.remove_component_button = QPushButton("x") + self.remove_component_button.setFixedSize(24, 24) + self.remove_component_button.setStyleSheet(""" + QPushButton { + background-color: #FFCCCC; + border: 1px solid #FF9999; + border-radius: 12px; + font-weight: bold; + line-height:12px; + color: #CC0000; + } + QPushButton:hover { + background-color: #FF9999; + color: white; + } + QPushButton:pressed { + background-color: #FF6666; + } + """) + component_header_layout.addWidget(self.remove_component_button) + component_header_layout.addStretch(1) + + self.component_first_scroll_content_layout.addLayout(component_header_layout) + + # --- Material Details Grid Layout --- + self.material_grid_layout = QGridLayout() + self.material_grid_layout.setHorizontalSpacing(10) + self.material_grid_layout.setVerticalSpacing(5) + + # Header Row + headers = ["Type of Material", "Grade", "Quantity", "Unit", "Rate", "Rate Data Source"] + for col, header_text in enumerate(headers): + label = QLabel(header_text) + label.setAlignment(Qt.AlignCenter) + label.setObjectName("MaterialGridLabel") + self.material_grid_layout.addWidget(label, 0, col) + + # Column stretch factors are removed as all input widgets will now have fixed widths. + # This allows the grid to size columns based on the fixed widget sizes and spacing. + + self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) + + # Add initial two material rows + self.add_material_row() + self.add_material_row() + + # --- Add Material Button --- + self.add_material_button = QPushButton("+ Add Material") + self.add_material_button.setObjectName("add_material_button") + self.add_material_button.clicked.connect(self.add_material_row) + self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + + + def add_material_row(self): + row_widgets = {} + row_idx = self.current_material_row_idx + + # Set fixed width for all input widgets to 80px, as requested. + # Note: This might make wider text truncate or appear cramped depending on content. + fixed_input_width = 80 + + type_material_combo = QComboBox() + type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) + type_material_combo.setObjectName("MaterialGridInput") + type_material_combo.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) + row_widgets['type'] = type_material_combo + + grade_combo = QComboBox() + grade_combo.addItems(["A", "B", "C", "X", "Y", "Z", "N/A"]) + grade_combo.setObjectName("MaterialGridInput") + grade_combo.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(grade_combo, row_idx, 1) + row_widgets['grade'] = grade_combo + + quantity_edit = QLineEdit() + quantity_edit.setPlaceholderText("0") + quantity_edit.setObjectName("MaterialGridInput") + quantity_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) + row_widgets['quantity'] = quantity_edit + + unit_combo_m3 = QComboBox() + unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) + unit_combo_m3.setObjectName("MaterialGridInput") + unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others + self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout + row_widgets['unit_m3'] = unit_combo_m3 + + rate_edit = QLineEdit() + rate_edit.setPlaceholderText("0.00") + rate_edit.setObjectName("MaterialGridInput") + rate_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(rate_edit, row_idx, 4) + row_widgets['rate'] = rate_edit + + rate_data_source_edit = QLineEdit() + rate_data_source_edit.setObjectName("MaterialGridInput") + rate_data_source_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) + row_widgets['rate_data_source'] = rate_data_source_edit + + remove_button = QPushButton("x") + remove_button.setFixedSize(24, 24) + remove_button.setStyleSheet(""" + QPushButton { + background-color: #FFCCCC; + border: 1px solid #FF9999; + border-radius: 12px; + font-weight: bold; + line-height:12px; + color: #CC0000; + } + QPushButton:hover { + background-color: #FF9999; + color: white; + } + QPushButton:pressed { + background-color: #FF6666; + } + """) + remove_button.clicked.connect(lambda: self.remove_material_row_by_widgets(row_widgets)) + self.material_grid_layout.addWidget(remove_button, row_idx, 6) + row_widgets['remove_button'] = remove_button + + self.material_rows.append(row_widgets) + self.current_material_row_idx += 1 + self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget + self.adjustSize() # Adjust the size of the component widget + + + def remove_material_row_by_widgets(self, row_widgets_to_remove): + if row_widgets_to_remove not in self.material_rows: + return + + row_idx_in_grid = -1 + for i, row_dict in enumerate(self.material_rows): + if row_dict == row_widgets_to_remove: + row_idx_in_grid = i + 1 # +1 because row 0 is header + break + + if row_idx_in_grid == -1: + return + + for col in range(self.material_grid_layout.columnCount()): + item = self.material_grid_layout.itemAtPosition(row_idx_in_grid, col) + if item: + if item.widget(): + widget = item.widget() + self.material_grid_layout.removeWidget(widget) + widget.deleteLater() + elif item.layout(): + layout = item.layout() + while layout.count(): + sub_item = layout.takeAt(0) + if sub_item.widget(): + sub_item.widget().deleteLater() + self.material_grid_layout.removeItem(layout) + + self.material_rows.remove(row_widgets_to_remove) + self.current_material_row_idx -= 1 + + for r_idx in range(row_idx_in_grid, self.current_material_row_idx + 1): + for c_idx in range(self.material_grid_layout.columnCount()): + item = self.material_grid_layout.itemAtPosition(r_idx + 1, c_idx) + if item: + if item.widget(): + widget = item.widget() + self.material_grid_layout.removeWidget(widget) + self.material_grid_layout.addWidget(widget, r_idx, c_idx) + elif item.layout(): + layout = item.layout() + self.material_grid_layout.removeItem(layout) + self.material_grid_layout.addLayout(layout, r_idx, c_idx) + + self.updateGeometry() # Call updateGeometry on self + self.update() # Call update on self + self.material_grid_layout.invalidate() + self.adjustSize() # Adjust the size of the component widget + +class Foundation(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName("central_panel_widget") + self.component_widgets = [] # To store references to each ComponentWidget instance + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + QScrollArea { + + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + border: 1px solid #000000; + padding-bottom: 20px; + } + + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + QPushButton#top_button_left_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + + /* Styling for component_first_widget (the container for the nested scroll area) */ + #component_first_widget { + background-color: transparent; + margin-top: 10px; /* Add some space above each component block */ + } + + #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + background-color: #FFFFFF; + padding: 10px; + + border-radius: 8px; + } + + /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ + QPushButton#nav_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + min-width: 80px; /* Ensure a minimum width */ + } + QPushButton#nav_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#nav_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + /* Styling for QComboBox */ + QComboBox { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + QComboBox::drop-down { + border: none; + padding-right: 5px; + } + QComboBox::down-arrow { + image: url(resources/country_arrow.png); + width: 30px; + height: 30px; + } + QComboBox QAbstractItemView { + border: 1px solid #DDDCE0; + border-radius: 5px; + background-color: #FFFFFF; + outline: none; + } + QComboBox QAbstractItemView::item:selected { + background-color: #FDEFEF; + color: #000000; + } + QComboBox QAbstractItemView::item:hover { + background-color: #FDEFEF; + } + + /* Styling for material grid elements */ + #MaterialGridLabel { + font-weight: bold; + color: #3F3E5E; + padding: 5px; + text-align: center; + } + #MaterialGridInput { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background-color: #FFFFFF; + } + #MaterialGridInput:focus { + border: 1px solid #DDDCE0; + background-color: #FFFFFF; + } + /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ + QPushButton#add_material_button, QPushButton#add_component_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + } + QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + """) + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Foundation") + top_h_layout_left_panel.addWidget(top_button_left_panel) + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + + self.scroll_content_layout = QVBoxLayout(scroll_content_widget) + self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setSpacing(0) + + # Create the Add Component button and connect it + self.add_component_button = QPushButton("+ Add Component") + self.add_component_button.setObjectName("add_component_button") + self.add_component_button.clicked.connect(self.add_component_layout) + + # Create the navigation buttons layout + self.button_h_layout = QHBoxLayout() + self.button_h_layout.setSpacing(10) + self.button_h_layout.setContentsMargins(10,10,10,10) + + # Adjust these stretch factors to control the position + self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + + back_button = QPushButton("Back") + back_button.setObjectName("nav_button") + self.button_h_layout.addWidget(back_button) + + next_button = QPushButton("Next") + next_button.setObjectName("nav_button") + self.button_h_layout.addWidget(next_button) + + # Add the initial component layout + self.add_component_layout() + + # Add initial spacing before the navigation buttons + self.scroll_content_layout.addLayout(self.button_h_layout) + left_panel_vlayout.addWidget(self.scroll_area) + + def add_component_layout(self): + new_component = ComponentWidget(self) + self.component_widgets.append(new_component) + new_component.remove_component_button.clicked.connect(lambda: self.remove_component_layout(new_component)) + + # Temporarily remove button_h_layout and add_component_button for insertion + if self.scroll_content_layout.indexOf(self.add_component_button) != -1: + self.scroll_content_layout.removeWidget(self.add_component_button) + if self.scroll_content_layout.indexOf(self.button_h_layout) != -1: + self.scroll_content_layout.removeItem(self.button_h_layout) + + # Insert the new component + self.scroll_content_layout.addWidget(new_component) + + # Re-add the 'Add Component' button + self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) + + # Re-add the navigation buttons layout + self.scroll_content_layout.addLayout(self.button_h_layout) + + self.scroll_area.widget().updateGeometry() + self.scroll_area.widget().adjustSize() + + def remove_component_layout(self, component_to_remove): + if component_to_remove in self.component_widgets: + self.scroll_content_layout.removeWidget(component_to_remove) + self.component_widgets.remove(component_to_remove) + component_to_remove.deleteLater() + self.scroll_area.widget().updateGeometry() + self.scroll_area.widget().adjustSize() + + def expand_scroll_area(self): + self.central_widget.layout().invalidate() + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) + +# self.main_h_layout.addWidget(Foundation(), 2) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file diff --git a/widgets/structure_works_data/sub_structure_widget.py b/widgets/structure_works_data/sub_structure_widget.py new file mode 100644 index 0000000..29d8beb --- /dev/null +++ b/widgets/structure_works_data/sub_structure_widget.py @@ -0,0 +1,526 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtGui import QIcon +import sys + +class ComponentWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + + self.material_rows = [] # To store references to widgets in each material row + self.current_material_row_idx = 1 # Start index for material rows (0 is header) + + self.init_ui() + + def init_ui(self): + self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) + self.component_first_scroll_content_layout.setSpacing(10) + + # Component Label and Dropdown with a remove button + component_header_layout = QHBoxLayout() + component_label = QLabel("Component:") + component_label.setContentsMargins(0, 5, 0, 5) + component_header_layout.addWidget(component_label) + + self.component_combobox = QComboBox() + self.component_combobox.addItems(["Earthwork", "Concrete", "Steel", "Wood"]) + self.component_combobox.setContentsMargins(0, 5, 0, 5) + component_header_layout.addWidget(self.component_combobox) + + # Add a remove button for the component + self.remove_component_button = QPushButton("x") + self.remove_component_button.setFixedSize(24, 24) + self.remove_component_button.setStyleSheet(""" + QPushButton { + background-color: #FFCCCC; + border: 1px solid #FF9999; + border-radius: 12px; + font-weight: bold; + line-height:12px; + color: #CC0000; + } + QPushButton:hover { + background-color: #FF9999; + color: white; + } + QPushButton:pressed { + background-color: #FF6666; + } + """) + component_header_layout.addWidget(self.remove_component_button) + component_header_layout.addStretch(1) + + self.component_first_scroll_content_layout.addLayout(component_header_layout) + + # --- Material Details Grid Layout --- + self.material_grid_layout = QGridLayout() + self.material_grid_layout.setHorizontalSpacing(10) + self.material_grid_layout.setVerticalSpacing(5) + + # Header Row + headers = ["Type of Material", "Grade", "Quantity", "Unit", "Rate", "Rate Data Source"] + for col, header_text in enumerate(headers): + label = QLabel(header_text) + label.setAlignment(Qt.AlignCenter) + label.setObjectName("MaterialGridLabel") + self.material_grid_layout.addWidget(label, 0, col) + + # Column stretch factors are removed as all input widgets will now have fixed widths. + # This allows the grid to size columns based on the fixed widget sizes and spacing. + + self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) + + # Add initial two material rows + self.add_material_row() + self.add_material_row() + + # --- Add Material Button --- + self.add_material_button = QPushButton("+ Add Material") + self.add_material_button.setObjectName("add_material_button") + self.add_material_button.clicked.connect(self.add_material_row) + self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + + + def add_material_row(self): + row_widgets = {} + row_idx = self.current_material_row_idx + + # Set fixed width for all input widgets to 80px, as requested. + # Note: This might make wider text truncate or appear cramped depending on content. + fixed_input_width = 80 + + type_material_combo = QComboBox() + type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) + type_material_combo.setObjectName("MaterialGridInput") + type_material_combo.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) + row_widgets['type'] = type_material_combo + + grade_combo = QComboBox() + grade_combo.addItems(["A", "B", "C", "X", "Y", "Z", "N/A"]) + grade_combo.setObjectName("MaterialGridInput") + grade_combo.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(grade_combo, row_idx, 1) + row_widgets['grade'] = grade_combo + + quantity_edit = QLineEdit() + quantity_edit.setPlaceholderText("0") + quantity_edit.setObjectName("MaterialGridInput") + quantity_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) + row_widgets['quantity'] = quantity_edit + + unit_combo_m3 = QComboBox() + unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) + unit_combo_m3.setObjectName("MaterialGridInput") + unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others + self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout + row_widgets['unit_m3'] = unit_combo_m3 + + rate_edit = QLineEdit() + rate_edit.setPlaceholderText("0.00") + rate_edit.setObjectName("MaterialGridInput") + rate_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(rate_edit, row_idx, 4) + row_widgets['rate'] = rate_edit + + rate_data_source_edit = QLineEdit() + rate_data_source_edit.setObjectName("MaterialGridInput") + rate_data_source_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) + row_widgets['rate_data_source'] = rate_data_source_edit + + remove_button = QPushButton("x") + remove_button.setFixedSize(24, 24) + remove_button.setStyleSheet(""" + QPushButton { + background-color: #FFCCCC; + border: 1px solid #FF9999; + border-radius: 12px; + font-weight: bold; + line-height:12px; + color: #CC0000; + } + QPushButton:hover { + background-color: #FF9999; + color: white; + } + QPushButton:pressed { + background-color: #FF6666; + } + """) + remove_button.clicked.connect(lambda: self.remove_material_row_by_widgets(row_widgets)) + self.material_grid_layout.addWidget(remove_button, row_idx, 6) + row_widgets['remove_button'] = remove_button + + self.material_rows.append(row_widgets) + self.current_material_row_idx += 1 + self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget + self.adjustSize() # Adjust the size of the component widget + + + def remove_material_row_by_widgets(self, row_widgets_to_remove): + if row_widgets_to_remove not in self.material_rows: + return + + row_idx_in_grid = -1 + for i, row_dict in enumerate(self.material_rows): + if row_dict == row_widgets_to_remove: + row_idx_in_grid = i + 1 # +1 because row 0 is header + break + + if row_idx_in_grid == -1: + return + + for col in range(self.material_grid_layout.columnCount()): + item = self.material_grid_layout.itemAtPosition(row_idx_in_grid, col) + if item: + if item.widget(): + widget = item.widget() + self.material_grid_layout.removeWidget(widget) + widget.deleteLater() + elif item.layout(): + layout = item.layout() + while layout.count(): + sub_item = layout.takeAt(0) + if sub_item.widget(): + sub_item.widget().deleteLater() + self.material_grid_layout.removeItem(layout) + + self.material_rows.remove(row_widgets_to_remove) + self.current_material_row_idx -= 1 + + for r_idx in range(row_idx_in_grid, self.current_material_row_idx + 1): + for c_idx in range(self.material_grid_layout.columnCount()): + item = self.material_grid_layout.itemAtPosition(r_idx + 1, c_idx) + if item: + if item.widget(): + widget = item.widget() + self.material_grid_layout.removeWidget(widget) + self.material_grid_layout.addWidget(widget, r_idx, c_idx) + elif item.layout(): + layout = item.layout() + self.material_grid_layout.removeItem(layout) + self.material_grid_layout.addLayout(layout, r_idx, c_idx) + + self.updateGeometry() # Call updateGeometry on self + self.update() # Call update on self + self.material_grid_layout.invalidate() + self.adjustSize() # Adjust the size of the component widget + +class SubStructure(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName("central_panel_widget") + self.component_widgets = [] # To store references to each ComponentWidget instance + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + QScrollArea { + + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + border: 1px solid #000000; + padding-bottom: 20px; + } + + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + QPushButton#top_button_left_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + + /* Styling for component_first_widget (the container for the nested scroll area) */ + #component_first_widget { + background-color: transparent; + margin-top: 10px; /* Add some space above each component block */ + } + + #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + background-color: #FFFFFF; + padding: 10px; + + border-radius: 8px; + } + + /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ + QPushButton#nav_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + min-width: 80px; /* Ensure a minimum width */ + } + QPushButton#nav_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#nav_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + /* Styling for QComboBox */ + QComboBox { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + QComboBox::drop-down { + border: none; + padding-right: 5px; + } + QComboBox::down-arrow { + image: url(resources/country_arrow.png); + width: 30px; + height: 30px; + } + QComboBox QAbstractItemView { + border: 1px solid #DDDCE0; + border-radius: 5px; + background-color: #FFFFFF; + outline: none; + } + QComboBox QAbstractItemView::item:selected { + background-color: #FDEFEF; + color: #000000; + } + QComboBox QAbstractItemView::item:hover { + background-color: #FDEFEF; + } + + /* Styling for material grid elements */ + #MaterialGridLabel { + font-weight: bold; + color: #3F3E5E; + padding: 5px; + text-align: center; + } + #MaterialGridInput { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background-color: #FFFFFF; + } + #MaterialGridInput:focus { + border: 1px solid #DDDCE0; + background-color: #FFFFFF; + } + /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ + QPushButton#add_material_button, QPushButton#add_component_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + } + QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + """) + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Sub-Structure") + top_h_layout_left_panel.addWidget(top_button_left_panel) + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + + self.scroll_content_layout = QVBoxLayout(scroll_content_widget) + self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setSpacing(0) + + # Create the Add Component button and connect it + self.add_component_button = QPushButton("+ Add Component") + self.add_component_button.setObjectName("add_component_button") + self.add_component_button.clicked.connect(self.add_component_layout) + + # Create the navigation buttons layout + self.button_h_layout = QHBoxLayout() + self.button_h_layout.setSpacing(10) + self.button_h_layout.setContentsMargins(10,10,10,10) + + # Adjust these stretch factors to control the position + self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + + back_button = QPushButton("Back") + back_button.setObjectName("nav_button") + self.button_h_layout.addWidget(back_button) + + next_button = QPushButton("Next") + next_button.setObjectName("nav_button") + self.button_h_layout.addWidget(next_button) + + # Add the initial component layout + self.add_component_layout() + + # Add initial spacing before the navigation buttons + self.scroll_content_layout.addLayout(self.button_h_layout) + left_panel_vlayout.addWidget(self.scroll_area) + + def add_component_layout(self): + new_component = ComponentWidget(self) + self.component_widgets.append(new_component) + new_component.remove_component_button.clicked.connect(lambda: self.remove_component_layout(new_component)) + + # Temporarily remove button_h_layout and add_component_button for insertion + if self.scroll_content_layout.indexOf(self.add_component_button) != -1: + self.scroll_content_layout.removeWidget(self.add_component_button) + if self.scroll_content_layout.indexOf(self.button_h_layout) != -1: + self.scroll_content_layout.removeItem(self.button_h_layout) + + # Insert the new component + self.scroll_content_layout.addWidget(new_component) + + # Re-add the 'Add Component' button + self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) + + # Re-add the navigation buttons layout + self.scroll_content_layout.addLayout(self.button_h_layout) + + self.scroll_area.widget().updateGeometry() + self.scroll_area.widget().adjustSize() + + def remove_component_layout(self, component_to_remove): + if component_to_remove in self.component_widgets: + self.scroll_content_layout.removeWidget(component_to_remove) + self.component_widgets.remove(component_to_remove) + component_to_remove.deleteLater() + self.scroll_area.widget().updateGeometry() + self.scroll_area.widget().adjustSize() + + def expand_scroll_area(self): + self.central_widget.layout().invalidate() + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) + +# self.main_h_layout.addWidget(SubStructure(), 2) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file diff --git a/widgets/structure_works_data/super_structure_widget.py b/widgets/structure_works_data/super_structure_widget.py new file mode 100644 index 0000000..cecbacb --- /dev/null +++ b/widgets/structure_works_data/super_structure_widget.py @@ -0,0 +1,527 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtGui import QIcon +import sys + +class ComponentWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + + self.material_rows = [] # To store references to widgets in each material row + self.current_material_row_idx = 1 # Start index for material rows (0 is header) + + self.init_ui() + + def init_ui(self): + self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) + self.component_first_scroll_content_layout.setSpacing(10) + + # Component Label and Dropdown with a remove button + component_header_layout = QHBoxLayout() + component_label = QLabel("Component:") + component_label.setContentsMargins(0, 5, 0, 5) + component_header_layout.addWidget(component_label) + + self.component_combobox = QComboBox() + self.component_combobox.addItems(["Earthwork", "Concrete", "Steel", "Wood"]) + self.component_combobox.setContentsMargins(0, 5, 0, 5) + component_header_layout.addWidget(self.component_combobox) + + # Add a remove button for the component + self.remove_component_button = QPushButton("x") + self.remove_component_button.setFixedSize(24, 24) + self.remove_component_button.setStyleSheet(""" + QPushButton { + background-color: #FFCCCC; + border: 1px solid #FF9999; + border-radius: 12px; + font-weight: bold; + line-height:12px; + color: #CC0000; + } + QPushButton:hover { + background-color: #FF9999; + color: white; + } + QPushButton:pressed { + background-color: #FF6666; + } + """) + component_header_layout.addWidget(self.remove_component_button) + component_header_layout.addStretch(1) + + self.component_first_scroll_content_layout.addLayout(component_header_layout) + + # --- Material Details Grid Layout --- + self.material_grid_layout = QGridLayout() + self.material_grid_layout.setHorizontalSpacing(10) + self.material_grid_layout.setVerticalSpacing(5) + + # Header Row + headers = ["Type of Material", "Grade", "Quantity", "Unit", "Rate", "Rate Data Source"] + for col, header_text in enumerate(headers): + label = QLabel(header_text) + label.setAlignment(Qt.AlignCenter) + label.setObjectName("MaterialGridLabel") + self.material_grid_layout.addWidget(label, 0, col) + + # Column stretch factors are removed as all input widgets will now have fixed widths. + # This allows the grid to size columns based on the fixed widget sizes and spacing. + + self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) + + # Add initial two material rows + self.add_material_row() + self.add_material_row() + + # --- Add Material Button --- + self.add_material_button = QPushButton("+ Add Material") + self.add_material_button.setObjectName("add_material_button") + self.add_material_button.clicked.connect(self.add_material_row) + self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + + + def add_material_row(self): + row_widgets = {} + row_idx = self.current_material_row_idx + + # Set fixed width for all input widgets to 80px, as requested. + # Note: This might make wider text truncate or appear cramped depending on content. + fixed_input_width = 80 + + type_material_combo = QComboBox() + type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) + type_material_combo.setObjectName("MaterialGridInput") + type_material_combo.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) + row_widgets['type'] = type_material_combo + + grade_combo = QComboBox() + grade_combo.addItems(["A", "B", "C", "X", "Y", "Z", "N/A"]) + grade_combo.setObjectName("MaterialGridInput") + grade_combo.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(grade_combo, row_idx, 1) + row_widgets['grade'] = grade_combo + + quantity_edit = QLineEdit() + quantity_edit.setPlaceholderText("0") + quantity_edit.setObjectName("MaterialGridInput") + quantity_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) + row_widgets['quantity'] = quantity_edit + + unit_combo_m3 = QComboBox() + unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) + unit_combo_m3.setObjectName("MaterialGridInput") + unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others + self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout + row_widgets['unit_m3'] = unit_combo_m3 + + rate_edit = QLineEdit() + rate_edit.setPlaceholderText("0.00") + rate_edit.setObjectName("MaterialGridInput") + rate_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(rate_edit, row_idx, 4) + row_widgets['rate'] = rate_edit + + rate_data_source_edit = QLineEdit() + rate_data_source_edit.setObjectName("MaterialGridInput") + rate_data_source_edit.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) + row_widgets['rate_data_source'] = rate_data_source_edit + + remove_button = QPushButton("x") + remove_button.setFixedSize(24, 24) + remove_button.setStyleSheet(""" + QPushButton { + background-color: #FFCCCC; + border: 1px solid #FF9999; + border-radius: 12px; + font-weight: bold; + line-height:12px; + color: #CC0000; + } + QPushButton:hover { + background-color: #FF9999; + color: white; + } + QPushButton:pressed { + background-color: #FF6666; + } + """) + remove_button.clicked.connect(lambda: self.remove_material_row_by_widgets(row_widgets)) + self.material_grid_layout.addWidget(remove_button, row_idx, 6) + row_widgets['remove_button'] = remove_button + + self.material_rows.append(row_widgets) + self.current_material_row_idx += 1 + self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget + self.adjustSize() # Adjust the size of the component widget + + + def remove_material_row_by_widgets(self, row_widgets_to_remove): + if row_widgets_to_remove not in self.material_rows: + return + + row_idx_in_grid = -1 + for i, row_dict in enumerate(self.material_rows): + if row_dict == row_widgets_to_remove: + row_idx_in_grid = i + 1 # +1 because row 0 is header + break + + if row_idx_in_grid == -1: + return + + for col in range(self.material_grid_layout.columnCount()): + item = self.material_grid_layout.itemAtPosition(row_idx_in_grid, col) + if item: + if item.widget(): + widget = item.widget() + self.material_grid_layout.removeWidget(widget) + widget.deleteLater() + elif item.layout(): + layout = item.layout() + while layout.count(): + sub_item = layout.takeAt(0) + if sub_item.widget(): + sub_item.widget().deleteLater() + self.material_grid_layout.removeItem(layout) + + self.material_rows.remove(row_widgets_to_remove) + self.current_material_row_idx -= 1 + + for r_idx in range(row_idx_in_grid, self.current_material_row_idx + 1): + for c_idx in range(self.material_grid_layout.columnCount()): + item = self.material_grid_layout.itemAtPosition(r_idx + 1, c_idx) + if item: + if item.widget(): + widget = item.widget() + self.material_grid_layout.removeWidget(widget) + self.material_grid_layout.addWidget(widget, r_idx, c_idx) + elif item.layout(): + layout = item.layout() + self.material_grid_layout.removeItem(layout) + self.material_grid_layout.addLayout(layout, r_idx, c_idx) + + self.updateGeometry() # Call updateGeometry on self + self.update() # Call update on self + self.material_grid_layout.invalidate() + self.adjustSize() # Adjust the size of the component widget + +class SuperStructure(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName("central_panel_widget") + self.component_widgets = [] # To store references to each ComponentWidget instance + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + QScrollArea { + + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + border: 1px solid #000000; + padding-bottom: 20px; + } + + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + QPushButton#top_button_left_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + + /* Styling for component_first_widget (the container for the nested scroll area) */ + #component_first_widget { + background-color: transparent; + margin-top: 10px; /* Add some space above each component block */ + } + + #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + background-color: #FFFFFF; + padding: 10px; + + border-radius: 8px; + } + + /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ + QPushButton#nav_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + min-width: 80px; /* Ensure a minimum width */ + } + QPushButton#nav_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#nav_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + /* Styling for QComboBox */ + QComboBox { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + QComboBox::drop-down { + border: none; + padding-right: 5px; + } + QComboBox::down-arrow { + image: url(resources/country_arrow.png); + width: 30px; + height: 30px; + } + QComboBox QAbstractItemView { + border: 1px solid #DDDCE0; + border-radius: 5px; + background-color: #FFFFFF; + outline: none; + } + QComboBox QAbstractItemView::item:selected { + background-color: #FDEFEF; + color: #000000; + } + QComboBox QAbstractItemView::item:hover { + background-color: #FDEFEF; + } + + /* Styling for material grid elements */ + #MaterialGridLabel { + font-weight: bold; + color: #3F3E5E; + padding: 5px; + text-align: center; + } + #MaterialGridInput { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background-color: #FFFFFF; + } + #MaterialGridInput:focus { + border: 1px solid #DDDCE0; + background-color: #FFFFFF; + } + /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ + QPushButton#add_material_button, QPushButton#add_component_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + } + QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + """) + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Super-Structure") + top_h_layout_left_panel.addWidget(top_button_left_panel) + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + + self.scroll_content_layout = QVBoxLayout(scroll_content_widget) + self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setSpacing(0) + + # Create the Add Component button and connect it + self.add_component_button = QPushButton("+ Add Component") + self.add_component_button.setObjectName("add_component_button") + self.add_component_button.clicked.connect(self.add_component_layout) + + # Create the navigation buttons layout + self.button_h_layout = QHBoxLayout() + self.button_h_layout.setSpacing(10) + self.button_h_layout.setContentsMargins(10,10,10,10) + + # Adjust these stretch factors to control the position + self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + + back_button = QPushButton("Back") + back_button.setObjectName("nav_button") + self.button_h_layout.addWidget(back_button) + + next_button = QPushButton("Next") + next_button.setObjectName("nav_button") + self.button_h_layout.addWidget(next_button) + + # Add the initial component layout + self.add_component_layout() + + # Add initial spacing before the navigation buttons + self.scroll_content_layout.addLayout(self.button_h_layout) + left_panel_vlayout.addWidget(self.scroll_area) + + def add_component_layout(self): + new_component = ComponentWidget(self) + self.component_widgets.append(new_component) + new_component.remove_component_button.clicked.connect(lambda: self.remove_component_layout(new_component)) + + # Temporarily remove button_h_layout and add_component_button for insertion + if self.scroll_content_layout.indexOf(self.add_component_button) != -1: + self.scroll_content_layout.removeWidget(self.add_component_button) + if self.scroll_content_layout.indexOf(self.button_h_layout) != -1: + self.scroll_content_layout.removeItem(self.button_h_layout) + + # Insert the new component + self.scroll_content_layout.addWidget(new_component) + + # Re-add the 'Add Component' button + self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) + + # Re-add the navigation buttons layout + self.scroll_content_layout.addLayout(self.button_h_layout) + + self.scroll_area.widget().updateGeometry() + self.scroll_area.widget().adjustSize() + + def remove_component_layout(self, component_to_remove): + if component_to_remove in self.component_widgets: + self.scroll_content_layout.removeWidget(component_to_remove) + self.component_widgets.remove(component_to_remove) + component_to_remove.deleteLater() + self.scroll_area.widget().updateGeometry() + self.scroll_area.widget().adjustSize() + + def expand_scroll_area(self): + self.central_widget.layout().invalidate() + + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) + +# self.main_h_layout.addWidget(SuperStructure(), 2) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file From d911e0ea33e960303f9ec3a83f6e87ac4d57dc67 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Mon, 30 Jun 2025 16:49:10 +0530 Subject: [PATCH 06/22] Added widgets for Maintenance and Repair Data *All Widgets can be tested Separately --- widgets/maintenance_repair_data.py | 346 +++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 widgets/maintenance_repair_data.py diff --git a/widgets/maintenance_repair_data.py b/widgets/maintenance_repair_data.py new file mode 100644 index 0000000..ca0efa0 --- /dev/null +++ b/widgets/maintenance_repair_data.py @@ -0,0 +1,346 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtGui import QIcon +import sys +import os + +class ComponentWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + + self.init_ui() + + def init_ui(self): + self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) + self.component_first_scroll_content_layout.setSpacing(10) + + + +class MaintenanceRepairData(QWidget): + def __init__(self): + super().__init__() + + self.component_widgets = [] + + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + QScrollArea { + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + border: 1px solid #000000; + padding-bottom: 20px; + } + + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + QPushButton#top_button_left_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + + /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ + QPushButton#nav_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + min-width: 80px; /* Ensure a minimum width */ + } + QPushButton#nav_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#nav_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + /* (Removed: QComboBox and material grid element CSS) */ + """) + + self.setObjectName("central_panel_widget") + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Maintenance and Repair Data ") + top_h_layout_left_panel.addWidget(top_button_left_panel) + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + + self.scroll_content_layout = QVBoxLayout(scroll_content_widget) + self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setSpacing(0) + + # --- Add General Info Form at the top of the scroll area --- + self.general_widget = QWidget() + self.general_layout = QVBoxLayout(self.general_widget) + self.general_layout.setContentsMargins(10, 20, 10, 10) + self.general_layout.setSpacing(10) + + grid_layout = QGridLayout() + grid_layout.setHorizontalSpacing(10) + grid_layout.setVerticalSpacing(20) + + field_width = 200 + + # 1. Periodic Maintenance Cost rate as percentage to total construction cost + label1 = QLabel("Periodic Maintenance Cost rate as\npercentage to total construction\ncost") + label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + pmc_input = QLineEdit() + pmc_input.setAlignment(Qt.AlignmentFlag.AlignTop) + pmc_input.setFixedWidth(field_width) + pmc_input.setText("0.555") + pmc_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + pmc_unit = QLabel("(%)") + pmc_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label1, 0, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_input, 0, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_unit, 0, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_suggested, 0, 3, alignment=Qt.AlignVCenter) + + # 2. Annual Routine Inspection cost rate as percentage of total construction cost + label2 = QLabel("Annual Routine Inspection cost rate\nas percentage of total construction\ncost") + label2.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + ari_input = QLineEdit() + ari_input.setAlignment(Qt.AlignmentFlag.AlignTop) + ari_input.setFixedWidth(field_width) + ari_input.setText("1") + ari_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + ari_unit = QLabel("(%)") + ari_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label2, 1, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(ari_input, 1, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(ari_unit, 1, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(ari_suggested, 1, 3, alignment=Qt.AlignVCenter) + + # 3. Repair and Rehabilitation cost rate as percentage of total construction cost + label3 = QLabel("Repair and Rehabilitation cost rate as\npercentage of total construction cost") + label3.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + rr_input = QLineEdit() + rr_input.setAlignment(Qt.AlignmentFlag.AlignTop) + rr_input.setFixedWidth(field_width) + rr_input.setText("10") + rr_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + rr_unit = QLabel("(%)") + rr_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label3, 2, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(rr_input, 2, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(rr_unit, 2, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(rr_suggested, 2, 3, alignment=Qt.AlignVCenter) + + # 4. Frequency of Periodic Maintenance + label4 = QLabel("Frequency of Periodic Maintenance") + label4.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + fpm_input = QLineEdit() + fpm_input.setAlignment(Qt.AlignmentFlag.AlignTop) + fpm_input.setFixedWidth(field_width) + fpm_input.setText("5") + fpm_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + fpm_unit = QLabel("(years)") + fpm_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label4, 3, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fpm_input, 3, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fpm_unit, 3, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fpm_suggested, 3, 3, alignment=Qt.AlignVCenter) + + # 5. Frequency of Routine Inspection + label5 = QLabel("Frequency of Routine Inspection") + label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + fri_input = QLineEdit() + fri_input.setAlignment(Qt.AlignmentFlag.AlignTop) + fri_input.setFixedWidth(field_width) + fri_input.setText("1") + fri_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + fri_unit = QLabel("(years)") + fri_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label5, 4, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_input, 4, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_unit, 4, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_suggested, 4, 3, alignment=Qt.AlignVCenter) + + self.general_layout.addLayout(grid_layout) + self.general_layout.addStretch(1) + self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) + + # Create the navigation buttons layout + self.button_h_layout = QHBoxLayout() + self.button_h_layout.setSpacing(10) + self.button_h_layout.setContentsMargins(10,10,10,10) + + # Adjust these stretch factors to control the position + self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + + back_button = QPushButton("Back") + back_button.setObjectName("nav_button") + self.button_h_layout.addWidget(back_button) + + next_button = QPushButton("Next") + next_button.setObjectName("nav_button") + self.button_h_layout.addWidget(next_button) + + + # Add initial spacing before the navigation buttons + self.scroll_content_layout.addLayout(self.button_h_layout) + + # --- Add a corner spacer to the scroll_content_layout --- + self.button_h_layout.addSpacerItem(QSpacerItem(20, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) + self.scroll_content_layout.addSpacerItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) + + left_panel_vlayout.addWidget(self.scroll_area) + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) + +# self.main_h_layout.addWidget(MaintenanceRepairData(), 2) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file From 667af9a3553c5e5873c75f179edb32cc8be1d999 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Fri, 4 Jul 2025 17:02:11 +0530 Subject: [PATCH 07/22] Added Project Details Left Widget Added Bridge and Traffic Data Widget Added Demolition and Recycling Data Added Financial Data --- resources/arrow_down_selected.png | Bin 0 -> 4267 bytes resources/arrow_up.png | Bin 93 -> 0 bytes resources/file_button.png | Bin 1402 -> 0 bytes resources/play-button-selected.png | Bin 0 -> 4537 bytes widgets/bridge_and_traffic_data.py | 417 ++++++++++++++++++ widgets/demolition_and_recycling_data.py | 306 +++++++++++++ widgets/financial_data.py | 316 +++++++++++++ widgets/project_details_right_widget.py | 12 +- .../project_details_left_widget.py | 379 ++++++++++++++++ 9 files changed, 1429 insertions(+), 1 deletion(-) create mode 100644 resources/arrow_down_selected.png delete mode 100644 resources/arrow_up.png delete mode 100644 resources/file_button.png create mode 100644 resources/play-button-selected.png create mode 100644 widgets/bridge_and_traffic_data.py create mode 100644 widgets/demolition_and_recycling_data.py create mode 100644 widgets/financial_data.py create mode 100644 widgets/structure_works_data/project_details_left_widget.py diff --git a/resources/arrow_down_selected.png b/resources/arrow_down_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..9a4ad609dd82e9b974cc7131e2b05f79f958535b GIT binary patch literal 4267 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 Oa(KA0U2B{D|8 diff --git a/resources/file_button.png b/resources/file_button.png deleted file mode 100644 index a6b579f2f748ebc18b4f1b8a4e4d6ed0dba70722..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1402 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 W{ov!T=FDTfhs+N9=2D<^_x}M+^Qh+l literal 0 HcmV?d00001 diff --git a/widgets/bridge_and_traffic_data.py b/widgets/bridge_and_traffic_data.py new file mode 100644 index 0000000..3d77d63 --- /dev/null +++ b/widgets/bridge_and_traffic_data.py @@ -0,0 +1,417 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtGui import QIcon +import sys +import os + +class BridgeAndTrafficData(QWidget): + def __init__(self): + super().__init__() + self.text_box_width = 200 + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + QScrollArea { + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + border: 1px solid #000000; + padding-bottom: 20px; + } + + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + QPushButton#top_button_left_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + + /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ + QPushButton#nav_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + min-width: 80px; /* Ensure a minimum width */ + } + QPushButton#nav_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#nav_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + + /* QComboBox global style with country_arrow.png as dropdown arrow */ + QComboBox { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + QComboBox::drop-down { + border: none; + padding-right: 5px; + } + QComboBox::down-arrow { + image: url(resources/country_arrow.png); + width: 18px; + height: 18px; + } + QComboBox QAbstractItemView { + border: 1px solid #DDDCE0; + border-radius: 5px; + background-color: #FFFFFF; + outline: none; + } + QComboBox QAbstractItemView::item:selected { + background-color: #FDEFEF; + color: #000000; + } + QComboBox QAbstractItemView::item:hover { + background-color: #FDEFEF; + } + """) + + self.setObjectName("central_panel_widget") + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Carbon Emission Cost Data ") + top_h_layout_left_panel.addWidget(top_button_left_panel) + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + + self.scroll_content_layout = QVBoxLayout(scroll_content_widget) + self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setSpacing(0) + + # --- Add General Info Form at the top of the scroll area --- + self.general_widget = QWidget() + self.general_layout = QVBoxLayout(self.general_widget) + self.general_layout.setContentsMargins(10, 20, 10, 10) + self.general_layout.setSpacing(10) + + grid_layout = QGridLayout() + grid_layout.setHorizontalSpacing(10) + grid_layout.setVerticalSpacing(20) + + # Number of Lanes + label = QLabel("Number of Lanes") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 0, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) + valuer_combo.setFixedWidth(self.text_box_width) + valuer_combo.setPlaceholderText("Select") + valuer_combo.addItem("1") + valuer_combo.addItem("2") + valuer_combo.addItem("3") + valuer_combo.addItem("4") + valuer_combo.addItem("5+") + grid_layout.addWidget(valuer_combo, 0, 1, 1, 1) + + info_icon = QLabel(" ") + info_icon.setStyleSheet("color: grey; font-size: 14px;") + info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(info_icon, 0, 2, 1, 1) + + # Additional Re-Route Distance + label = QLabel("Additional Re-Route Distance") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 1, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + input_widget.setFixedWidth(self.text_box_width) + input_widget.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + grid_layout.addWidget(input_widget, 1, 1, 1, 1) + info_icon = QLabel("(km)") + info_icon.setStyleSheet("color: grey; font-size: 14px;") + info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(info_icon, 1, 2, 1, 1) + + # Road Roughness + label = QLabel("Road Roughness") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 3, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) + valuer_combo.setFixedWidth(self.text_box_width) + valuer_combo.setPlaceholderText("Select") + valuer_combo.addItem("option a") + valuer_combo.addItem("option b") + valuer_combo.addItem("option c") + valuer_combo.addItem("option d") + grid_layout.addWidget(valuer_combo, 3, 1, 1, 1) + info_icon = QLabel("(mm/km)") + info_icon.setStyleSheet("color: grey; font-size: 14px;") + info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(info_icon, 3, 2, 1, 1) + + # Road Rise and Fall (RF) + label = QLabel("Road Rise and Fall (RF)") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 4, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + input_widget.setFixedWidth(self.text_box_width) + input_widget.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + grid_layout.addWidget(input_widget, 4, 1, 1, 1) + + info_icon = QLabel("(m/km)") + info_icon.setStyleSheet("color: grey; font-size: 14px;") + info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(info_icon, 4, 2, 1, 1) + + # Type of Road + label = QLabel("Type of Road") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 5, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) + valuer_combo.setFixedWidth(self.text_box_width) + valuer_combo.setPlaceholderText("Select") + valuer_combo.addItem("option a") + valuer_combo.addItem("option b") + valuer_combo.addItem("option c") + valuer_combo.addItem("option d") + grid_layout.addWidget(valuer_combo, 5, 1, 1, 1) + + + info_icon = QLabel(" ") + info_icon.setStyleSheet("color: grey; font-size: 14px;") + info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(info_icon, 5, 2, 1, 1) + + # Annual Increase in Traffic + label = QLabel("Annual Increaase in Traffic if Re-Routing duration increases more than a year ") + label.setFixedWidth(200) + label.setWordWrap(True) + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 6, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) + valuer_combo.setFixedWidth(self.text_box_width) + valuer_combo.setPlaceholderText("Select") + valuer_combo.addItem("option a") + valuer_combo.addItem("option b") + valuer_combo.addItem("option c") + valuer_combo.addItem("option d") + grid_layout.addWidget(valuer_combo, 6, 1, 1, 1) + + info_icon = QLabel("(%)") + info_icon.setStyleSheet("color: grey; font-size: 14px; padding-top: 14px;") + info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(info_icon, 6, 2, 1, 1) + + # Composition of Various Vehicles + # Remove the old label and vehicle_widget from the grid + # Instead, create a horizontal layout for this row + composition_row_widget = QWidget(self.general_widget) + composition_row_layout = QHBoxLayout(composition_row_widget) + composition_row_layout.setContentsMargins(0, 0, 0, 0) + composition_row_layout.setSpacing(20) # Space between label and box + + # The label + composition_label = QLabel("Composition of Various Vehicles") + composition_label.setAlignment(Qt.AlignLeft | Qt.AlignTop) + composition_row_layout.addWidget(composition_label, alignment=Qt.AlignTop) + + # The white box (vehicle_widget) as before + vehicle_widget = QWidget(self.general_widget) + vehicle_widget.setStyleSheet("background-color: #FFFFFF; border-radius: 10px; border: 1px solid #DDDCE0") + vehicle_widget.setFixedWidth(400) + vehicle_widget.setFixedHeight(250) + vehicle_layout = QGridLayout(vehicle_widget) + vehicle_layout.setContentsMargins(0, 0, 0, 0) + # vehicle_layout.setHorizontalSpacing(7) + # vehicle_layout.setVerticalSpacing(8) + + vehicles = ["Cars", "Buses", "HCV", "MCV", "LCV"] + for i, vehicle in enumerate(vehicles): + v_label = QLabel(f"{vehicle}:") + v_label.setFixedHeight(40) + v_label.setFixedWidth(50) + v_label.setStyleSheet("background-color: #FFFFFF; border: 1px solid #FFFFFF; border-radius: 10px; padding: 10px 10px 10px 1px;") + v_input = QLineEdit() + v_input.setFixedWidth(self.text_box_width) + v_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background: #FFFFFF; + } + """) + vehicle_layout.addWidget(v_label, i, 0) + vehicle_layout.addWidget(v_input, i, 1) + + v_label1 = QLabel("(PCU/D)") + v_label1.setStyleSheet(" padding: 10px 10px 10px 1px;") + + composition_row_layout.addWidget(vehicle_widget, alignment=Qt.AlignTop) + + # Add the composition_row_widget to the main grid, spanning columns 0-2 + grid_layout.addWidget(v_label1, 7, 3, 1, 3) + grid_layout.addWidget(composition_row_widget, 7, 0, 1, 3) + + self.general_layout.addLayout(grid_layout) + self.general_layout.addStretch(1) + self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) + + # Create the navigation buttons layout + self.button_h_layout = QHBoxLayout() + self.button_h_layout.setSpacing(10) + self.button_h_layout.setContentsMargins(10,10,10,10) + + # Adjust these stretch factors to control the position + self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + + back_button = QPushButton("Back") + back_button.setObjectName("nav_button") + self.button_h_layout.addWidget(back_button) + + next_button = QPushButton("Next") + next_button.setObjectName("nav_button") + self.button_h_layout.addWidget(next_button) + + # Add initial spacing before the navigation buttons + self.scroll_content_layout.addLayout(self.button_h_layout) + + # --- Add a corner spacer to the scroll_content_layout --- + self.button_h_layout.addSpacerItem(QSpacerItem(20, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) + self.scroll_content_layout.addSpacerItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) + + left_panel_vlayout.addWidget(self.scroll_area) + + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) + +# self.main_h_layout.addWidget(BridgeAndTrafficData(), 2) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file diff --git a/widgets/demolition_and_recycling_data.py b/widgets/demolition_and_recycling_data.py new file mode 100644 index 0000000..87b6a21 --- /dev/null +++ b/widgets/demolition_and_recycling_data.py @@ -0,0 +1,306 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtGui import QIcon +import sys +import os + +class DemolitionAndRecyclingData(QWidget): + def __init__(self): + super().__init__() + + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + QScrollArea { + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + border: 1px solid #000000; + padding-bottom: 20px; + } + + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + QPushButton#top_button_left_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + + /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ + QPushButton#nav_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + min-width: 80px; /* Ensure a minimum width */ + } + QPushButton#nav_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#nav_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + /* (Removed: QComboBox and material grid element CSS) */ + """) + + self.setObjectName("central_panel_widget") + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Demolition and Recycling Data ") + top_h_layout_left_panel.addWidget(top_button_left_panel) + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + + self.scroll_content_layout = QVBoxLayout(scroll_content_widget) + self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setSpacing(0) + + # --- Add General Info Form at the top of the scroll area --- + self.general_widget = QWidget() + self.general_layout = QVBoxLayout(self.general_widget) + self.general_layout.setContentsMargins(10, 20, 10, 10) + self.general_layout.setSpacing(10) + + grid_layout = QGridLayout() + grid_layout.setHorizontalSpacing(10) + grid_layout.setVerticalSpacing(20) + + field_width = 200 # More compact width for input fields + + # 1. Demolition Cost rate as percentage to total construction cost + label = QLabel("Demolition Cost rate as\npercentage to total construction cost") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 0, 0, 1, 1) + demolition_widget = QWidget(self.general_widget) + demolition_layout = QHBoxLayout(demolition_widget) + demolition_layout.setContentsMargins(0,0,0,0) + demolition_layout.setSpacing(10) + demolition_input = QLineEdit() + demolition_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + demolition_input.setFixedWidth(field_width) + demolition_input.setText("10") + demolition_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + demolition_layout.addWidget(demolition_input) + demolition_layout.addWidget(QLabel("(%)")) + suggested_label = QLabel("Suggested") + suggested_label.setStyleSheet("color: #B3AEAE; font-size: 10px;") + demolition_layout.addWidget(suggested_label) + demolition_layout.addStretch(1) + grid_layout.addWidget(demolition_widget, 0, 1, 1, 1, alignment=Qt.AlignLeft) + + # 2. Scrap Value of Structural Steel + label = QLabel("Scrap Value of Structural Steel") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 1, 0, 1, 1) + scrap_value_widget = QWidget(self.general_widget) + scrap_value_layout = QHBoxLayout(scrap_value_widget) + scrap_value_layout.setContentsMargins(0,0,0,0) + scrap_value_layout.setSpacing(10) + scrap_value_input = QLineEdit() + scrap_value_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + scrap_value_input.setFixedWidth(field_width) + scrap_value_input.setText("50000") + scrap_value_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + scrap_value_layout.addWidget(scrap_value_input) + scrap_value_layout.addWidget(QLabel("(INR/MT)")) + suggested_label2 = QLabel("Suggested") + suggested_label2.setStyleSheet("color: #B3AEAE; font-size: 10px;") + scrap_value_layout.addWidget(suggested_label2) + scrap_value_layout.addStretch(1) + grid_layout.addWidget(scrap_value_widget, 1, 1, 1, 1, alignment=Qt.AlignLeft) + + # 3. Structural Steel Scrap + label = QLabel("Structural Steel Scrap") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 2, 0, 1, 1) + steel_scrap_widget = QWidget(self.general_widget) + steel_scrap_layout = QHBoxLayout(steel_scrap_widget) + steel_scrap_layout.setContentsMargins(0,0,0,0) + steel_scrap_layout.setSpacing(10) + steel_scrap_input = QLineEdit() + steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + steel_scrap_input.setFixedWidth(field_width) + steel_scrap_input.setText("98") + steel_scrap_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + steel_scrap_layout.addWidget(steel_scrap_input) + steel_scrap_layout.addWidget(QLabel("(%)")) + suggested_label3 = QLabel("Suggested") + suggested_label3.setStyleSheet("color: #B3AEAE; font-size: 10px;") + steel_scrap_layout.addWidget(suggested_label3) + steel_scrap_layout.addStretch(1) + grid_layout.addWidget(steel_scrap_widget, 2, 1, 1, 1, alignment=Qt.AlignLeft) + + self.general_layout.addLayout(grid_layout) + self.general_layout.addStretch(1) + self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) + + # Create the navigation buttons layout + self.button_h_layout = QHBoxLayout() + self.button_h_layout.setSpacing(10) + self.button_h_layout.setContentsMargins(10,10,10,10) + + # Adjust these stretch factors to control the position + self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + + back_button = QPushButton("Back") + back_button.setObjectName("nav_button") + self.button_h_layout.addWidget(back_button) + + calculate_button = QPushButton("Calculate") + calculate_button.setObjectName("nav_button") + self.button_h_layout.addWidget(calculate_button) + + # Add initial spacing before the navigation buttons + self.scroll_content_layout.addLayout(self.button_h_layout) + + # --- Add a corner spacer to the scroll_content_layout --- + self.button_h_layout.addSpacerItem(QSpacerItem(20, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) + self.scroll_content_layout.addSpacerItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) + + left_panel_vlayout.addWidget(self.scroll_area) + +#----------------Standalone-Test-Code-------------------------------- + +class MyMainWindow(QMainWindow): + def __init__(self): + super().__init__() + + self.setStyleSheet("border: none") + + self.central_widget = QWidget() + self.central_widget.setObjectName("central_widget") + self.setCentralWidget(self.central_widget) + + self.main_h_layout = QHBoxLayout(self.central_widget) + self.main_h_layout.addStretch(1) + + self.main_h_layout.addWidget(DemolitionAndRecyclingData(), 2) + + self.setWindowState(Qt.WindowMaximized) + + +if __name__ == "__main__": + QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) + app = QApplication(sys.argv) + window = MyMainWindow() + window.show() + sys.exit(app.exec()) \ No newline at end of file diff --git a/widgets/financial_data.py b/widgets/financial_data.py new file mode 100644 index 0000000..aebccbe --- /dev/null +++ b/widgets/financial_data.py @@ -0,0 +1,316 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtGui import QIcon +import sys +import os + +class FinancialData(QWidget): + def __init__(self): + super().__init__() + + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + + QScrollArea { + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + border: 1px solid #000000; + padding-bottom: 20px; + } + + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + + QPushButton#top_button_left_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + + /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ + QPushButton#nav_button { + background-color: #FFFFFF; /* White background */ + border: 1px solid #E0E0E0; /* Light grey border */ + border-radius: 8px; /* Slightly more rounded corners */ + color: #3F3E5E; /* Dark text color */ + padding: 6px 15px; /* Increased padding */ + text-align: center; + min-width: 80px; /* Ensure a minimum width */ + } + QPushButton#nav_button:hover { + background-color: #F8F8F8; /* Very subtle light grey on hover */ + border-color: #C0C0C0; /* Darker border on hover */ + } + QPushButton#nav_button:pressed { + background-color: #E8E8E8; /* Darker grey on pressed */ + border-color: #A0A0A0; /* Even darker border */ + } + /* (Removed: QComboBox and material grid element CSS) */ + """) + + self.setObjectName("central_panel_widget") + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Financial Data ") + top_h_layout_left_panel.addWidget(top_button_left_panel) + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + + scroll_content_widget = QWidget() + scroll_content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_area.setWidget(scroll_content_widget) + + self.scroll_content_layout = QVBoxLayout(scroll_content_widget) + self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setSpacing(0) + + # --- Add General Info Form at the top of the scroll area --- + self.general_widget = QWidget() + self.general_layout = QVBoxLayout(self.general_widget) + self.general_layout.setContentsMargins(10, 20, 10, 10) + self.general_layout.setSpacing(10) + + # Change section title + top_button_left_panel.setText("Financial Data ") + + # --- Financial Data Form --- + grid_layout = QGridLayout() + grid_layout.setHorizontalSpacing(10) + grid_layout.setVerticalSpacing(20) + field_width = 200 + + # 1. Real Discount Rate (with info icon) + label1_widget = QWidget() + label1_layout = QHBoxLayout(label1_widget) + label1_layout.setContentsMargins(0, 0, 0, 0) + label1_layout.setSpacing(4) + label1 = QLabel("Real Discount Rate") + label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + label1_layout.addWidget(label1) + info_icon = QLabel() + info_icon.setPixmap(QIcon("resources/info.svg").pixmap(16, 16)) + label1_layout.addWidget(info_icon) + label1_layout.addStretch(1) + input1 = QLineEdit() + input1.setAlignment(Qt.AlignmentFlag.AlignLeft) + input1.setFixedWidth(field_width) + input1.setText("4.2500") + input1.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + unit1 = QLabel("(%)") + suggested1 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label1_widget, 0, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input1, 0, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit1, 0, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(suggested1, 0, 3, alignment=Qt.AlignVCenter) + + # 2. Interest Rate + label2 = QLabel("Interest Rate") + label2.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + input2 = QLineEdit() + input2.setAlignment(Qt.AlignmentFlag.AlignLeft) + input2.setFixedWidth(field_width) + input2.setText("10") + input2.setStyleSheet(input1.styleSheet()) + unit2 = QLabel("(%)") + suggested2 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label2, 1, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input2, 1, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit2, 1, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(suggested2, 1, 3, alignment=Qt.AlignVCenter) + + # 3. Investment Ratio + label3 = QLabel("Investment Ratio") + label3.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + input3 = QLineEdit() + input3.setAlignment(Qt.AlignmentFlag.AlignLeft) + input3.setFixedWidth(field_width) + input3.setText("0.5000") + input3.setStyleSheet(input1.styleSheet()) + suggested3 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label3, 2, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input3, 2, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(QWidget(), 2, 2) # Empty cell for unit + grid_layout.addWidget(suggested3, 2, 3, alignment=Qt.AlignVCenter) + + # 4. Duration of Study + label4 = QLabel("Duration of Study") + label4.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + input4 = QLineEdit() + input4.setAlignment(Qt.AlignmentFlag.AlignLeft) + input4.setFixedWidth(field_width) + input4.setText("50 & 100") + input4.setStyleSheet(input1.styleSheet()) + unit4 = QLabel("(years)") + suggested4 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label4, 3, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input4, 3, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit4, 3, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(suggested4, 3, 3, alignment=Qt.AlignVCenter) + + # 5. Time for construction of Base Project + label5 = QLabel("Time for construction of Base Project") + label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + input5 = QLineEdit() + input5.setAlignment(Qt.AlignmentFlag.AlignLeft) + input5.setFixedWidth(field_width) + input5.setText("") + input5.setStyleSheet(input1.styleSheet()) + unit5 = QLabel("(years)") + grid_layout.addWidget(label5, 4, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input5, 4, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit5, 4, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(QWidget(), 4, 3) # Empty cell for suggested + + self.general_layout.addLayout(grid_layout) + self.general_layout.addStretch(1) + self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) + + # Create the navigation buttons layout + self.button_h_layout = QHBoxLayout() + self.button_h_layout.setSpacing(10) + self.button_h_layout.setContentsMargins(10,10,10,10) + + # Adjust these stretch factors to control the position + self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + + back_button = QPushButton("Back") + back_button.setObjectName("nav_button") + self.button_h_layout.addWidget(back_button) + + next_button = QPushButton("Next") + next_button.setObjectName("nav_button") + self.button_h_layout.addWidget(next_button) + + # Add initial spacing before the navigation buttons + self.scroll_content_layout.addLayout(self.button_h_layout) + + # --- Add a corner spacer to the scroll_content_layout --- + self.button_h_layout.addSpacerItem(QSpacerItem(20, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) + self.scroll_content_layout.addSpacerItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) + + left_panel_vlayout.addWidget(self.scroll_area) + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) + +# self.main_h_layout.addWidget(FinancialData(), 2) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file diff --git a/widgets/project_details_right_widget.py b/widgets/project_details_right_widget.py index 4b1287b..f308fba 100644 --- a/widgets/project_details_right_widget.py +++ b/widgets/project_details_right_widget.py @@ -1,4 +1,3 @@ - from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, QSize, Qt, QPropertyAnimation, QEasingCurve, Signal from PySide6.QtGui import (QIcon) @@ -389,6 +388,8 @@ def __init__(self, parent=None): "Maintenance and Repair", "Disposal and Recycling" ] + self.selected_icon = QIcon("resources/selected_icon.png") + self.param_buttons = [] for label in button_labels: btn = QPushButton(f" {label}") btn.setObjectName("parameter_button") @@ -410,7 +411,9 @@ def __init__(self, parent=None): background-color: #F0E6E6; } """) + btn.clicked.connect(lambda checked, b=btn: self.select_param_button(b)) self.input_param_option_layout.addWidget(btn) + self.param_buttons.append(btn) self.input_param_layout.addWidget(self.input_param_option_widget) scroll_content_layout.insertWidget(2, self.input_param_widget) self.output_button = QPushButton(" Outputs") @@ -465,6 +468,13 @@ def input_button_toggle(self): self.input_param_button.setStyleSheet(self.input_param_button_css_active) self.input_param_button.setIcon(self.input_param_active_icon) + def select_param_button(self, selected_btn): + for btn in self.param_buttons: + if btn == selected_btn: + btn.setIcon(self.selected_icon) + else: + btn.setIcon(QIcon("resources/play-button-arrowhead.png")) + def close_widget(self): self.closed.emit() self.setParent(None) diff --git a/widgets/structure_works_data/project_details_left_widget.py b/widgets/structure_works_data/project_details_left_widget.py new file mode 100644 index 0000000..57635c2 --- /dev/null +++ b/widgets/structure_works_data/project_details_left_widget.py @@ -0,0 +1,379 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt, QSize, QPropertyAnimation, QEasingCurve +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QTextEdit, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy) +from PySide6.QtGui import QIcon, QTextDocument +import sys +import os + +class ProjectDetailsLeft(QWidget): + """ + The main application window that uses a custom title bar. + """ + def __init__(self): + super().__init__() + + self.current_selected_button = None + self.all_param_buttons = [] + + self.setObjectName("left_panel_widget") + self.setStyleSheet(""" + #left_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #left_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #left_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + QScrollArea { + border: 1px solid #000000; + background-color: transparent; + outline: none; + } + QScrollArea > QWidget { + background-color: transparent; + } + QScrollBar:vertical { + width: 12px; + margin: 0px; + border-radius: 6px; + } + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + QPushButton#top_button_left_panel { + background-color: #F0E6E6; + border: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_left_panel:hover { + background-color: #FDEFEF; + } + QPushButton#top_button_left_panel:pressed { + background-color: #FFF3F3; + } + #input_output_header { + background-color: #F0E6E6; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; + } + #input_output_header QLabel { + font-size: 15px; + font-weight: bold; + color: #333; + letter-spacing: 1px; + padding: 8px 0 8px 0; + background: transparent; + } + #input_output_separator { + background-color: #F0E6E6; + height: 10px; + border-bottom: 1px solid black; + } + QPushButton.category_button { + background-color: transparent; + border: none; + text-align: left; + padding: 6px 0px 6px 15px; + color: #000; + font-size: 13px; + } + QPushButton.category_button:hover { + background-color: #2A3F54; + color: #FFFFFF; + } + QPushButton.subcategory_button { + background-color: transparent; + border: none; + text-align: left; + padding: 4px 0px 4px 30px; + color: #000; + font-size: 12px; + } + QPushButton.subcategory_button:hover { + background-color: #2A3F54; + color: #FFFFFF; + } + QPushButton.category_button[selected="true"], + QPushButton.subcategory_button[selected="true"] { + background-color: #2A3F54; + color: #FFFFFF; + } + QLabel.output_label { + background-color: transparent; + color: #808080; + font-size: 12px; + } + """) + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + top_h_layout_left_panel = QHBoxLayout() + top_button_left_panel = QPushButton("Project Details Window") + top_button_left_panel.setIcon(QIcon("resources/close.png")) + top_button_left_panel.setIconSize(QSize(13, 13)) + top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_h_layout_left_panel.addWidget(top_button_left_panel, 2) + top_h_layout_left_panel.addStretch(1) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + bordered_spacer_widget = QWidget() + bordered_spacer_widget.setObjectName("bordered_spacer_widget") + bordered_spacer_widget.setFixedHeight(50) + bordered_spacer_widget.setStyleSheet(""" + #bordered_spacer_widget { + background-color: #F0E6E6; + border: 1px solid black; + border-bottom: none; + } + """) + left_panel_vlayout.addWidget(bordered_spacer_widget) + + scroll_area = QScrollArea() + scroll_area.setWidgetResizable(True) + scroll_content_widget = QWidget() + scroll_area.setWidget(scroll_content_widget) + scroll_content_layout = QVBoxLayout(scroll_content_widget) + scroll_content_layout.setContentsMargins(0, 0, 0, 0) + scroll_content_layout.setSpacing(0) + + input_param_header = QWidget() + input_param_header.setObjectName("input_param_header") + input_param_header.setStyleSheet(""" + #input_param_header { + background-color: #F0E6E6; + } + """) + input_param_header_layout = QVBoxLayout(input_param_header) + input_param_header_layout.setContentsMargins(0, 0, 0, 0) + input_param_header_layout.setSpacing(0) + input_param_label = QLabel("Input Parameters") + input_param_label.setAlignment(Qt.AlignCenter) + input_param_label.setStyleSheet("font-size: 15px; font-weight: bold; color: #333; padding: 8px 0 8px 0; background: transparent;border:none;") + input_param_header_layout.addWidget(input_param_label) + scroll_content_layout.addWidget(input_param_header) + + header_separator = QWidget() + header_separator.setObjectName("input_output_separator") + header_separator.setFixedHeight(2) + scroll_content_layout.addWidget(header_separator) + + # Define icons for the four states of category buttons + unselected_unexpanded_icon = QIcon("resources/play-button-arrowhead.png") + unselected_sub_icon = QIcon("resources/play-button-arrowhead.png") + + self.current_selected_button = None + self.all_param_buttons = [] + button_data = { + "Structure Works Data": ["Foundation", "Super-Structure", "Sub-Structure", "Miscellaneous"], + "Financial Data": [], + "Carbon Emission Data": ["Carbon Emission Cost Data"], + "Bridge and Traffic Data": [], + "Maintenance and Repair": [], + "Demolition and Recycling": [] + } + for label, sublabels in button_data.items(): + btn = QPushButton(label) + btn.setProperty("class", "category_button") + btn.setProperty("selected", False) + btn.setProperty("expanded", False) + btn.setCursor(Qt.CursorShape.PointingHandCursor) + btn.setLayoutDirection(Qt.LeftToRight) + btn.setIcon(unselected_unexpanded_icon) + btn.setIconSize(QSize(10, 10)) + btn.clicked.connect(lambda checked, b=btn: self.handle_button_selection(b)) + scroll_content_layout.addWidget(btn) + self.all_param_buttons.append(btn) + if sublabels: + sub_widgets = [] + for sublabel in sublabels: + sub_btn = QPushButton(sublabel) + sub_btn.setProperty("class", "subcategory_button") + sub_btn.setProperty("selected", False) + sub_btn.setIcon(unselected_sub_icon) + sub_btn.setIconSize(QSize(10, 10)) + sub_btn.setCursor(Qt.CursorShape.PointingHandCursor) + sub_btn.setLayoutDirection(Qt.LeftToRight) + sub_btn.setVisible(False) + sub_btn.clicked.connect(lambda checked, b=sub_btn: self.handle_button_selection(b)) + scroll_content_layout.addWidget(sub_btn) + sub_widgets.append(sub_btn) + self.all_param_buttons.append(sub_btn) + + def make_toggle(button, sub_widgets): + def toggle(): + is_expanded = button.property("expanded") + for widget in sub_widgets: + widget.setVisible(not widget.isVisible()) + button.setProperty("expanded", not is_expanded) + self.update_button_icon(button) + self.handle_button_selection(button) + return toggle + btn.clicked.connect(lambda checked, b=btn, sw=sub_widgets: make_toggle(b, sw)()) + + input_output_middle_separator = QWidget() + input_output_middle_separator.setObjectName("input_output_separator") + input_output_middle_separator.setFixedHeight(2) + scroll_content_layout.addWidget(input_output_middle_separator) + + output_header = QWidget() + output_header.setObjectName("input_param_header") + output_header.setStyleSheet(""" + #input_param_header { + background-color: #F0E6E6; + } + """) + output_header_layout = QVBoxLayout(output_header) + output_header_layout.setContentsMargins(0, 0, 0, 0) + output_header_layout.setSpacing(0) + output_label = QLabel("Output") + output_label.setAlignment(Qt.AlignCenter) + output_label.setStyleSheet("font-size: 15px; font-weight: bold; color: #333; letter-spacing: 1px; padding: 8px 0 8px 0; background: transparent;") + output_header_layout.addWidget(output_label) + scroll_content_layout.addWidget(output_header) + + output_header_separator = QWidget() + output_header_separator.setObjectName("input_output_separator") + output_header_separator.setFixedHeight(2) + scroll_content_layout.addWidget(output_header_separator) + + output_labels_data = [ + "Initial Construction Cost", "Initial Carbon Emission Cost", "Time Cost", + "Road User Cost", "Carbon Emission due to Re-Routing", "Periodic Maintenance Costs", + "Maintenance Emission Costs", "Routine Inspection Costs", "Repair & Rehabilitation Costs", + "Reconstruction Costs", "Demolition & Disposal Cost", "Recycling Cost", "Total Life-Cycle Cost" + ] + output_layout = QVBoxLayout() + output_layout.setContentsMargins(10, 10, 10, 10) + for text in output_labels_data: + output_item = QLabel(text) + output_item.setProperty("class", "output_label") + output_layout.addWidget(output_item) + scroll_content_layout.addLayout(output_layout) + scroll_content_layout.addStretch(1) + + scroll_content_widget.setStyleSheet(""" + QPushButton[selected="true"] { + background-color: #2A3F54; + color: #FFFFFF; + } + QPushButton[selected="false"], QPushButton { + background-color: transparent; + color: #000000; + } + """) + left_panel_vlayout.addWidget(scroll_area) + + def handle_button_selection(self, button_clicked): + """ + Handles the visual selection state of buttons in the side panel. + """ + for b in self.all_param_buttons: + b.setProperty("selected", False) + b.style().unpolish(b) + b.style().polish(b) + if b.property("class") == "subcategory_button": + b.setIcon(QIcon("resources/play-button-arrowhead.png")) + else: + self.update_button_icon(b) + button_clicked.setProperty("selected", True) + button_clicked.style().unpolish(button_clicked) + button_clicked.style().polish(button_clicked) + if button_clicked.property("class") == "subcategory_button": + button_clicked.setIcon(QIcon("resources/play-button-selected.png")) + else: + self.update_button_icon(button_clicked) + self.current_selected_button = button_clicked + + def update_button_icon(self, button): + """ + Updates the icon of a category button based on its selected and expanded state. + """ + if button.property("class") != "category_button": + return + is_selected = button.property("selected") + is_expanded = button.property("expanded") + if is_selected and is_expanded: + button.setIcon(QIcon("resources/arrow_down_selected.png")) + elif is_selected: + button.setIcon(QIcon("resources/play-button-selected.png")) + elif is_expanded: + button.setIcon(QIcon("resources/arrow_down.png")) + else: + button.setIcon(QIcon("resources/play-button-arrowhead.png")) + button.setIconSize(QSize(10, 10)) + +#----------------Standalone-Test-Code-------------------------------- + +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() + +# self.setStyleSheet("border: none") + +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) + +# self.main_h_layout = QHBoxLayout(self.central_widget) + +# self.main_h_layout.addWidget(ProjectDetailsLeft(), 1) + +# self.main_h_layout.addStretch(5) + +# self.setWindowState(Qt.WindowMaximized) + + +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file From b36131d37abd14a9679aff2faf9efbe12b27ebda Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Tue, 8 Jul 2025 14:26:29 +0530 Subject: [PATCH 08/22] Fixed the click functionality of the available buttons and tabs --- __pycache__/Home.py | 178 ---- __pycache__/ProjectDetails_ | 0 ...ridgeANDTrafficData_Window.cpython-312.pyc | Bin 47171 -> 0 bytes ...ridgeANDTrafficData_Window.cpython-313.pyc | Bin 47780 -> 0 bytes ...jectDetails_BridgeANDTrafficData_Window.py | 486 ----------- ..._CarbonEmissionData_Window.cpython-312.pyc | Bin 74484 -> 0 bytes ..._CarbonEmissionData_Window.cpython-313.pyc | Bin 75474 -> 0 bytes ...rojectDetails_CarbonEmissionData_Window.py | 813 ------------------ ...ionANDRecyclingData_Window.cpython-312.pyc | Bin 33667 -> 0 bytes ...ionANDRecyclingData_Window.cpython-313.pyc | Bin 34068 -> 0 bytes ...tails_DemolitionANDRecyclingData_Window.py | 351 -------- ...tails_FinancialData_Window.cpython-312.pyc | Bin 36818 -> 0 bytes ...tails_FinancialData_Window.cpython-313.pyc | Bin 37272 -> 0 bytes .../ProjectDetails_FinancialData_Window.py | 382 -------- ...tDetails_Foundation_Window.cpython-312.pyc | Bin 54201 -> 0 bytes ...tDetails_Foundation_Window.cpython-313.pyc | Bin 54909 -> 0 bytes .../ProjectDetails_Foundation_Window.py | 597 ------------- ...enanceANDRepairData_Window.cpython-312.pyc | Bin 41090 -> 0 bytes ...enanceANDRepairData_Window.cpython-313.pyc | Bin 41586 -> 0 bytes ...Details_MaintenanceANDRepairData_Window.py | 420 --------- ...tails_Miscellaneous_Window.cpython-312.pyc | Bin 52237 -> 0 bytes ...tails_Miscellaneous_Window.cpython-313.pyc | Bin 52961 -> 0 bytes .../ProjectDetails_Miscellaneous_Window.py | 566 ------------ ...etails_SubStructure_Window.cpython-312.pyc | Bin 50769 -> 0 bytes ...etails_SubStructure_Window.cpython-313.pyc | Bin 51460 -> 0 bytes .../ProjectDetails_SubStructure_Window.py | 546 ------------ ...ails_SuperStructure_Window.cpython-312.pyc | Bin 52197 -> 0 bytes ...ails_SuperStructure_Window.cpython-313.pyc | Bin 52921 -> 0 bytes .../ProjectDetails_SuperStructure_Window.py | 566 ------------ __pycache__/README.md | 1 - __pycache__/Warning_Window.cpython-312.pyc | Bin 3407 -> 0 bytes __pycache__/Warning_Window.cpython-313.pyc | Bin 3480 -> 0 bytes __pycache__/app.cpython-312.pyc | Bin 1369 -> 0 bytes __pycache__/form_data_storage.cpython-312.pyc | Bin 889 -> 0 bytes __pycache__/form_data_storage.cpython-313.pyc | Bin 847 -> 0 bytes __pycache__/form_data_storage.py | 24 - __pycache__/main_template.cpython-311.pyc | Bin 16847 -> 0 bytes __pycache__/main_template.cpython-312.pyc | Bin 20078 -> 0 bytes __pycache__/title_bar.cpython-311.pyc | Bin 9285 -> 0 bytes __pycache__/title_bar.cpython-312.pyc | Bin 8565 -> 0 bytes .../tutorial_widget_left.cpython-312.pyc | Bin 11217 -> 0 bytes main_template.py | 70 +- resources/file_button.png | Bin 0 -> 1402 bytes .../maintenance_repair_data.cpython-312.pyc | Bin 19943 -> 0 bytes ...oject_details_right_widget.cpython-312.pyc | Bin 24979 -> 0 bytes widgets/__pycache__/title_bar.cpython-312.pyc | Bin 8573 -> 0 bytes .../tutorial_widget_left.cpython-312.pyc | Bin 11940 -> 0 bytes widgets/bridge_and_traffic_data.py | 7 +- .../carbon_emission_cost_data.cpython-312.pyc | Bin 12280 -> 0 bytes widgets/demolition_and_recycling_data.py | 2 +- widgets/financial_data.py | 36 +- widgets/maintenance_repair_data.py | 2 +- .../project_details_left_widget.py | 54 +- widgets/project_details_right_widget.py | 2 +- .../auxiliary_works_widget.py | 2 +- .../sub_structure_widget.py | 12 +- 56 files changed, 114 insertions(+), 5003 deletions(-) delete mode 100644 __pycache__/Home.py delete mode 100644 __pycache__/ProjectDetails_ delete mode 100644 __pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_BridgeANDTrafficData_Window.py delete mode 100644 __pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_CarbonEmissionData_Window.py delete mode 100644 __pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.py delete mode 100644 __pycache__/ProjectDetails_FinancialData_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_FinancialData_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_FinancialData_Window.py delete mode 100644 __pycache__/ProjectDetails_Foundation_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_Foundation_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_Foundation_Window.py delete mode 100644 __pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_MaintenanceANDRepairData_Window.py delete mode 100644 __pycache__/ProjectDetails_Miscellaneous_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_Miscellaneous_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_Miscellaneous_Window.py delete mode 100644 __pycache__/ProjectDetails_SubStructure_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_SubStructure_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_SubStructure_Window.py delete mode 100644 __pycache__/ProjectDetails_SuperStructure_Window.cpython-312.pyc delete mode 100644 __pycache__/ProjectDetails_SuperStructure_Window.cpython-313.pyc delete mode 100644 __pycache__/ProjectDetails_SuperStructure_Window.py delete mode 100644 __pycache__/README.md delete mode 100644 __pycache__/Warning_Window.cpython-312.pyc delete mode 100644 __pycache__/Warning_Window.cpython-313.pyc delete mode 100644 __pycache__/app.cpython-312.pyc delete mode 100644 __pycache__/form_data_storage.cpython-312.pyc delete mode 100644 __pycache__/form_data_storage.cpython-313.pyc delete mode 100644 __pycache__/form_data_storage.py delete mode 100644 __pycache__/main_template.cpython-311.pyc delete mode 100644 __pycache__/main_template.cpython-312.pyc delete mode 100644 __pycache__/title_bar.cpython-311.pyc delete mode 100644 __pycache__/title_bar.cpython-312.pyc delete mode 100644 __pycache__/tutorial_widget_left.cpython-312.pyc create mode 100644 resources/file_button.png delete mode 100644 widgets/__pycache__/maintenance_repair_data.cpython-312.pyc delete mode 100644 widgets/__pycache__/project_details_right_widget.cpython-312.pyc delete mode 100644 widgets/__pycache__/title_bar.cpython-312.pyc delete mode 100644 widgets/__pycache__/tutorial_widget_left.cpython-312.pyc delete mode 100644 widgets/carbon_emission_data/__pycache__/carbon_emission_cost_data.cpython-312.pyc rename widgets/{structure_works_data => }/project_details_left_widget.py (92%) diff --git a/__pycache__/Home.py b/__pycache__/Home.py deleted file mode 100644 index 6d5404f..0000000 --- a/__pycache__/Home.py +++ /dev/null @@ -1,178 +0,0 @@ -import sys -from PyQt5 import QtCore, QtGui, QtWidgets -from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, - QHBoxLayout, QPushButton, QLabel, QFrame, QSplitter, - QToolBar, QAction, QGroupBox, QMenu, QLineEdit, - QComboBox, QSizePolicy, QMessageBox, QTextEdit, QScrollArea, - QTabWidget, QStackedWidget) -from PyQt5.QtGui import QIcon, QFont -from PyQt5.QtCore import Qt, QSize - - -class MainWindow(QMainWindow): # Renamed to MainWindow, inheriting QMainWindow - def __init__(self): - super().__init__() - self.setWindowTitle(" - BICCA Studio 1.0.0") - self.setGeometry(100, 100, 1440, 1024) - self.setStyleSheet("background-color: rgb(255, 255, 255);") - - # 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. - """ - } - ] - - 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) - - # --- CORRECTED ORDER: Create UI elements BEFORE retranslateUi --- - self.create_menu_bar() - self.create_toolbar() - self.create_window_tabs() # Custom tab buttons that control splitter visibility - self.create_content_area() # Contains the QSplitter for panels - self.create_status_bar() # Includes the persistent "Data" section - - self.retranslateUi(self) # Call retranslateUi *after* all widgets are created - - self.update_tutorial_content() - - # Set initial visible panels and button states for the custom tabs - self.handle_tab_click("Tutorials", initial_load=True) - self.handle_tab_click("Project Details", initial_load=True) - - # Connect QGroupBox toggled signals - self.generalInfoGroup.toggled['bool'].connect(lambda checked: self.toggle_general_info_group_content(self.generalInfoGroup, checked)) - self.inputParamsGroup.toggled['bool'].connect(lambda checked: self.toggle_input_params_group_content(self.inputParamsGroup, checked)) - self.outputsGroup.toggled['bool'].connect(lambda checked: self.toggle_outputs_group_content(self.outputsGroup, checked)) - - # Connect internal buttons to hide/show their respective sub-content - self.pushButton.clicked.connect(lambda: self.toggle_sub_buttons_visibility(self.gridLayout_3, self.pushButton)) - self.pushButton_7.clicked.connect(lambda: self.toggle_sub_buttons_visibility(self.gridLayout_4, self.pushButton_7)) - - # Initially hide the collapsible sub-sections' content - self.gridLayout_3_widget.setVisible(False) # Hide the widget holding gridLayout_3 - self.gridLayout_4_widget.setVisible(False) # Hide the widget holding gridLayout_4 - - # Manually trigger initial state for General Info, Input Params, Outputs groups - self.toggle_general_info_group_content(self.generalInfoGroup, self.generalInfoGroup.isChecked()) - self.toggle_input_params_group_content(self.inputParamsGroup, self.inputParamsGroup.isChecked()) - self.toggle_outputs_group_content(self.outputsGroup, self.outputsGroup.isChecked()) - - def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", " - BICCA Studio 1.0.0")) - - # Custom Tab button texts - self.tab_buttons["Tutorials"].setText(_translate("MainWindow", "Tutorials")) - self.tab_buttons["Project Details"].setText(_translate("MainWindow", "Project Details")) - self.tab_buttons["Results"].setText(_translate("MainWindow", "Results")) - self.tab_buttons["Compare"].setText(_translate("MainWindow", "Compare")) - - # Headers for panels - self.tutorials_header_label.setText(_translate("MainWindow", "Tutorials")) - self.project_header_label.setText(_translate("MainWindow", "Project Details Window")) - self.results_header_label.setText(_translate("MainWindow", "Results")) - self.compare_header_label.setText(_translate("MainWindow", "Compare")) - - # Group Box Titles - self.generalInfoGroup.setTitle(_translate("MainWindow", "General Information")) - self.inputParamsGroup.setTitle(_translate("MainWindow", "Input Parameters")) - self.outputsGroup.setTitle(_translate("MainWindow", "Outputs")) - - # General Information Labels and Placeholders - self.label_company_name.setText(_translate("MainWindow", "Company Name")) - self.lineEdit_company_name.setPlaceholderText(_translate("MainWindow", "Enter Company Name")) - self.label_project_title.setText(_translate("MainWindow", "Project Title")) - self.lineEdit_project_title.setPlaceholderText(_translate("MainWindow", "Enter Project Title")) - self.label_project_description.setText(_translate("MainWindow", "Project Description")) - self.textEdit_project_description.setPlaceholderText(_translate("MainWindow", "Enter Project Description")) - self.label_valuer_name.setText(_translate("MainWindow", "Name of Valuer")) - self.lineEdit_valuer_name.setPlaceholderText(_translate("MainWindow", "Enter Valuer's Name")) - self.label_job_number.setText(_translate("MainWindow", "Job Number")) - self.lineEdit_job_number.setPlaceholderText(_translate("MainWindow", "Enter Job Number")) - self.label_client.setText(_translate("MainWindow", "Client")) - self.lineEdit_client.setPlaceholderText(_translate("MainWindow", "Enter Client Name")) - self.label_country.setText(_translate("MainWindow", "Country")) - self.label_base_year.setText(_translate("MainWindow", "Base Year")) - self.lineEdit_base_year.setPlaceholderText(_translate("MainWindow", "e.g., 2023")) - - # Input Parameters Buttons - self.pushButton.setText(_translate("MainWindow", "Structure Works Data")) - self.pushButton_3.setText(_translate("MainWindow", "Super-Structure")) - self.pushButton_2.setText(_translate("MainWindow", "Foundation")) - self.pushButton_4.setText(_translate("MainWindow", "Sub-Structure")) - self.pushButton_5.setText(_translate("MainWindow", "Miscellaneous")) - self.pushButton_6.setText(_translate("MainWindow", "Financial Data")) - self.pushButton_7.setText(_translate("MainWindow", "Carbon Emission Data")) - self.pushButton_8.setText(_translate("MainWindow", "Carbon Emission Cost Data")) - self.pushButton_9.setText(_translate("MainWindow", "Bridge and Traffic Data")) - self.pushButton_10.setText(_translate("MainWindow", "Maintenance and Repair")) - self.pushButton_11.setText(_translate("MainWindow", "Disposal and Recycling")) - - # Outputs Label - self.label_10.setText(_translate("MainWindow", "Output content goes here.")) - - # Menu and Toolbar actions - self.menuFile.setTitle(_translate("MainWindow", "File")) - self.menuHome.setTitle(_translate("MainWindow", "Home")) - self.menuReports.setTitle(_translate("MainWindow", "Reports")) - self.menuHelp.setTitle(_translate("MainWindow", "Help")) - self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) - self.actionNew.setText(_translate("MainWindow", "New")) - self.actionOpen.setText(_translate("MainWindow", "Open")) - self.actionSave.setText(_translate("MainWindow", "Save")) - self.actionSave_As.setText(_translate("MainWindow", "Save As...")) - self.actionCreate_a_Copy.setText(_translate("MainWindow", "Create a Copy")) - self.actionPrint.setText(_translate("MainWindow", "Print")) - self.actionRename.setText(_translate("MainWindow", "Rename")) - self.actionExport.setText(_translate("MainWindow", "Export")) - self.actionVersion_History.setText(_translate("MainWindow", "Version History")) - self.actionInfo.setText(_translate("MainWindow", "Info")) - self.actionContact_Us.setText(_translate("MainWindow", "Contact Us")) - self.actionFeedback.setText(_translate("MainWindow", "Feedback")) - self.actionVideo_Tutorials.setText(_translate("MainWindow", "Video Tutorials")) - self.actionJoin_our_Community.setText(_translate("MainWindow", "Join our Community")) - - # Data section retranslate - self.combo_box_lookup.setItemText(0, _translate("MainWindow", "Carbon Data")) - self.combo_box_lookup.setItemText(1, _translate("MainWindow", "Maintenance Rate Data")) - self.combo_box_lookup.setItemText(2, _translate("MainWindow", "Recycling Data")) - -if __name__ == "__main__": - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - window.show() - sys.exit(app.exec_()) diff --git a/__pycache__/ProjectDetails_ b/__pycache__/ProjectDetails_ deleted file mode 100644 index e69de29..0000000 diff --git a/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-312.pyc deleted file mode 100644 index 5efa48e219d39d3910af829aefd3409df27554b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47171 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 RUiqb?@Qckh$4*V){|l9kk+%Q< diff --git a/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-313.pyc b/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.cpython-313.pyc deleted file mode 100644 index 89360983adeaa7f074c3520af8167cdea2a960de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47780 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 diff --git a/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.py b/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.py deleted file mode 100644 index 583f82a..0000000 --- a/__pycache__/ProjectDetails_BridgeANDTrafficData_Window.py +++ /dev/null @@ -1,486 +0,0 @@ -# -*- 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 - - -class Ui_BridgeTraffic_Dialog(object): - def setupUi(self, BridgeTraffic_Dialog): - BridgeTraffic_Dialog.setObjectName("BridgeTraffic_Dialog") - BridgeTraffic_Dialog.resize(1440, 999) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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") - 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.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.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.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.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.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("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("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)) - 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)) - 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.setGeometry(QtCore.QRect(540, 520, 341, 32)) - 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.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.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.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.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.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.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("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("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("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.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.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.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.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.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.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(BridgeTraffic_Dialog.accept) # type: ignore - self.buttonBox_4.rejected.connect(BridgeTraffic_Dialog.reject) # 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): - """ - 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: - QtWidgets.QApplication.quit() # 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 ")) - - -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/__pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-312.pyc deleted file mode 100644 index eb9b8320244fd584b4bb597d78320f9c9854995e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74484 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 diff --git a/__pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-313.pyc b/__pycache__/ProjectDetails_CarbonEmissionData_Window.cpython-313.pyc deleted file mode 100644 index b7cfd3b3a99259f4e7b0e5b730d885714a6b1092..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75474 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�TA$_E-rCV~jpt;fsZ0ehKWLj}iDvqL^O- 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 diff --git a/__pycache__/ProjectDetails_CarbonEmissionData_Window.py b/__pycache__/ProjectDetails_CarbonEmissionData_Window.py deleted file mode 100644 index 816b055..0000000 --- a/__pycache__/ProjectDetails_CarbonEmissionData_Window.py +++ /dev/null @@ -1,813 +0,0 @@ -# -*- 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 - - -class Ui_CarbonEmission_Dialog(object): - def setupUi(self, CarbonEmission_Dialog): - CarbonEmission_Dialog.setObjectName("CarbonEmission_Dialog") - CarbonEmission_Dialog.resize(1440, 1000) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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.rejected.connect(CarbonEmission_Dialog.reject) # type: ignore - self.pushButton_51.toggled['bool'].connect(self.widget_2.setVisible) # type: ignore - # Corrected line: Changed self.widget_8 to self.widget_2 based on the traceback suggestion and available widgets - self.pushButton_57.toggled['bool'].connect(self.widget_2.setVisible) # type: ignore - self.buttonBox.rejected.connect(self.show_warning) # type: ignore - QtCore.QMetaObject.connectSlotsByName(CarbonEmission_Dialog) - - def show_warning(self): - """ - 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: - QtWidgets.QApplication.quit() # 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", "Componenet:")) - 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", "Componenet:")) - self.lineEdit_45.setText(_translate("CarbonEmission_Dialog", "")) - 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", "Componenet:")) - 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 ")) - - -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/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.cpython-312.pyc deleted file mode 100644 index c9955bfc867742049932f2e3e56c01e64a10127f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33667 zcmeHwZEzGxc3?@}5~VL-0D(TC)DrpteTz>az9gXmMiS@)7=#v0OI4Dp=?_g+X+Rp` zWy2dbcx}(_c_L=Uw_&3_B7mETL*v`HzRAyzq_wwa?FJHdO%JeKF5e71IseKPG8nYRtA^Jz0Qe_?{cD3%Kq44;*M;Q zcroi%EypSI0GsX28Oh1yB!_1gX@sUyj%PQ$Be{>EZTyVn8+aw=o&EZ8>}F&uKy0z@ zwn^JO6?Ql?FKs0hGdO9hr*bs}p@2OCo~l+uNcoO`53WX(D2vZA}L$Yb4-FW{9mIp81bmfhaK)I_F@AP;%e%~!uTi}V&U zJfsC1keEUH01z=<+5kZ7qT}Nj-3+8cY{dp7Iw=)x3#srR2MM1AtGxwo%%Zfwc?{c( z2lf=i-{YOOjg)z|lN?ROEHPW$BJv+Q%`zKTI|&ra9*I8hW-erU_7F&#iaC@LKorv& zk`r^|AWBI&O~pJL1iQcG6q@7~GE-vhBd|2pwvxO@AUT?PTr^K|DO=f3AZePSm1+t} zaw%J}EP`NgoS09sG^$1LGl?Hdi;e|{=U`$j*bc?6UtKE}kan~kq}Ib|#nVV?X_^9~ znL?5r@tQEA*tXC{AZfbbARMlVb(lhvoM`icYqZ7k3)^U0wh?`VP^D?yh%O`L(9$uI zr)e4^I!D|tz9#N?r6ZcnYwFR+>R}2CUPCS?sEkNX+(|XC1{Ot{%w zrfEo}SS%4s#j@2>vH2!OykvQ{^~_IfUQ=5$tA|~*wvt14nx??$p^zju4>3dv ztn&nxriewfXL!gnw2tJMF;F?h`kEGF&5~d-7fG8mP0M2Tiu+!<#jtoyZ80pY&22HP z9R#;f6;}{keOPBkZx*Q}L zplQkxc7VVF(ZdYIW7UU@9U;x)BaSXi4o7Gm{?WigE#W-+bA zI>2H?lBa1}7E>ZNy>g3T@tWFVSXi6eVpu(5Sxht2VkW7kNe=9Qu^K_JA$mS!gPbOL znxJiIg+L#u@6Ft*3Zq?4RD6kGMg(0~#tRn|z*h8;)|AftJYU^P2&|0@7eNMS< z>Dhdp zpVB3{ILozG&Y@Q`1cIh%S?&?>sCbO&w^=Qyv^A1TkN^vdrK9*&Y8KWkX@RD3`-N;7 zu%vyQDQ(8;K3Oq_InsJ22U)*EIUu<-WZf>dizmbmrmR^luzGTm*%Qm75$-tLA7juY z*w8<#_W*f^;*ngM{o5y=THU`5U8QI7n%WDpuvk_Z?>SglFF&)+6PePK=)qX0W3|H4 z{b`D2^BI;NDfN`Px1vLUT%dR)mzM6cVZHs#YTd8V0!#Ms9)ZOdODboXQu!6q7n-s; zA=oe?&Jb9Dyhrjh_53Q$lN^j_;~(j_q0f7ymZr@8lTOlhT)fMQUYJ@U*uZ%wX=y`8 z+II<6ntD=4}PK>J*A{wnu_OXuRv<=(;AZ7AWxOGkhO_C6{%BQ32nc-k-BkYOS-dSX%E*OY41UX`R8- zhH3p0k?A*1>%T`hqv<-d&caHqbru#&>-}kI{Zd+5XYfR|4qojZl?%yv*b!fnxMdwt z&jO)NQz8Y3xh|8^4YVK5^N>)b={j`J!eVLhGU5LvBWDJEN-5=>n_P|qq^BDy#~%@@ zG+l=rSy-$w6#tCG!b<&&#KL0fY=BDorEH%;pHi~vtbDOHgO&V3_h`lN>w3WXOYHgV(r9 zn1;T6Lh>|CV}-34R+_H7;yw9|c^YH&H^sWq8d747$@nK1R$DC1UM0GDNspLB%U^qD zvCg+`s7L%S)XGS19UhT|#ggN-H08*kPbsAwYstOTo1~u`D#s;4m8R>EBMXZq$2V#3 z!S?UbHb`!bNQXh6Qu>;5yiVHQP&xhqp-R(r$dQG`lH+yS`6-&tev#gfXmXj`@su}u0xQ{W`IRK7)E0rHPX zo~CI?BfKZO>dx5SDU+uxIpg31@;WE7P23+S|t1*?S z@!Hly&GoHq#{atc_q*Chuc(ruj;e0AUmZO=Gt=$X+@rmL3AcCjvJ#k5+&*>m@|-pu z@W+z2mgdo3d3;orHK}0+xP$M+P)Fa^4v)EKX1wx*8^72>9Uag{$7TO$V`Ed2zwW?o zzc=6()uwJ)^~tK*IOCt{iRRDDs?(jbng-vHab)EvcI=mMk^d_^xktHZme)NldF3C& z-OB&Mt}=x*IeC2Yc$B|we1peW>&h-9^2-P^fcI zv=;fv$;oK0SN2QiL|Gd zQQl;zwPgi2dac{G6%Ih61)6k6>r?`_L1jRL0JpB7*N^5>sjYNC!OH)Gi~JO2ma9%E z0k8M0BDoc~=T`n6s~%%j5v?-6?4&E;*CfBDuIvLCBB54ULI$!;X-2ocBPklF+S}`% z3(RWszxR!?qZzMzZp^KKjK(HquU8V$qUL|}&E-$1ZK*>ZqXkrbV=ad*!v(vV3@AQR z&?~K|6FX#UbNp?~3g{^Tq;&;#9uL>H0^TLQtmSwdNXwCxzW@-q7!^;3(Xo}!R-qk= zM>`sSdo2F8S^ia|7aMqO@{jN?&t}&vFQ9>y?bz~nfNkdB>uJV+(btF-4jI^=ZO}ml6Gm_BTZ=iZl4s*RwPxPmyF^eZO$tVO-qudU{D5M>~q=jHVS@CO*9XX zUzY+tfS6MZF=Y)50x89bwOIo_FhZjF0CYJZ`?Vn?yanz$170zj1DR`*JTOa*t#3N!=`G@3VvM1}*s(xe7$UYLNg z9JeT5mhbr7Gtn&ru}pS9SclsibzA~>glrg)*#EP$T3}F`k=z=5u~9F&Ol2o_0D*?V zkpK;I#4G3vl@hGOUY+&IQ+^+~fM~vv?Umf33PyhRzwp`jb?Ru z1F95t4!J?&RY)BVYAdx!D}Mm{5_5{m0W3d=mj!4aj&G{SW^xeWQ?B2dW&bUf$&x6jj1!`2$mq<#C7($l_}Y;DmZgRGcWlK zv35;MFjoR$qx;B1w>0UV^=gJ%W12W-67R7_ymA~$6g&%TnwBw}MRN>w4hDcr^z_Op zyqv*HCtkYnat!B+`wl5qaI#U(;{{LnC>Qa9L4?u^muPNG ziZH+w+|7*gS7YuU29{e^rLky%0)qu22(Md{uEMla6MaMNwXn za9BcBzB&zF`nFr~qjhc30#j4quVJo|w5zfzqonXlw=}mokfS?dq=vi!P3@e+fnAp! zb*hqgQo*<<>YT)Z>68I3nuXUEGig<`p;|CL|Kzr&0^hVcJ3V^IFLf*OJJM*kq~6j3 zGozPO(LFVGQM@z8ax_Sc@)B{gd4scaM9{7f1LSov#Vjo(S_D#;u_9!oyt;wJ=|AtL_T( z-7m`aN6K3CvX)?LSE#J}zB9}hKj-r!e6fyn_UruqN0X1ei{7QS<;>--FLs2gyTkms z7u$Bd_wIvtzjyb((}Moc73Ry~?!Ej6`QIN7^Cg(y`e5t#6*Et0R_J`i!>TY}V_`=4 zTAi=`WcRb;r^S)FQ+nO0Fn`)sTd(u=pIm)*{ps~cL#N)*8RolewPs&?!+c*nSJ?LZ zvmf03!QC%8uKI%GD~>C==z!R-*D>JuM@+6cAgaN{xVvfI-9(VL91<43<+z1~m>72f z^DclOIWrC$6Rb`lV^6Zee4`D{DBBw5kG?3}8!6nc7w!*M9}N{AyYKwXTmXA7_d#xi z->dU`fB4StFMPD{q;n~AscU&>Xy3^&e=5FzraUdF8<-_JU-IFhFfUlNQ1!uzb78*6 zDl&jt!~9;W#ITP&VZQD~QAMPvPA{qp9yk#y>Hw|^5UAimL4?OHKB|6PzgYi7UFuwR zEDwcTXTyA_1yHH;mBFghVg5{9(Zh>jzUjptA+o1N-&6C%`K;h+L1@nrpq-C!U@Z|I z8F;k&aoJ+olkug>C3U$oRDpz&_SnEI)A_Ow!P;CFtr(wmi**s#G2M0S^W8r!{z-A< zc&`qBuHK(r{mbirc0JNJp~IhRBFu{xrc38t508iWhE2w8NoAy@K`&`|B0lpy^@U1M zZ`-g}+a7F-@TitYJ&$`AdzW@DjW1U&E1@bJLP=$7VD8iTeGkDS?6+v4KL}P6ts6jC z^w1mT+g`i`8xzZIUu1@=3=fpdm8_o)gV)S3Um3@%$yjMsr1Y>}dU#3v%=f7;RBE`n zA^?o=MLJ&;;j47M>e1E5ql=@4@u8dBk8aKef&=%H>}NSob0W1JdTmFTKWVEysPhLu zxe%%E(d&C6^}Tw1@6W)*ru4q4NS{}Szj~06&xTN~^VJXUhWW!Ukhw;^wDF1d?C#UM zq0*D+gV)1cGX0T`nT|iqS0^+btg)P^DFMn4dVpHJsP@UOXXQ`JLq$iEIe|r>I$!>w7*mi@g2V06k;lguk4Gwx>y^hpul{M>PwFD=eLDPA_Wf+~FXcazBmJTd zf0ckKK_^UCgeGmem6zbQYk9}=V90eQ%%4prI!Lo`8fs(i99%pW&=^*S~64d^?1hbFzGDcF2A zR017md#D{tgUdUY$3s=V4NrjXA*+PpXp#FTFt<0y z0hx{0wnS==>$S&2wI|S|&|#awTZC`X`IaA7e^&o#{qoyiRD3b?r-A8rT z(J+6^s-v^>?tPKnHTrIdC1OnOMkezRGVeiNgh$0a%6^=`nEzxT7C#9XKUo0SOwhHe z=wWS`KloyAO=NGIz844S8=t-r+It2iS_-%kzEtN+BYdOIH$HiLsbXnhIU~%s+iL1` z9wMjqFn{6&4)s>Oymjf&XD2>A5h_1z4E1DUB0j+AC+NBM#rDDv@_vx_I|cW%p7RB0 zra#R3{eq7QAe^lk#&EXy3I?Lr9HSVWUUQ6Nbc%^_C!2RNg4}Ye* z9aURX4oeGm>6-2lN6f>DN7p0!+x7kJk^SBJ{_ZctNY9&k&zoWXy4~D2S~fPV|2wZ6 zXmcAa*Ht6&v2W1_bDG^Y(shM4UhTF?JaXd5w~f>dXpv=gEI+j#&dBpM+b6|6hbW=_ zIFmrv&5$%{CG}>uzS~BkO@MJRW`^+~R+I8U3})x=ug8(u+F^-pZ4v!jF_yJ^NGB$n ziZ;MJ*V7_heOX6Xw{vM?xiwUQi*U)}6*FR6z5arFPb;j$C*c;ov}LI((t1g6y%Z|F zj5F*S&%zZ;L$H>ihW4ZF8fHhIWRpDJRvd^_9M&sfEg*7aNIx^L!~6vs2(Cy58xjgJivMKtnfz3aG@j8LV~-bvs)<6LIgB_J zgMct!26RnXVt7f{P)+A%-jnBfjBLYvg^d$*edb!KZKAG<~3toTgO`9|2B6W|nq zpw7lJx7~hC&;r5)?)pIkaQI|a6IAy*u+?(9zpg;R?HpwiF3~O5V%tZ!qh;>E49gm9 zi^&M0B8cO+kwRXq zuVdqZJGg(P&cFc|Nk!8Jp(r0=Q}4lLzJk5qW83E117kq_U4XsE{gUVQxPHfXAD)4C znttPlZ$!$P^s**ny#UIL*%(&p+AN4*8Sa@Fw?;_t?&EJSe*5#%pYHw1UcJVADhE6x z>bUXv=Hg8V<-W}3cI}JotkQQ@g?3io=bz{8eE;1KzWsx5|5aY~i~Pb!zDv({Me=L( z{FV;ei1hcP0O&~P&OV2sZsG_sg9aIY`nlY~Y#lg5q&0&tsz#ug?G z%MVamtl!Ov$&_c(D5AI|mxK>?U+}C!!Y2uh93~%S2?XFa35{GP4Lm)U@JT`=k4ZyF zAON>XXyh|#xL%4z0h316OVQZMq*47+G`2Bm;Hiv+PZD`-XVSo990{K!G+txUs81jO zw@GO1VA5!ODH=POG@4(E#x5oeJc^R=Nz#`>CXGW01mHFajonNdc+w@|lf-}QSxo~@ zycYEvr#0rw>9JPUFuxB@__$|;t8VYCBm^b}I9D}0q0K68 zFJvXjJHI=zL@+Ss9ku)G=K0zS{ew+?!*%Xjn9DjUx-WGNU%z}#I6vIiD_p+P*?XZ& za5XeFU2E%VYU&>DHY*M_Ha9h$>vsvRX-%7HZ)&=I`*!2)w#I-m)igZVGdQbnj%kVjiM&HPUUuB;i-;kI8Sw|15TH@1wWi&Iq7;EV5ad1oJ)X1D|q!Ywb%3wK|m*Yy+XHlN@%S)Vb;PCD5?EeW9#7=Emm^d zIMddCsJWSdU<2)bx(P$!r`{K0tFAf znI#QxYYpSxz{ITvh%iAW?a(sHz`6y}E)da-+i$gObj@JCr5RXvow6BGS3uPS1P?ZyZ#*-N(j{jl*!d;D?{B1QUQKI673&sBA;!8|eww?uj{WBr!>^-pN#m(6j)0!mVf?u7%!_#JrvPt`5#1lfb+XtcQpfv3sm*K~djVQ=)0-Mj! z;K?O%KHPkrutmzIYOHu~b51PeZ2sn)ScZ)!l3saIYUqM@3|7tV@y{c5W#(&2+XwLH z%kAsfIo4Yl3Vz#}@qUxJHjYbh#+y6zT^i%<8RPAR^YD~IU!yTzf-zoZFp#j;@8oC1K65(G1t-KUNNb|g>-y@@gDc0uryLwuNT%w3XkZ8NAAO+ ze0U84w^*N+1P{F)yf7WAlf%5n*xoYVk-&Yj#RI{HGr>+z$aO2sd#w#U+#LOA1YX1l zp1c_<9}Dwuv8t|w%CCm`YglESl>NvPtm{~oLVLT-)1Qeop3vS4Vg91Q2Hj;_Y!5b` z{gQKbXQ*&=%UPwtp(|(3tmDgEE~g-ogpIg$mU1ySc<@y4%*~Ln7PFT^<(I?!0IT6( z*Y!~4NSMFDs{EoORJk_daHtYa7hGi`o(@$Gh56x^tqXPyg(~62fGaG-q^t(JZiXsj z{U=+KxP4+sH`p~As{B@%zsV+hBUCvW=D(GylJ1=w=CS-{sJti4ziyzJ+p~Cbd$A-~ zfBH*qduPT7sB^pPhU5Qp+jB-8#;!^~ysyNMjp3@#*jO}oY|IxBXT6xu9~*mn*6lTG z6x<*(c9awx4o^7)+1zKsT@eK@_@<(YFYbMp`-jYP&aAT;|Ip8G&wA~vs`{*0a|RqX zh;K5DjrG^w`uI887Jy{u}jt`Igk~1=24(4*s{QL8N zL znf>b*zjHCTO$a+Ho;wR8&Jx{O5_Xn7cjn*g`t|eQIe&lXz3UIIfA7Z6oyA{nteQ=ebK5~MJ&s{WV@K7=7^ROi`^nw%Pf*@Jokvzz*V=63sO$k zTNaCZ!Gh4am-5uSajSC=t%Rj^%Xpr1FT1C?BTzej)Iz{3GxFxE-Et&)t8O3PX1d!k zzQb8%hBWfy+bKoSGQQney%vJfgXek9nzhe?(?Vzc+H#7==+ETNNftF* z%2KUTc9})Bq0H4B$p+V4DM#IgHAeP~DXupAn~y*C#}u|BxSJ%0Yp@_CIB!3$$BJ8S41k*bWA{!jZwLtOCLCqgM^PAC{Wlj zizh~F%;F@*8V~L)ioeIZ>)UCWb0^KQ6wEY7%9XZB!V#NMN^27>*LP7kzKK#5d$Y%~ zo%<*(OQk$U3m{6_6PlCq;~>guIZLGi69m7_l?6x0X)aAIXxRy$w6(;nf+0$U49ld)1_nWMEJ%2*V^-%OTFz1vej_WTIk2E2T9*lu zQW~Ce^#iGW{kSvlZI3}r!7a?}Ax=g-()I>O% zP4Mjs3ITN;r+JoUZYcb8G=?VHM981$+ z^fO4BYuXk!0uX6AOP%Lgp5_pXYLGhV@I^O|0((ySd2frMV34X1{dc zE3g|Lzv=CUhqblshSwrqyb-x}GeC9CQi!_g;!W&j1R#ePCe3AJH&rl3wo9d7dc5KB zo8E4CSiHWVnn^i{Yhn0Se z!^7ffw3e$;C({GXt;5?6QdrRFD9y7pBaPNc^-_c6SnF*`>bT14qq#IaB}~F3ewa*> z)N(x$1A12W?#_{(b9}=XH`ZdT>5F+%=4tsLYmtf1%>1^M@&&Sahvr$Dk(R5Z{a=aS z^7u{fw>+%$e#^t+>E;kuH<^vE+o&EuHxo3^(u{P|2>o5tS1?ZE@ta;ZJglufPU5wQ zXE)88XgBTDM!;^SXr84R+D)0%@)g((kKgom!^7IzcEf8C&u&_|b~DYiO>^t$@efm2 zu$!AS&(e(Src^4G+N36KkI$=v*T!~k8#9ZkbeaI)Hg2)D(OgDtY=r*qtB9#Qe$)E~ z4{K}t2CqdtyXoNCjX-tH(u5hmm}P;Dco-DTWn?3H(C;4p($`}6{HC`NUMpksp0wR* z=bP*dlIEBOlE^YP7eq1A){<@=h1V9|_|CMBl7zSP>kYiRViAV5C}F*UhsCqsBTRax zalV()rMU$Ajm^G8yXGhiOEa|JqtY?yI5&>->d;tyG?yR+9u`ke@$=j~ta(}kOXJob z`BLCXypt<&?ivAAGt7g>-rqsqw;2yKmyx`?q%P@%)XkMQuMS>oPI6lli*A%Z3v6lN zb58N0jWHPj_M!Ok$X}eR`wCTGUGUK-IOXK#}sZ3cK zxA%glJKk8xwATqNpNI8`)j)FzJu?sMTNIY1>%ii9`8g`Xr2h5WtPYyvw}nw!MqCs9ftn`}aVevG7 zF(b`iGU;jkn&TiD@39+XE&Snsf3;f+^zux<*1DQ2H#T za)6Q8FfH9w8?rbbQ@Si&hyHn3JWUSM7Hn`-&7sdIne?2j4^A!15!%*GmgQ4Qm!<2F zB@c_|OY!eeJgoHZP&_Q2-bR?TH{=5x`izoEZ{18nG`Eh~li2i_HWJ`8?Vi2W}1>niszs4Z#KNTc>29U^|QeUnL^W>(A&gz(`|Bu z{3B*at$mYa`NxzlOV=Sw9u`lQ*O&x0Bufr`M#&`0ql{~sTgNKR+Y}af z`Vq~uG^5_2OL`ktX}Q5b@;|N zC@e_k-_txxGm^{=7%91=ces+_)xndDo3$|sXR)4}acdf*ima68oIjyCmZq(RC;y2! zEKZ;3{3)w}=IGuCVjsM<>U20fze$I~v#4}9JPoD8nWL>_sgpBL4Vt{oAZRX5n*WiO zvXp%%0Fm7&dqQ*0|3q^vb^a;K(;R$1!JpAQOXKS2QU41DOLJW67Uy5l=PZq*&Rg00 z8H1%cX%bx`NgbTo7w3PYm@JK}m-Dvj+-SX=ad_%_P2T8de9@dUNOLSTYkJ1wm>P<_ zvF58Rl*b?&<$qf(mW9#@_vE`Xs&C#q)imkz_|z`3dfJWulpEl;tNVenl7l(?Hip%F zxQwn|g6o2PEv7m%(a?UlwXwZ}{C6yTx3_D2Op{e@Tywj<+W6VIxjwh<9v|>cx;^8= zs&7Vh&uZht_w<`SZ#3y>YaJg@CdM^Imz(B*JNQ};ZTvm`h|4`U=TRoz_?;Zu_=r9} zp?Jren_E)+_4)32JwEr8*3zeFvx=rQ&v|G1BZYJG+RdJMU579CIJ$Za8}=(u7>q27a{WaOu(rz80u#VeniQgqi5Q~szjipYNe$jTVzM5J6WL0SY~f#Pb9 z6vPnQI+SM!gKwb`!CGEB@NA=wSl>rS3+`fuYKa$U+w%QE^ ztN#}+$_tcPzBZ})Jf5?v>{j94vig7U(Q|xM!X6pl*V60r>athYRu2FSl~B7PBLlgH zG$Y$@%c>5l_6)f1`R4V7-}~Cw(VWM9&*fG@My_ea1{ z)pjIixL{Y)K6Ta*^lCfm#0=Tq8h_ii3VKQaXzMw~!Owc*p8k$&)yNFe|X`xLJ(A>nOs*W>d{Me-nX zRaRzh>ZFW-Ziljd-{ibD4BuEZc`uSZqDKlOUG+h%!{W#;05BBXr>Ia71kgx<6N!xa z2IOfS>bx)sWqIzYsbS^rta~o9Z6un>9Rllcdm`4$5RQ-y0*UQEJFokk@|^6};k%gz zFl4G20}0S51QMWOc)Yq7idEbrL0Wazqs(|`Ap}GUNp?VXPbpxadC@!I$9Qn17l33{ z#XbnW?`a?!3RMxwRbk=I5#zg=j93nlPp!dX6v$;&0c_G)MDox3RAm9A3hh8yNV?lQ z+xxF#3s2x?LCDy(vRq34}@WQ8|6`w0qv86SYP)(P1tltmbHlVqZx+cT>U8>m z%ai#0G+xf)1>a@WKD_k9g;X`Dc$L}tSz@O-BP2lu47%^qV)Z;C;PxOD_volNeozPS zf*Twn`B5o?gQ>$pRV=X zL&tXHMr@kwnN|ysE!#9YrcD93NDf}xjHF%5g~uZKL?$QWhdh_Pa-XW)mdE?R718Qp zxTE@RjbGNL+%vAR3*-(M?gK^?k7oMbv9m*c5EbrCLQg(}qbAo?#XIG@(>!;NxY+z0 z2%>`bO=Zo{LVSwBM*DMcz6X|9LTOm24+!=Ch7%#7o7{>4L43UDu^ti{O^-s{%xz{`P zeEa?1eE(bbAKGHjAK61fCER_x@KNE1qamRT^V=V7|4=pZlx9^xsCrxz66#}^VWA-) zG<>}GW$BC3u;Wy~aVjL7Ha%?&2#p_Kd3o){wQy5Ups6P$^qQU;Z5;>+gYjHp-QS)6 z{{8RV|J-7!yI}poVkx<3h0))Db;SA)m|V5OxCRq)cf+{5fgtZ#Wh{Kh`Ysk?LhgLV zoex3s=By?rc#T5F7IQ*Evk8urZ4U{@UKj5V7uT*7*ZS*@1&fbAwEb2-fPFjvQGQt1 z9}xEc@b>S2@PiK)dzP}7dY5+x51b4Mr{ddZ$TKE&f>{<2%048ISP(TN!1%!k(6Ca{u(;=C<%`N-$+1*HAR8Rj zp4L%G_^1pBl^;z-6+}wV-JTtN-ua|6T-_O{?)*vJPaQvYgu4a<@K-(fv+2K9{!$4K zO$Feu8Zc#Ogb|9+q>Zq$0d9MjcP%@E_A?>jY%0+iHGDbTR@Q|pJ60+?mij&&{A4g# z*@L>qeljdzP!0>IhG(~*fAI7Jh_FU~Ti-_w(E!1&a`8Y&=p?avotpX@v>l^E%Su^` zzx8ae3>walP`j3#%e$8+f;AXpQmQFQXoyPi-jjRb`tCq|)K85_crYLw{P^h0&KI5G zLuUeq&=Sz3Vqwf*2VK>rSP?(`J(kHR%7n(E-+J3OX++>yO-%uzX%Xlij+Jl%EpopI zeS2#hkWqOI{J!8G|-UH#i^(%W}ED>dLFEUwxkOhwl!U8JpS?=?~r-h3n(eaar)M9Tp;ER+X?^03ex5Ska?TdGu8?rzHM)BHN@e@f;ZIL|aw1rHnz(u@G0_-6+6j7Yc)hdu`vu=A_?@DM zIj@8wG}9mE{C?37ieNZfKZ?WI(lH!}UbT+n==7>}0!OEqkUPb=QxN1`>l_xoYrTzy zn2@^#<8FZrXWxh6Y(gb(kxH)Nc86ShZn@nXW)x{bZM4HNCQP3oPG7^~z*BuYk9jEaT z$FhqxFAu#q6fD6ZCmYVPK7>Z5CEKS(pA-elPQ?W$Hoh^b^yDWegOz8~s+7D6jTzPx z|0-mX!2cgr$GRMz7R=H$!XtXj(!Lc%q(xo@^? zOj`dZUN_O^He0T1Jn{MLlUeA~%(jtfC^YeEwoMw56FuMLshdzE&+2$_YCW9M_iKJk zihB=HgSFU`K+}yOY08Hznwi>e@=ohO3UORKZ+8_^1>(Dg~=Xu@amM zGbY}`Li9DE`wzNdl56C%?9Y0Ght7wD3nmbpk@Pnu6yhlU`aX{%+mKLY;sisVF_&uUsB6OZ(*g2c{6UdQoz^3b~^G!0s5Oy|6Di&gIo9o4p`8OeQ?1H7tp@=4^a9&%k|hZ zx>LxT#sAHX*k(416KN!TO#42voZAu}!fnEDdgO5+%)cj*<;3NKgpY}X!h}a~YkInU z?bC#B_{?&?u@)vZg}4^(UW*-*AN*2>i4uoX^7AyXlz)8>rNa9e8s2Yt&^koRjCIE9 zuq|}vOR!=iy2a?4jd+JFc!+my%3e{wDT`#8PBgYTh>9l8!Lq9cOYr#%MZG@nw4%-$ zAPdc~ASc4k0a2TeX70GXx~TiaNnGgz!m#0FUKcg@ZCG46J>)1-aS2DAflFlD)##ED zu3{N$E~APLOJTPVL`M+oHCc;f5BVZH0VKN2W-KN~a^$=6q{~?PEgXRjFLcXF#P;62 zqC2eW_mDzCw2iI_-(6g@(&k`Oi>#qB!#a}s1FY&p1gzq(@R*ji_JP%Fi15Jj3&FC_ z{yVb|;jMSK5h;FnDO}MKsAwTG1W-o$UznTgh(Y)(aFvAIl0m@x&%gQPn?EW4>HZ(@ z57Zm);ebcR8P}iRcya>%!u2 zp*>J&4;R)43hRGf==j^ho$9{<_Tqj!2gOZ1_YCB~bK%zbIB_pQ!iVRh@O}JT-yF+^ zb2?d4Huszk=OhfzH={ISzsh6s7K@YP11V_OxO{MYIt7g!E)87WOZcRqk;|pAD}eyq zrl7HnOC#p@j27>=eWEfIxHL)_F3qLjgWr}iipkOxH1fE7R4`ncOF<)_O9OW+Bz#iP zDB#i%69~X<3L1r68uks*DB{wn*$|EGTpD#7qOpTZqhUidc5-PP+z^d7xHK9!L}M41 z25uZl_@vP7ZZ3`11OjlIg2o;$4czLL@JT_Vm`meu0s**9L1QnM2JUxB_@sy*`_|Hc zU9KfVDh{z0D%n<75w`e!%YOwKE?!p09(;X1S3Gse@H&hIm zr@C8=$|ihM_e4^3(k|l8n5Jp>tm3)XRX-?+u>Z~D5&JwdVtf4w<0))=lDm#Kw;!3) zqa}C9KDMsIt*sOUE9ml)4QD5?adWW>X33VPCfzsJ)jD_ggc!Y_@WCeD*|;JPY-o!s zP;sN1QPT9D-ZbIyO}^U%BTJA;7u1Y0h}{BdJBVn`?Tyt;nnsv!YX#Qrr%XoF>(g{m zg*{248%CqDIteskyD|u*z&h-P4bw7giqjVMP#cuRt3LHzjhJ{OzaO`9lWlJcJEVEw zsVN4xP$bPyu-iz{prTEJp4?v9H?J*h?^nET?8!A0nPQ*_!Mp#@8trgFt`cl$)F|WdYL_Th1?rWmWM_L1$p@ z2GF#$E!Pxt3u67^)@+JlO|p2wtIgqtX=9ACMcYEg6Jniv7KW-$`KEh9f!}T>qaYFl zww|HEn@eJUxb+%g8Yvs9iH-NRX2(Kz<8RH5Wt6;;3@FobQ!kt11#k5N%CVR zDUXgsXVb~ceO~_6d!#E z+wkGw11_q*DDxkF(|_S+(4mBcTVz4YI2C~_V^0qHo6h)qZUyb{h6GP+2@jV;fA?)T zdf`8LBUtGQ3GeV8jRh;OgoLa3D7H8DcW?O}-OF-tf1k16Gx5o-;Qk9C;UZy!t*brh z@;9IT++yp?(qOZetwx7!R<^u(>*x8FyrM)BCgQQ(lTY&fhfev=+z5)vn7tgV91aO1 zyo&w3*Mik=hlK0Aho5x^tCJ&+2CHE|!4)pz>0q@K5=J-r%-<^otKlfX7!NTetNz{_ z!Rl!H>0%@v%aZ$59SM9uJ`8Mp6HVuE;weZYC4`~_WE2d^6BQ$fk zu8$lEB~;Wuftn&#?VhIoTg=&D0_%3f3TN2iT%>Vo(Fj}F^9pGdoHT<)y@*Y|tLW-8 zKv9?Rf-7gFArYb55Yj32KSLQVb!(5{^0_rDE9)QjTCxrWEC>I_QvZwG{+z6F>qkd_ zVIi5%o%t49;lqWXwe)}3w!g~X9nKe5^2NuypO-u-3FaSs(EEz+KMIx{^4p64dtPNQ z@4$neSGL^WxcI9V{X4{vt?HGnIBY8m*vdk-idVL6zj6Mboqs5O``V*x-@5*DTj}RJ KEVeSj>i+|udEK1= diff --git a/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.py b/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.py deleted file mode 100644 index 0e6119d..0000000 --- a/__pycache__/ProjectDetails_DemolitionANDRecyclingData_Window.py +++ /dev/null @@ -1,351 +0,0 @@ -# -*- 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 - - -class Ui_Demolition_Dialog(object): - def setupUi(self, Demolition_Dialog): - Demolition_Dialog.setObjectName("Demolition_Dialog") - Demolition_Dialog.resize(1440, 1000) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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(Demolition_Dialog.accept) # type: ignore - self.buttonBox_2.rejected.connect(self.show_warning) # 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): - """ - 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: - QtWidgets.QApplication.quit() # 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

")) - - -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/__pycache__/ProjectDetails_FinancialData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_FinancialData_Window.cpython-312.pyc deleted file mode 100644 index 05f3853ea38d5168582b20d7621bd80f161c3408..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36818 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 Xe*5ZMS3mdi&un`>-eR%s)dc<@tLPR$ diff --git a/__pycache__/ProjectDetails_FinancialData_Window.cpython-313.pyc b/__pycache__/ProjectDetails_FinancialData_Window.cpython-313.pyc deleted file mode 100644 index e3a9c91a71e1b2f836fd28ad3620d837f7a2afcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37272 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 V>W5c<``S-!d%xIbv6btr{(m@J4pIOB diff --git a/__pycache__/ProjectDetails_FinancialData_Window.py b/__pycache__/ProjectDetails_FinancialData_Window.py deleted file mode 100644 index d333163..0000000 --- a/__pycache__/ProjectDetails_FinancialData_Window.py +++ /dev/null @@ -1,382 +0,0 @@ -# -*- 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 and do. - - -from PyQt5 import QtCore, QtGui, QtWidgets -from PyQt5.QtWidgets import QMessageBox - - -class Ui_FinancialData_Dialog(object): - def setupUi(self, FinancialData_Dialog): - FinancialData_Dialog.setObjectName("FinancialData_Dialog") - FinancialData_Dialog.resize(1440, 1000) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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(FinancialData_Dialog.accept) # type: ignore - self.buttonBox_2.rejected.connect(self.show_warning) # 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): - """ - 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: - QtWidgets.QApplication.quit() # 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 ")) - - -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/__pycache__/ProjectDetails_Foundation_Window.cpython-312.pyc b/__pycache__/ProjectDetails_Foundation_Window.cpython-312.pyc deleted file mode 100644 index d6ffc0d04a75c318b8531fef4ddf8105c2a2c8ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54201 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 brQ>(H-n{h2rEgyTX-3f}D=Znss?PrpwTOhG diff --git a/__pycache__/ProjectDetails_Foundation_Window.cpython-313.pyc b/__pycache__/ProjectDetails_Foundation_Window.cpython-313.pyc deleted file mode 100644 index 435c012a1113428dee670e6c470c474323968b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54909 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\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", "Componenets:")) - 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", "Componenets:")) - 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 ")) - - -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/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-312.pyc b/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-312.pyc deleted file mode 100644 index 5e5a638c8d6956610bc39f84180bca836c44ec88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41090 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 Q*MDg#dcMtMDOLsk|ItR*qyPW_ diff --git a/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-313.pyc b/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.cpython-313.pyc deleted file mode 100644 index 1bc78f59c10337bcaca51dd5f6070532a3ce6345..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41586 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 E|BmyzZvX%Q diff --git a/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.py b/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.py deleted file mode 100644 index 5b4d907..0000000 --- a/__pycache__/ProjectDetails_MaintenanceANDRepairData_Window.py +++ /dev/null @@ -1,420 +0,0 @@ -# -*- 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 - - -class Ui_Maintenance_Dialog(object): - def setupUi(self, Maintenance_Dialog): - Maintenance_Dialog.setObjectName("Maintenance_Dialog") - Maintenance_Dialog.resize(1440, 1000) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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(Maintenance_Dialog.accept) # type: ignore - self.buttonBox_2.rejected.connect(self.show_warning) # 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): - """ - 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: - QtWidgets.QApplication.quit() # 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

")) - - -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/__pycache__/ProjectDetails_Miscellaneous_Window.cpython-312.pyc b/__pycache__/ProjectDetails_Miscellaneous_Window.cpython-312.pyc deleted file mode 100644 index 6406c8d6af4e09f1f61fecebe1c3ba1520ae3358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52237 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 diff --git a/__pycache__/ProjectDetails_Miscellaneous_Window.cpython-313.pyc b/__pycache__/ProjectDetails_Miscellaneous_Window.cpython-313.pyc deleted file mode 100644 index d1c1c7e25253bff863297342e102b027894ecd19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52961 zcmeHw3vgR!b{gZq^KA5ATB9c7A+VgFUf0x0CfS1 z5^b%$&URPcb=I3*Z|rQGtfX-wDYJ>_G_AGm&aRqvT4%eR5(IYOph{+E(&>bqPU6PS zq|J0XJ?CEh_%FZ%T<{v=O6fxO{r`LKIp;tB`Og2i|BIg$6%|zS9#qwQ?Xc4ncSdM2YS;w=L?BiC&YQtyQCvuMGD!DdGvBho?t(z=j&R|}% zRpV7w3<_dy`|}oyYsP}qc_;Ff{6VW@Jxhd#cFSPCVw|rI( zPrqv!v?y7kMadSk6swrM$)e<-&6Qj+2d;TyuCfACj66FaJ5S2KVae@wc_i>z>B4p`hVSpyb_>aX$Sj-vQ`yt}Z9J#(yKF&=`a=EA=`aYb{E_LQY& zvs_rq=x8rxD0{L8vK`MdS{{nITvif&OE@))4P+Z~ZDzDQ)a7yuf68KxZ7jw^!&Hb8 zV~~oSOJ^~|RAPuS4yYXj&ZWbEEPpW!tOHi6BQ?Zz- za#<)AiL1obi_2w_G$>D6!}F7bi?YHqoJ(hg7M2?xf)&X8GJdGBVxla%_=aFQ)8Y?a7)mW6i#x)kjMOk?Rvl7&_ybWk)mf>Lp5my9*Ubemkv?7RW~X(HYOLeKFH!c zG=*rPis+Wbh$czH*q)STls&hSmN8eO>|j;^tvgwqho&sER@^3TUxpfm(%0D6DXzmj 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 diff --git a/__pycache__/ProjectDetails_Miscellaneous_Window.py b/__pycache__/ProjectDetails_Miscellaneous_Window.py deleted file mode 100644 index 8be13e0..0000000 --- a/__pycache__/ProjectDetails_Miscellaneous_Window.py +++ /dev/null @@ -1,566 +0,0 @@ -# -*- 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 - - -class Ui_Miscellaneous_Dialog(object): - def setupUi(self, Miscellaneous_Dialog): - Miscellaneous_Dialog.setObjectName("Miscellaneous_Dialog") - Miscellaneous_Dialog.resize(1440, 1000) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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(Miscellaneous_Dialog.accept) # type: ignore - self.buttonBox.rejected.connect(self.show_warning) # type: ignore - QtCore.QMetaObject.connectSlotsByName(Miscellaneous_Dialog) - - def show_warning(self): - """ - 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: - QtWidgets.QApplication.quit() # 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", "Componenets:")) - 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", "Componenets:")) - 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

")) - - -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/__pycache__/ProjectDetails_SubStructure_Window.cpython-312.pyc b/__pycache__/ProjectDetails_SubStructure_Window.cpython-312.pyc deleted file mode 100644 index e58b5e19b130db9d43c370d438768002eb800345..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50769 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
55`5fzdC%oJG;SNpc+W_{MNQKW}(nK3Mee zgrm$k3SDiFG~yhT-LifHh1LkXj3t6$M|6Mq@)e>}8inIwv<_W|#u)FtoE-}rpuU_P zOP?1GVR0Olho$Nc7--Mw-J{=MDr@!Qg7V=|9YuoE@!0ux;ibkwA<9qS-952|3y!GWu~ZP9(we zSLQ!yWelHQ!Ha?yA71cllzv_c9z}ByUK%1_@$?f*vImUvM}NQj6!<1f=lOmYb-cXw5eNnr)#qJN#>Q+=L@S;EWMmp8KGAX2+44V?%+hVo;Fq!@IU} z7uU$&$)4G|Z>IfXz~KxEgZRi;75kxUrn2?%vcSd@LE)sfvVfo4aJ&0K_DsXUAD0Iz zaDA@!lrJ2-(>zntF|+?%paj-p_sFp4&g{P!D2Y^mg7ZS4Bw`%m;a37B@Kt|+ zD#*-!{xHU89}9tvhl0XkitRI7eplK#sF?Ys=k5Q)mcO*$u6^}C4yQc{0|PjxGB6O% z9vB$)isNpK=MD^98F#w%C(2)7n~Mt|6l_Hw*feH+Sp+WAQSh=H{`cXjrf=JRnSMPh zvm@=7YF>WkiqA`1G9%e(beJKY12r&kymBqfI8Jn{OO*=zZ>jNF@(Pwa*NE@Ro#Y{$*YP`1OL z?YO<_UcsG$Kz7xe9Z!q4eoz@I+UhUb8Yrrs$#~`8F5MJZy7|rarx{t_IQ-V(ndQa7 xjFP7ruY@wz`!m)DGd4cW$bGZp8wcMyc(eE2v$xKE^Zd^;3O`$J%P7)x{(oalen9{L diff --git a/__pycache__/ProjectDetails_SubStructure_Window.cpython-313.pyc b/__pycache__/ProjectDetails_SubStructure_Window.cpython-313.pyc deleted file mode 100644 index ec6ae8a2869fcf10dcf696060e9336bcd023483a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51460 zcmeHw3ve4pnjT1y)QAs>dXv-(0x7;EKE#(qilW7bs2BAhhLmiJYX}5~1c40{M4Q@5Qdi5QlK9rX zTrQPL<^N~U=mGcx1Hus3x-4W*_w@Y#-+%w#-;e3(Zhlf!ly8FHUmh)=-1dga^q=sC z{ccSLA4YjTv;Bl%)p$BQPDW7dS6t4CTnkzyv%Vj3y7RV}4p^5A)at$OKm&~%M$&(d_p z$K_9~npjmMEFx)*Ih;4>>-8v7ICs!@bVd%Z9`v1;$0j77wxpe4lV^Vn-haS3|`U7habvjM-a>YWi=$WHO z>1&K0#kGrBfrmODwenOJvsJJd4>O26cB0(@;lA)c&aM;W=OvtrgM;?ia zYYn-mda3K!4L#FK$>`FB9ZJdD6xhD^QwIR@lV3 zbXK7I7P7pIANreU{ROQ%xkXqkEz4|%9%qYKveYsXX7 zn0_pvLzM2-jS!9YVxV;&%Ylb!A-YaQbmvk;6XaoRPf9b&o)&ITon_K%QOcPWKmLImzc#2Mkgm29S=Dw32aGWS1&K7#g%z1saz;5cV`gG=y(~hJk2p=v9z$P1T3po082_=V=O7I%wtLA zLSb2*K`f(hGJxe-h9D2q!m?(Cu%z@g#**U7JeE{06qdCa#IlCr23XoyoQG*)xo3s2 zr1Uk$lH$rdmQ*ejmU~Go(;MeU-^>BaA&w!7rG;gkSP%UrG0snrhw-S8(u}f9Lk2Aq zomU2}&oO)OFip!eCbSHdhp}ZSF3K|dNXx+YfcReu?}pJ?A+XHL@%$!JT9#=7pJ^61 zFMX59@EbU#uW|jSxQwx(xF~E|GKft*!vnAxVR0U&iB02*U_#HO7YGGRB7D%6xB2L`1Ov^HLV&95<^`Z1N_F9T7^P?&%7YfTGB$izq0TxRE%jkLyz|zh5c$gNJ{o>IT z`C3isYm6nuWn9lFE()7t9Ge6dP5oC=WQnJ>WBclWk|H0hS7j^Dr$e*NG=r~E(F-T&M{%J zw6Gb5mUboPyeNH*v7xw(v7xvqY|fF`r1yUE0z(0?`67$+FfDA(uTUFE>1&J)#g+Ls zkjjO^@OQ8)s%xD42H(8vAX<_-2_;SKIGAa*aFQ>RD%UsByWxAOaz%pNA zaUP~+nKSCV3O##BVFTj0$AoZS)7MyS*8vwvl6>dD1D9VKgC5^ z`3t0#(;J;cXPLmtU*^_jv9zo_45O2k=m9Bxjjc>^8P@=ci^Ap=5}Ph&PadYwYW6W| zz~&~4^Dr%J>@Ye>v~rNj!`Lzu7iF1?+%gHu6ZJ)2M*aSr^tg0$Yw4kIglPl$f0e~~ zm?jD*6QV%nVT=OBMWNt`L4o2jMj;I@9SZMo+p$;*b>S$Z1r&ap#d(+(3c2E_=oH76 zu82&K$2WL>EM~BO=bMaoOo8hk za%vVcDADgSIvyJ8MtS=<=SrSq3Lbct=>pzyi^X}ErUyFdAR{LC3R-NVjO!QTIZ#d(;Py?vrjoDr{(_NMZnN^_MgjleZy zF}0^8q%wXe<3&abWqi!yJWRWc*Tm~f%b4N)Qcnf}YfC*KK^~O-W7`KxGs-frvy#PF=3nvh zu$Wpm2&s%8EaPOfp!J`zI1g=q&f_cwwOjqiO0J&jMDbF(-(Y#i=uVG~NURt?=q@ph zGF<2Wg30Bf&7jWdG^piVEXG@0cg}E5TBm${k(E1nP8mPtD?M%BcxKxEgxQpbF=vud z*imC4-RX>|KD;h7E{|RLMr2jIv-u_3?+TS_;^U>|0TwsQk#=Je>fLZ`|0Z8 zMwZ4yU5!uWWK8<~Nq+(!{r>bDykuaHFwdXGGS+V84(C2s?G|kfswId$p_H;ARXcw( z#`2UW{)}P3L(!+Je}wP3@#^fb%qbLi1OXS(sw$fLnt6qhmb z6c>g3KS&GtFY9nh9eF~h3?hG-S@m;>{C{M+@h}DCDK2B=DJ}~6ucU>%los-YP8mc# zx+eW|i2Q%%h_hG<$WvU#$WvSt@;B2${_V7oCv@V-L+eJpY0)FWB<+4mBcUHL`}2_5 z0Y+k8mYPu?GTHu$$>m`R_)}aIl3!&dSmvmjkk2LQa8A?*C%2`Kl{Le*`~{QC!xU^u zaZzXVCC#uc|0k2n!xU^uaZ$GXCbt08fz(GqgnTaP zGu!fYR@MyL@_#Y8JWRos6c=U7Z*dDO%a(+EE=gxgfosiTDSW59!DvCJ|IOk&OshBW ziQk5=G|&D!9i^{vOHOf7--%+s(@|WR|4v8cLiy5rqzC*TjvI@m-~sSgxwId6!2e}& z9;WF5-vJM}#=cf@Z4=}{*+2F>9i}A9;i|AQtFK6f#q1A zED!$f0+btY{{s@d1*rN%(%zuoF11) z=_v2-#(#_XAbqhMFE|2|{{U*)taS;sMVT0_Zfa@V+tjT7UlTTkb6t*6$+h@H z_S?N3Bg0-v@s4;M4!3utdwROh;d6`(c$^N`$XUfRp*W_zBWJJsCOz&*(7dm4WI!Gr z@yb4_ZW@$?Ka1xbx$JATJEo^yveV&{Js@h(H!>=_N9yYvlKj`_x$1U#9An;wKG{1Z zd%gA3?uq_z{`8D@vS-HU^SI?#prXk6U=ev53tGCgpylJ^ zA29LlausOEZd)1`{}f5wI+B)s@_r~zl%z?1sK0Gq+<_cp$f3)>W${aKDXohONI0k_ zyJWX?WK8zin_9yK{H9GF0@;hrNE6Ypr6n3`m4Ade>*)9EZw|;EP?SX}E`xR*O>?8X z7c=-V(!|)NSw5jJRomisfJ9!S=EtqtCjYUXYQOwt{p|sHpWc@3@}%x*Yopw)zumX^ zG*;2(eR3bN^Wrr(E#e{(R{qu|xeX-9KS41u%D2ScwnjOc_0rlFtJNHNalm~1=D;G3 zOXR;p8)AF42oX~_55cjw%AaDU+D)7M`>4igcrFh^Y|(;*t4CGW2j?4FJc$W~I@fDk z#Q7BY0hTY{>r^~0SGOWL6wG4rf8e8g_^6CO(*BHPugC3^+&-_pc@aC2#hq}87Oq8> zP$^b}TEfLwB*h1H*)`y}?wRq~n`eLbbJJ8zyByc;4n^@?wU5g#mo$cr#_XNXuYQ*5 zeKn|KjB7PRWsEdf`@@Cf9%YJ|da((i(R*ccYwT^?BA^vVvOkvOz#=r6aU|_AB+x+p z8z7NO5&6{-RiI(}D4wrnUo2l!>}_+*?Y=hoH&j>B+T7Mw`3~L|`J`*U9)Juw8+)_- z7Pu(c`t~>Czwid;exmB&Jq}>1c_&=tm3(JLUzMD`(~c=AoTo@$c~(-BMc;LoBu+|_ zPra4N~n}*f$kZf z$0kin4j)i!m0Q7nPQfl6{%&n=q~}t$Q~{J$S+6{XzCXq_a^PF>xPWu!m7v@PgBON*i8qqFQOk%R#(! z;-w2OFW}`cUb^wpgO^^s9Kj3rsmd|D;G3dy0x#GADg$^qjh8cU0dV_aTp}qV-fV=@ zsEyk--zmvG6TxDApET~6ap|9Ecp;-W=@|1|4d;n7?n{zewFedqZ0VZ}uLXIhIJej3@k-$w(E)z{B1%BOdpxeO@M?&hm(XRv0kztoHuoYbEetOv5x;$gNC6RYkU@k6I3yg%BUDL9%@#xYqfiaJCKuU0Fk%>#-};By%-ui^!l zPARy4N%6o%&FYlwmZxT>)M}YKs0L`@Q;usaS-Ff13SRJopW?&I3|zv6gIISX4UcjK zlky{GghoZViV4EGNM9E&QlN!^aj?tblZNH6;BY#nX`eI}UIn$(4HYR)>ehrN4Q0o$LH<2j%`;>UO2}qxyBWIWeVqvV*`{U z16?>5uluy1$(sj{E$S~k?2H|&pK(ilihM;H>4UZgYch<}70;_9XS`#M3H$JI^$v9D zlY9=@<&FK@(&uG2)PVZw>#C>COhajR(0_lr9!!nTaUA14u-jyM)AU5x8WL*!Ld|^b z3qj$qdTaFy)>|$2HwJ}z-Q&KXaOCNx9idIT7dGvluWS!&>bRNnPW}_2@ZJ2k^FzW8 zzpSAOsH@4tS(XD)lL_tA#H&O*?B!@4oic*S>b+W)9GXgi^my`o1+NY>#r5 z`GvCgTY`cmnqu(_mib*rfiZiXZhx>nP_jRf zY-O%#PI=T6uyzH77hjLelorp=GD1G{^JLSM3M8L#StP*Ey%^3YA@t9pV$ zZ(QQ78$qE}m!>kW4+<4ew^xR?H!W;$nrr#!(1(Wt+q+T6GL;nlJ|sk3@AW&cL&aME zNPKJ!R7I*sNYF^C{X+G8O&`=!9Ums%-x(CzmuGtKqn>~jF-&Fy%^{YW9-EK5ZTH%5 zw};9P_{$GK?x2xIorWuDZe|Nuu}=M3p9IzL|2z9Er&?R*oV;4=Dq!-O`t);_Mfn_5 z%V*m-Evv{Yp%3;%DRmfLwO#xSwSefiGTxA3)BUSZaXF5>5~x1!`t&|tAJ~mOZL;nt z{UO4#YvHrjXL-Yu4Ue3Un*vq+LE&iJNF+1ElV%uoeui3`?k3a@E+O}u;83llmKUFKG^)Q^g(H;=7_(B z&3UTKY6A_kD9!%z=1_Tuzq}(T9E@_R1A%psNiUUNbsd8R+VcqeN+(eDoVZMo@P4lP(I8qtHEm5$*uShfdS{@5 zA)`*k*ZGCI`!HACf(BMo&~WpWY~oo>!WsDc@D-skhUk_$pi}Ob&tCrdw<*;XgM1c2Gzo9B&~j-b-v9O6hvL;8mc+wuQ?Vd zIi6?=`|{i_a5HPbYWNW($rSc{#j&7p9807Ur9NWA>HGT?q1xmA+T($e6N!~nTSslO zH<}GG7s7ibOHIhqx?pLY8~bSL!>ND;zPD%}b3y{n1&4%2ztA|hZmxUo@}pfrp;I-a z_GFJ=*fYOZozkbf>}2mIY@Qo!a0_s*epmxgWAeW;5Ky0qxLznZ35U*io`clfKZZBu<&bCZX459$IX2V$Ir1J0VT zxr&eKKCBCDI~?DNse;u?zI;^G)@(Q^Yz0MQTQlve#;*|Vq(HtLv= z-(S`LsIJfz=w#2t*>_=1ayv*BnNu_*P^q?px zw58!8F^vVbiaKp$QKuV$<=5C`Ia0nyHCd@>hNHRKg;MC#A0B#eC{WrSGn$JXIRz>Y zYCTQNNG>w0ihIb%X1cO>eb^P)4&y~Olv?uDwdYZm`U$DZ=*#f4sj_O+18BF(?I#~nu{N>1B^ivq&sUJ(y|5C1vv(gyhArQ?A+u=b%f%3hf^8JDG{f}}&2hRHsoDY=0h?(FjoseKz5G)}f^5!t|k4E5q>hfdj zV`rca*SY9O>it6fd_!DfgI{R)!@Q4Hf4Dl-*t5_WedExiYAk))N(ODbuf1=CtlfTf z4*TcoC}}|`o8MZm4SckUzjHy@84{`%@QvkQsJdsNn!QJ;Z*lzb{hUyB`$BblQ0M?% zlQBOP;N7tdvI+@x3qsv|efLj=9vEDjOiow~8~x$xQ(TSu$uA)FJ=2BgGh9fCKX=w} zmJKQ5ez>(k`x$ILA~*hs(W7i(<4=}tHIv0dF(*Ren#r{%ldN z$2~49QyR%^J*)+_zyf`XcP0|K>TvrkK99wTD@s8yY}}dgS-g%bvU}o%(=}@qTv4d} z7F@!s&PNt4;%Z85LEw<=gC&`7A<1ndF~1~v!`Y`j;bI_(EcetFt%h@@Ym(Ehu6I>_ zhz$P>FKA)~H`vsem469AxFAwS`>5v{uA1~t!{#8#i%t#88kIjo`aeO!@6 zg(n3YzWv&F-}uHGfr82>g~jile*1K&u-so*9xB}9FWeK{`$A}MzkhH4PYaL!ys%jL zbI{cZYu%$i+=r)D`5zRhRX+NF+$)I|B}d{y{s)>LQ-7lmJGMr#Ql=!LAT2t zaRT5rX}i3R^wd5GMd`lgQXg$~e6& z8QVx1XO|^oJ1N7qEEzjU8AHpGv6GZ>Zdo!cqzv5t7WYen%PvyJ%W(qW*7k*^X_0ow zx>N?7&`^F_eVLiv2*)(k!KTg`rw`w;wEcdw+iy8O14ug~1tk!IOR0!#Trl8TO1J#q2wslfEfeSLdYU7;EV29QBM{@9LcH z>hw-K+!inHj6Gzv;2yI&*bXRl>}#C%9kg~$bv7VDaxx3siCgEu9ituInQ2J@=4Qs+ z0Qod%IX0+TgtISA%&yVO$qE)0y5VhB-EJzt8>|!d*-6SIAQH|MeUjvwEs3?VW=Fiv z79V#{!yD{b2X5R1E-xSwMdQ3jx#U$16wdF*qcYU}ma}U`)RLn%TNEQcTy#qIIwhCO z;g&oz-q|(%vfJTyA^>RX*^RvpWz^%g9KjPQV9&0WS{k3)>+u5q$grhH!F`P?azlz^ zd|Y;F4>q52z=mQ8rE5=N->ySeW;ere7teC*y)HgOX964>ZSSNkH>5hhm zt5>hqUu~}UC=(4swg%gg-a5pkrLn>5Q)H*Fe#|#!?aJ4=OjjpZ)?smjfe%?P1I;Av z*0x$;BO)FdaLBsPY5})W9m(PY7d>P}f(ED83xcNiT3oW_HJH7?13|!}oKgqQSkzrr z9McCaH}b(#&;`Lw&_6ZzjP=TK$CT{4-cfZ*w7?c_7r@|}uryU2)cNdz`leR!S}oud;`3P)&6_04akqCGhH&s7Yj~eOShVqkrP474 zU9C-;bd1U_Svz$?Z3Ldj62Y(|wm*FS3ehJ`!I3bkUun=p$9vCb$HE4v&u7OnQ4ayqK9=FhJKAwO zc+?Ocw}Z!y@FRD~b9I`L38xWLJ|qX{PU4To!8r)^R2=o-810CcQA``hOTyDtF5^=L zFJ8R(@Pa3%XlJD0@iWKZr6KYaPdlX~-ySW0?DxAzz!$Da@cl04NR_Af0q-e%)_T+Q zbko+*roH}6dqbPr{F~Zt!l58=st7K~eb6!Ac5?odGq7hYC`kCJU0th-tKx6x&+j=r z-*YiwaRh}?d=y;=`@L7^Yq}n<3G6r%6waz^3HV73w+0{N&o>|YaaEuiSLCWs`BK5# z9rN|Q^G99|l*8)lk)=ZC55E-Hfs65|N4CHYF(?czee^g#u;XM<7$BbH1$Jm(8<(h` z@CmF>?amT$?dZ-Sb=BuEuKJ8GeNJG<5$*gF{TiA``Gy1K=YqodrEL20u??u|f~Sbp zY6I1<WZOeatn9({cWALW`wV#>9DYgC{LL@h~Cu2L8k?L)lI5ZOMIc=~KOA zzC{0a?R;I&eBUbpt34=OTndQZxI{581jQ8XK5-5)thj{pfKskKXw^Id~Kf)iz`RrpM zu;X}8I6<*}Y%1>0dKoHaam5Am-L2&)AF$FY9|n`Ra5H+VQCG_4f?$5%A4?-$f6s&HesybQjTz* zM?KU~6|MXgrkK6gy>NJ$e)N$86IoZnW;kRQj{Vc7p|yTFdq!5v3@3`gTA6T;bWQdt z^B_g}uXw>VC~7goLZ@cJF6Hka4L?$O-+{}==B%u&zuj!g+T%CX{>)VMGt=HrbB+sH zBj)$deQHu8AHP^^$|<}#8!(st`&>1gIeA|>@z#m?wbo!x`IDS2p`300oNd9J9ZzyredXAfj@=aB Wed+C&zINfKIi(-3HRY74+WtSM6ZonC diff --git a/__pycache__/ProjectDetails_SubStructure_Window.py b/__pycache__/ProjectDetails_SubStructure_Window.py deleted file mode 100644 index bdad583..0000000 --- a/__pycache__/ProjectDetails_SubStructure_Window.py +++ /dev/null @@ -1,546 +0,0 @@ -# -*- 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 - -class Ui_SubStructure_Dialog(object): - def setupUi(self, SubStructure_Dialog): - SubStructure_Dialog.setObjectName("SubStructure_Dialog") - SubStructure_Dialog.resize(1440, 1000) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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(SubStructure_Dialog.accept) # type: ignore# type: ignore - self.buttonBox.rejected.connect(self.show_warning) # type: ignore - self.buttonBox.rejected.connect(SubStructure_Dialog.reject) - QtCore.QMetaObject.connectSlotsByName(SubStructure_Dialog) - - def show_warning(self): - """ - 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: - self.buttonBox.rejected.connect(SubStructure_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", "Componenets:")) - 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", "Componenets:")) - 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

")) - - -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/__pycache__/ProjectDetails_SuperStructure_Window.cpython-312.pyc b/__pycache__/ProjectDetails_SuperStructure_Window.cpython-312.pyc deleted file mode 100644 index c37cd94fe05c1c252dee2a3cfef87f6807ee4131..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52197 zcmeHw32+?Once^cfK3n}DP9724hJAE5(feBfk<-B}LDyAIJIX89Z=S-n{!*}d5#&R%C0hGqM5Msj;|vmDuuLk`jTxaDc0*!91{$_rD7=P(q z3NaQLZ_A`*-p%HCx?ft#IA%GdrQVX+F&GET5y(_JJBC!?EuS6A=s5rJyz#usMKc(+ z(izL^R9gdz6wB>Y4~@yO#hvOISv)VP!OE=IlFnmNFz7iiwFIsVnpwwl@Az^fgud&L z9KBhhqc>a3dfn0M6tm%(gF5fc9nY!EjTN1e`@6?RC8b+c#s<|fMe1*pJ-)#C!E6&# z0sM&1m%kiEo|9Q#mWnBon8ot}G2(cdA^`HD^TRmbEabxM#S}@*;at>3?1L92Ncv40 z=}kKN4Nj8|uV$Z#k-de9@A#%JV`1Lq%*S0ZSIiR^iNc3DdYDP7l`IxzlMK_V*^}Ac zr&&zyin*Lq3R#PJZ04KHwxlvNz-Sx}aj_-UIv$((#C%IC3jHOV%7oi`#+ADXxEUCL zF3@0;el~C`0OShAr3;E%wwI_U#ARFqfIU%9h|5he==@q^PibjOlG@-^UCP3|Wz5H2 zFVwHeKTFxRCR13ZvTZhnWhxeStn$)VLvW7=U3E@6z1wGhH&NHm{whAjq*GV0uU;Y* zZzKE4T?>kxJSOw877uDd+7|Y(nB1LoG6_>sJ;P%&pSZ%3iYn17IhCnIH#4r>O(@Zu zSUBWrAMbYWZ?QE@>Y@Fyb&9i-?WVNoNC@M<7>zIbJ947s?aJ?`ejTxf`{klyt+nArb zHuSq5^t(aaxWM|QWVO~WMMYWtCZ-{+wxFmeR9u!+hqz{$&)jP;VM(?BR%NA>@dVTl zGe382V2OQ#4dUk6Sf)t9T9=eulrG&|mnN;)sX?|d4S>u?nV-8hv{EFNh@}f&gHWA?JW}Y9LU9YHdPo#2m?V(;N#^IS4HU}&#q|r0A|YNi2@xtIC5YfCHjDYZhS zV67F3iqc90sg*vS8|H(&67vR(8pe9HrkF1>KX+|tr4ejphgdXwe2~$xG$pHbiKnQn zQK6_PRCY4GCXB%Cpi;*W0Q_EJe(u^rrT!71Ldj~43Poj&3PnYsvWrB;&ZyCt0|itD zI2z1n1C`xSR`x7-S)pXLMunoXMunove49$8LSf&OG3@IZ%7DGd{M@yLef=ZAo|4rX zdy2{$dx|Ra*i)%c*gr#JZ>KhHU^oNzL(I=z8`w8Lk~Wf()f#(>${Kr$ib7>?#!zWw z2mmU>%+Fn0s62``l9JUL6^hCl6^e>NWnacn*})J1RJ_d3T^p!83uR^hg13>Btk$Sd zRMx0aR1_))GKR`dh5(@AV}9=1LZ$u@phC%NjS59&jS59Yq0&O4VrOk;7efG0@iRYn zZJ^RBwxwK~p;EBc3PnX}lAqRM=qiAse+@esoyVFcU4wam9(x2MSV~rF6e%j}5=l{I9(yVk3j5<3!@ilJ4A@^`e(u`9{sffZ zg?x|uqe!Jfq1Z#B*vE9wUBJe`iH9<37~4F{ zLLuhYn4h~gP&@@~@WQXQQLNvm<093xt{M@yH%GpO!<599&qe4+-zQ&_cp-}84 zQC!cp&wQ3OUW+N_>&(wx8z`O=`%=zvQ7Ks4Hbq5gg zL52XJ@@?klt}Rp^MW2b1)fyFw${H1libCZj5*0i3dOJe^Pxc==W{r=dKO?o`Lbx!uQlES*`U;QCV9(MMa@9NTOo&*SJi+#wg=FhvxHo_NYvA`*sjG%4C7m-(i04+Ct!1 zN(87BtP!B7CNO<(hOm}0)i{M@yHK(2UR92Vu-`=~({ zDuv(YsWG3$iN5z)Jnkmwo=W9MJTCL8dCcb}H0Qm}zH-+xm4CqFGN0(Rr22;}E_dfZ zb(6!VW0y!BD3WZwFyRIII! zqO!I=iYoKgN2Nkp-w0`aA91T;KFhlAj49@inV-8htj{l=5d-2VS@%&Xe8N*>K8w2V zAG3Je<@$geQ>pw@9+&w{H3gwL?>}SlxNAXihsR{TiA4^FrEC6tOUq(-tQL|2pEEyqZDC(4j*FACu}_f#rTfGhfs%{T%2k%H1g(6=bHjYJB#augBAH_T8|LS( z_uq0q^FfK${;_;(_s>wYl;mGyX(vd&p7G|crR3*X+>DmAf5*6T*K1MIOzGEfD&}Lg z>(!je92>Wmy~6ZmaR*r=OOLx26gzoL<}=+|W=Gan7fjY^CLwoCvO2sJYI}HG=9@I# zKMD3lQH5Dt?k2Ur6xHWEHuKGaiYgzkF&(BXRZ*4#cj>%vPNv=#U2_uk4x@%T!pBmA zbnY`hcgg!gzs~3r%6W`aF(1v7QA6r}Q_Kg<&s{S0Z!r3V)ahD=$PuH4)B`LfQz>7? z!nkXc^10j$O5ZSPzQ>QgVe)k*mu4G(##3j$4AoS*lete&P37=XnxIO5;_a*y6;&dC zCqe6!1^zkHKX=73)3}PTdhdVYahVTF0wEKx=S-#8jwLHBoyX3S{|l3hyH=JAx}vD8 zQKzUV)W2y9^>5iioscPGs0W!&A3N0lf=R~RIiOBaS))!-QK(V!-gLtSM$ zee6*GOC}k2=YTpzWsN#TMWOzNE!4kl3w1&!jylw8)HsUn@ulgv%%RuwbFP2pV_JY- z&ln5Kp#3GgPtj ziDM*+%6g1MQBjz^$u&JM+b8&QPA1HnxrUez(n*imWmC-m&ivf<{!i{_KGR2EzyR8*Nv9Jx?b6tdrA*vzv=OoQ@6w=9!P6B(@$ z|2N~x-8s~V6cwe%-?gbnf?xt~{UPw?lQ z9#W5AV>!*J9{(@n%H28Wk)opX_zutgy!1%$=bTJ>6u8vP2kE3=S$oA4^ZzkFcWu=D zW8&Y1)s=-_S)*jNuD2;FYIP@ZWsRcB{K^`Y3S~tG6^Q^f~5S>3&P?e1p+wlIO8E?%L4K zDd-u=;tx}3hf0Ie&hPPDrj%{ods#qBL=7^Blqej2-%LmD|M+vRN9Ie9h-Dwf{I|z` zhx3Gd<+C^L+Q2UN5&RV^EQge|mUUYSx-C4%%xAHqse;Aht{KY|=@>VDb1I8;w(*$E zH-~hn(vVPFr;v~8ohSU=rWAC>Z6{1d_zQ046`xdiQDx%&r2G|fp3KL0P-%VtAG2`o zf}BhF8}mWvQ_RoZ$t(z8!QYwBEC;ni4XB8^AxJhqzrn~3stFbVlDoesxD(ST?0!g(by<`=661HORLRMLI`|IJ{Dnkm7@ zSMZSk3AA(*?pd*2%K3qc`o_9#^$ptpl`%&w*XJ3KeDaHc=gd!$0T#rbndjg+@lI<~ z->IOa1p9&>k3ZOVV05(2qk8%}0)rl3-$^BKUh#|s`%aFl!vTNXY1m%Z*C7w|1!YyL z9tC;fuSEv?E~-2FJ)@&OdC;TE0btds_6^AXzM7iaH2<{)F8h4}j~J|NlY=92FjzC{ zKi?iJ7##}^w~VQ3z~8@9&IirNqnP31*%>Yw8XDR&v?nH9*8YZNfBg*NK64HqtKkt7 zwCwlS*Uh|%VgCXj2zDxFz%Q0R7#JA{z@NbE-!=0zGBFFZYo;A}+`w?ge7jr@@)@u7 zGq`bY#&3?&AnyVMj3|xr$L7}^GaHdYL<*kz?wPMA2JD%+htWDQbcs*)O9w?+?cd&D zerc3@5Nf=t)ktlp;oBwuF{W!G-eG>(DK|kzW~8J9cA4YsmbYO7pJ5!cVs^_%%~{$# z^8<(>FV&JW6uP}m{)su(9{HQ5*M>TIyGfA^+vQ>N*LwMY`L$u@L6V$%8t{vp}tlSHgJCjQ{@mCseNy%J?iu%P-zL)|-L z1w#R4gsFO_9-%Q?Tm6p2*PSzfR#KE*iBWdXK-HNPWlutshPs(wKoq&yjQpONPm{?S zwkML+C%z_?i+cGR8b7`2ZP+FM0lpTh(iOD@5Q7G%zg~U~ER?K-+w1UOY$db4Zq2@1 zJU|r7=f$t`yRpKcq@Eb?N`vZg&xjPuQ>36gA!)(g>bOtp9+o6k!44Pv(c=zm29;&_ zvIOW3Nr4ggIId_a%I)j|E((sZ;O`>0LotaJK%|oa*{^mZ&qeU21*4}2G6H`p`J~ZG zDJzi>KeYsWVr(&Z&Pek4VKuf?qZ=Fxo(w?SI3COH1WNd;$N^~Q0uUTq0TFc2+hhe~ zfW|Ynqzh?#0v*zj3gR3Ygs{aPQ9LPM8S#w97InrwdB?$GJieIo1T%-$H%8imk1~9ecM=1YSrP` zGT?Vi7I^?dprr?7qwtru59250G^V}0Q~yiYdiMsZa^-mpMmss7$e>}Z{}jtV98ly5 zutR_sO{81(_(hK*>SV!KZmTa4lwvvEo=Xy#qFzR{HlP>Ttp*abfp(}HTZ}%X#!0t^ zE-WgZ0iUGwL42hjA1@*0lFrj0lx~rV2SYG<)^Hs2DCcE=P#M4v*(Y>Gv<^$q3j|(T zE-=}+CN-mtTi>A0yxpkd6zuFMLl~@;7p?ymTdWn+t^f#z+iVpXA6|U;@G*i9+!?1O zbxiilBV!|40nP2y9Mt16&lMJ|Ttor|A3=Pm_!xsnte_K1alATGE@4o9T#ZoGD7Y3E z6Hdn)K;;U)7AjC-KsVXvQKeIIOz;d2N~5YI#uh;V^+VZ><>^5&L6NXlf=>m(VQ8W* zdlbJ6bwB=jb=9L+S+Nyys%~FE4YrJ96;_!S%Lz)pAqBhpv78~ShjL_y7t6)xcHOBD z;w+0x`~CWq(d3N`2gio{PWYuZMZP5UwMoGXYGAbQL{Rjc?>}{dWNjdZ z>)jRMvbKnDFs)z(Zxp=U6A{*8YD?c(`nIC`8PClj zVe|FUh)`}yry?X&e6;4{;@id1%6*~AeG%bV)7NbwVcSQiKR$c=Y_z&1RNWF0T1{We zLqhp{f0>s4>OU!j?vaT@Q*jM~f;$MU~T4 z&xVWkgE!&Q_7wCup8B#sJkRn)1=xE5;nXiMuZX~&9x=b zwbh}u)wjftM{bXV*P?kZ!+6WySQZsf+z;AscHHQgS~)dvr{s+vX2W-|V~5H&rqP1@Ufncng;5bfzB_bXu*!Ton<@O#I41LfLe=(HiLyG4T4; zh_K_q<0cO_ICH9}g3tk3apxiIo@kyjUUmIViTJpo`lQQ)UCJ$O7G26mSPB`wFCboQH8S^rjL^BS5xA}gD3$$Z?pObc97Ja)ChBWOIHbdz}8Omv~;*-#eU zP#@Y*Kh^l@-cR<1HyprG4^F*B1)TPa3OFG7;L^>>82qY+mEa z1r}w^rs$fo(3-OA>dmV+u7=m_fN8%aAoN>H-dGY9a2D=^yqg6#3T}15#NQxHHM-(6 zyLtrktna~4C7ZL>h_!AK%yTt_N*baiO`(#ei13WTTGP-|@wrGXzGgZwErk zD#ZJo%{Q8-obgGUlK8xZ9w9!@FW-=(Wlf z=yOu&IVt*_Hw6Dxc_YFFBQ0$OgI@E*uj4*Q1 z=5(t=LiH^vA~YHy#Jo~fNT`Z0wZzGcso)1y4biG+LRHU%tF(C_eX#*&a-%{+NND)O z_D?%L>A3sU-2-=npS6Y?Pez1JNGdAmQFerc9n(9zB0{&R_KQ{?4pklw7af7DkdyTn zr#6Fj-C?)o+D(cHUVY-^a6~wQ+14V>eYk*&3Aak4RYyWqN5Vx%Qwyn83i>Rxkqj|q z!z*J~Wz@AJow?TZ?gU-W0VWdQ4GA`yo7=1Xm1nx+l02j^D->(Bqj2^mdIKBe9rk2 z(wuV+Aq_g(+lc-)f>8oa6$1lKjV(Ib+f|Gba9-6C`I^3pq_@>tMt8unoTfoNOj|Jm zeOH^put?r2g%O1*3@cNuy*6oIr;S!Lg(|Q}YqqSL$;Z{VtHVXR6O4odZrRV&7;CE^ z^GEe9Tpv6Y5!QjAiR*(VCH&2xZFHI_AQBafOR@UO*7XA)j7Q6xL*>oU^7c@9`)4`P z!@Z%yy%FKug9X%%3Hskk%X_}*>XSKJUgmW>t5AMic)Ktn?6hGavuZ_?w23yVSI)_m z!2E48U5b?LQB~GlBx6hBw@Rt%DPj3W32$MY6s?&|`;k{{bly!0G>w*8?qq4*_;RZ? zqJ=RlGkCu##xfTppdm_Etkaq2TvFt7t#jz%X)A~Jm+`Bs-@xH2I$~N8@oe| z-Qf*Arjh==q>=ssSl?{DyE0sEtQ@S>qM$m=BYh)KAL*BDiAT$rYrhxoy6+B#t6|&8bEYUYA)#ivHYu<+B-H+4-lvN{ zSsblvxmRbbn(AD2k~Vz?LLUL>J8Git10ikiORCYtw-fi5=%a~&a4GH(NoO=~Eknx0 z{UsO@uK>nx#M|_UuotqX$Hwu-M>{{>b9+y8>;BMI+%{o+(}ySVOs8V54NqWHvn_#4 z+q?2hZN>)hm|j;0gC~rjjlMSB6E?x9U%={_>{~`mRGYzI_;vay4r*%Zo9ReyZ_K z9n<6oiGX^M5!LXB#<4#*GL_|BYQV!)LEMuI3BYNoG1V3HT!KBH&mONVRB*qh@-{qTi_XNig5pM1 zeY<6ktio>BuVa)CFpBf66pUpb55$&3l=x0secNv=SGpn%_Uk*^3p(Mn7CUbh%egov ztCddWkC4NXcwYMl0#|S^YH$=zWJy6ZTu_Sgb4&^kLQQN*y}8r0ZS~Kf90Y$4;=k(n zN^m^w{{F}{nA2Jr71o7>b?-kHU0)koU#so!gfOl91N*sP?^!&?^m^QisJ&`+$;&ri zx$(+R*ZpkcPd0|i4@HE-+9$2Qa_;7fH(rD}jxX{Yt2RYfmWEcAhF6wd6Yeir`OeGl zzVf|S?ky>MP_Q~$;0_hIqXp%mg7VJ`Dt}q9T=@*d8_b3W`(ZiL%JTbATA(bma0huv z97}%E?`k$(TC*LK*pCH>OySWNQ3gAYl+G(7T29&3{r zz-t;FOGqAg;3DalhDQO(1CMDW{nGF#Bzd@!7{F^99!p6c?s@T8M)D|~7Z2J#VDowM zFRisNHzYAzJG4B>zcd=5k->8?Nxw8aXsvEbVgRpcc+l2fGcO*rh1AW92W{6g}0pCT=Fu3T2cYxk8Tm*ylv z;5Dr*566YcLgxt2d?o$T=ysKnA9G8yKZyanrs1)g6bJ(~wyj8Sr2 zKkk;NAB>z>tKE`O4TogM22~tl@!v!N99)ILjO3TpV3T&-J(kl3rw=DqZgm|HMOXLO zKsAe^PCR`KPUP>KniV=#!5)$v%) zDZdP-?l8v0o6W=Oh;Lu>u;dYI_cad$#PNO2qx+hJqaMF2h)0a~x?OlmuNqEMN=@7A zM%8EB`$n2;F+%!iE_S@R+Jnbzn}TDbk^;mHjJOuksnt`g)uf1JUpPOpRL>{NSS+s< z4_*di0*p}y;e4W`3pb8v zvmRQU9NHQPs`{s=TNFI{siD)Oc!q}LLH)y;V{l**t}6iU`d2v6?2(m;HE>tRXdnm) zBA{??dwkG`hb!|=jH!@Y&lMPOHE(V^(b{wN z+j`pc4~;c-wY3M2yIt;KRUK`rt-XBta?Rz2nt*b?wx_GM>tJg&>b|kAHmEA{pjsoU zqI+My$t?CYgJwN0KPY&w`y#{{#$)bo7aZ5bs}}aUx4T_nI-0S#R4~K6Zj4Yn7z_fV z(QPiD?0OkyBk(pAu$n=s31=XxFDstWXIxkF!5Yxaz!cC1brVdqydlqs>>FR1#^pvkWtB?kk{Zyq%g7}1XVR>LvU zrn=E9&$#060|B_2Wh5cU2S?Qt0u(&Lu7^}#RI3Mkfx!#aF!cgDX@Znd2gWO~c3YH| z_-!~I&+dWU0}%TN6jHP~Fnd4-Dhf0;G%8c^dBSp{mm7(dnnVtxFA5m)?%5tqKkr4QjM zYxUcfmc#u+iez5CT5MdbqWl8JFWP-d`jQyV+v~R!;jK-0OA+4N!fz@fuOq@~NBzDb zoGP2k-9r3EA)KzzuMols1MT)8{d$}Md>z6^%A08};#UP9L44q%lXlyTe!mRf&~z9^ z6WVPu@wGaA=$qeft9OQ0 z@4N;VtH7pI+^KN8cY61U>CUs^YTS+oi|XtMCGIY`Q8iuNHr)8(BA<*0!LY!7ZrCc`prb)E6r$ zm7NZ|&qRdhXOo%p#}4gk8^Swp=TA2r{p@hKB1INcXM{_VB;#~TN{!7ekEzq-3MUMP z=ia>Y=tk3Y&B5tIec_T9BEpNaRb#RyN;5~p6>*hPUk`;V;6|NOz;*s#Ip;L`BXaUW zxC9ob`?WOr&Ri{Spuhj(n8&AoQT`Ryzqrvs!Fu$zLnGF=KL{9#54;dCh&RQ)?)X)9$D-VWS-_&->JF_n(=H9vw`45SLTfr?8J!WNLOT4 z`S(Dg%;4iQeEb|9zZRPH%nHYoc+$Z&vS~uSmwxCs|o!70h8cD_5c6? diff --git a/__pycache__/ProjectDetails_SuperStructure_Window.cpython-313.pyc b/__pycache__/ProjectDetails_SuperStructure_Window.cpython-313.pyc deleted file mode 100644 index 788e6bdddda97f899484e7576a9e30d17cc462f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52921 zcmeHw32+-{b|y%WRFfhlS(izj5J>Toc$1PyQL=c5qA2PhniMUIHVlGIvMmsx8=xf8 z*4X1oX5<-XJcnn-nK(O=*C|OU*O*SSYb7<6QKnK^Z#LN?2)LldDyd|5Yb#7`?Rv+a z&0e*&?{(wH-vA%bAPh0mWFWi$zq{Xi|NGzf{-ghkpA{7qnBe#O$4e)F@tVo>AMu9$ zawCz?+OkZh@0moCn03n3m!)L&Wh>czX2op5XW6H6`f`cUTHHqeJCNXCuuf?qK zDr-grF}Lk$lgTk>Lh8Iz`AYtX*|wP_!b7WRB;U4`-?LZ?q>ewO5csxfasAb5YKhz` z>qge-?$(d2x9!nWYVnbEOrpg!vd&hzoPx=N_xZN68GOEf9jVwPeSv$vU)9JINTE9Ss8Ps~-;V2a`5A=xg@OiPOBQ)ZmL8AY;p%MO=k zVre~yac1jM5Fn0leRfpA1ctI~EJ+=an8h;%6cKoJ1mFmwxg{dkf>N0K=!nD|F6Ck# zdtpNj;(k_;z-Wg|uBfablTCHlSaMrY>^yxTlvwlyrL(D64e ze+%XR38tZrl1Rzcyqw#B-K(|k1ei=WsSr%pm zNdFj%^Dr&T6hp07B5qr58J$OxU8@zgS(LuUH5SE1S$R9N6126v7tqOV!(!lTMpm{m zTA=J^aUP~+beV=Gf!R0-J4t(?F%6gCz^t`pq4ET->I=n$oQ21baM zF{z++FN^aqEkv=muv6T#9MJ@67~7N5jIyVd+f!$m^lFs7%nG1&AB*!aEz1;(rDEAC z)F_m`#=cH*o#r`aF@xU88AiuLj!FVsQdsWgTwfT=N~Q~>e~!g@m=>1hfaQ)=z>?C} z7)y#P^H@@;P*_%E5X<8(?W;aUQ0HWhG!)wF+2L`WjI`C8&2R%O z2U(nlX<@l7!h1sh6VV{9lcV{9lc3Y&u@HtBt5Mn_Wtn^BGl zi=~ClAt*Pkt59wzeT}i9xQwx(xH8|`QmIfL(3U|DXk=J}2aK^e57YF3##QkEN?&6S zpty`Zfa1!$2T-X{9&niSfb`nVCWbb6z&MNZFf9*gU!}H_(%0AnC@y0UptvY(j${y< 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 SzH#xBoRUx1n{u|Ps{ViP#;qU# diff --git a/__pycache__/ProjectDetails_SuperStructure_Window.py b/__pycache__/ProjectDetails_SuperStructure_Window.py deleted file mode 100644 index 22ec97b..0000000 --- a/__pycache__/ProjectDetails_SuperStructure_Window.py +++ /dev/null @@ -1,566 +0,0 @@ -# -*- 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 - - -class Ui_SuperStructure_Dialog(object): - def setupUi(self, SuperStructure_Dialog): - SuperStructure_Dialog.setObjectName("SuperStructure_Dialog") - SuperStructure_Dialog.resize(1440, 1000) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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(SuperStructure_Dialog.accept) # type: ignore - self.buttonBox.rejected.connect(self.show_warning) # type: ignore - QtCore.QMetaObject.connectSlotsByName(SuperStructure_Dialog) - - def show_warning(self): - """ - 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: - QtWidgets.QApplication.quit() # 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", "Componenets:")) - 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", "Componenets:")) - 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 ")) - - -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_()) diff --git a/__pycache__/README.md b/__pycache__/README.md deleted file mode 100644 index 89a73e9..0000000 --- a/__pycache__/README.md +++ /dev/null @@ -1 +0,0 @@ -# Osdag_UI \ No newline at end of file diff --git a/__pycache__/Warning_Window.cpython-312.pyc b/__pycache__/Warning_Window.cpython-312.pyc deleted file mode 100644 index b69865239e2d005513ea04bd0f8e300a4aa05128..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3407 zcmbVOO>7fK6rQ!$_QW_21Wa%si5tj|4WXq#iqeFNBx)#10&a*}-Uw2qCK3{$aNx+XJ-3H!ZM5k&he|!=h8m=Qb((uH}l^6 z=Dm6I-pntRm0p0`^MhRzKYIZ9iwtOU6^Zp@3;>S+4|paCQcR3VIbx2KGv;J~1M~sj zaToAT#4b54`Irm2dGe={NSJoAFuMyF( z({M-C9!W-qE2{Qo1fwFVUJ>|lq$-JWtfISM{Q#bQ1Q3WZJcv1X<}Qdi$@a$BtTW6S zdxnMh6;4hI>G624z$K;egrf`xVw%KYeU>FKIYy+wWkNh-+lnQMF)I_m@#LLLMO+4` z^b`QmT`pzOxe{6q#PO=dSs)TyHZ^hArU^V$E9so*r^x3syWg<>cIY43vXpuc`p1-S z_cnT2-kh@eloCIol--wmo(18mLBqeXSP9?a9bJ2bHP*<1TI2Cg7$$QeOc@np8C8|i z-O}_L$;vfyF$ryi-9?6^J{YbfH-?hoLq?^7)Qe*xN~nWe3K?!0DZ&hrNwG3^N3G)6 zP4Q$?cNbRaLsANv#A5ytA z&&j-nsTiJKK}q1T!47d#c>QQUF-+czVP#SaF!twI+wfuCOOlXQhp>5u2an9@N*P+Tq1!cRm2;fgp^Jbqukb%0cAwI#4PM1p^RP80i<%4^G0=1ekduaN_UoQ z2EKM!1tqV`6~qqqIyOTg?uJ64cc1lij1DVERz_13N@im8VjA_z!W0_qMam6TnjF2T z@Z5NO_=~m_C!|Md{H_RTUb=aFGHZH}nH&~of?MvQXu-zQiX|QfSv2d~gTULXOdhnzcJo$1kAMBZ9zw@s^-$Va>zYZHU*!ZOVY3E|+ zOUKKeS9@QLN4>D43sTBQHi z5q)4>8yMFIk{bRZ$pTE3#EF3mR}1jOYM@>ZG--jRg>*h}8r$O~GT!@M9oB2G{)ed_ zbB}Y+x=o9!^9`K^*tJ@<@8PZcw;tS{W3j9bgBlFZHx^)BNwZdiwezP6FjSfeX)v@< zQ-I+=iH6BPTYwS5H+^r=V1o`@G}!XYueWw-tz89p_P@ES5bqg5(qWqh+n%Lg!>)B4 z2e9FRKrV3ef$#zokAO-9%qEsUBj{Y|N-Iv_4wqm?#MjW~<^VbTTHzh^*4A1F&_0Jx7)}v#m>0$66X$+9lW*=Xz=6_PAu)a2 zvE1Cgd?uDZc=a_Ll|RHlgJG8ec`x!^urf8lGZxwl>z05lka-$(oRE8Z3#W;X>0W5tlnK zm~Hht*l9U=R_?`R8^M)4oK0UB_An%RL_N)Y&zaP;9&03&2A84H&{Jo+!UvX%*dxdr1yKv{i u+}8!Reub^p*&2CsRC@RY3RSbIFGFW diff --git a/__pycache__/Warning_Window.cpython-313.pyc b/__pycache__/Warning_Window.cpython-313.pyc deleted file mode 100644 index edc00450bc11ffaa9201215f3824083ad17b3ede..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3480 zcmbVOL2nz!6`m!RX*xY)LHBlkF`pAbTQ#03aFS6``aaaJmAMfp`8zw#>M09^S)ky zUyhU`+2|G%&4y_-Z=ExUN+Wsey{bk#^41k?zN}hisTHf?F4`7g_C6{i!bymPB#wmH z_7YwSLmQEJ5@q1@wSp#JQHZYTWqC?dDn@xL37Vx)3n&Jfg=G6K&oB#|=&#ZuByoY^ zAkhe6ivWaI3J*7M1vCd(-X}(VD(Ew?ijP-7m8NU~iOiZ^pLk%`1PtdA{+!4h+V#O< zgQV9(g^)N9aC}PY z47}XO-V?rT&=*3l)Y@xDlRLDs0iI6Xc zwBuUUvJ8FFxV^=4bBo+e+G(oRrE53Uk~OO=s5DAcQ(I99 zyUXaoyjLqHeLTX>Gd%z*@2SRuYLR8aDj*bV5ddhCVG&K$EydCdordxjjh{D&wqn3k zg?0m9vJ`z@A@d%lNn=x*S%NX;C1nZL&tGJQkwXAWSj__LUw}4EfbL~O(=7?iqcIq{ z)13sYP;|4RSZYC|SSgj%MN6Hhv67+d%&4e0MKMvCm%sua8AT7|XH-k^oTuFld8uMp z=HxQl47iPWQ>|PlYy&BO9n3J9yMiFliPC7XV5-C{nhGpmyripBL|ameQ%0$}06Sn7 zFB9Wt@sc^Ol;y%Ni+%*JX!^Wy>-6HX>qB)Bj83;*PN%^=#$wxT{vIN?iZ<|J2fu0K zH=hj**YSuuJZ9r#4@&E2>iEQFI^(3zJWro_GFwZZTjM`VZs5ed(R;}dl}*%TPC@xYhYA88LYXK>UW9IfM@HF;SZXTQoixhXq0<>W5f zxr_g}>dchwnX)revEk2E>Ug0k&J0|itK+vfQ+-Zq;CX6bU9Y8v!JasiiQkDkxX;FY zpD%s!(O*7#Jn350UF$zv$77pauiyLd?uVcLc8v#F2d8bEevql-V@=Io8}~kVr;df@ zj9_D7{YV{WzhN3Kf4q)!jPLs1Z{vOkpRnmtPyNO}X7?OjBY&`T> ze}TugAr3nq3+ys?9}8araUabw0rwMIpA`OFTYfm>{ZPR-v&BLA|Y6!Y$3ZVN@-_RF>eq^z&@g zOY(zx_Ar}RT&!p%FTXpQspqD?ceLNvtcvR9uY1DZS8rR{FgXQ&dh5pJrk0;ggcWUa z92(l+o(nrNoC%pz(5#}()EiFfEj#s=lNzy8BWrNNAVt_=et7fQ+Y^6(w>EgOj%S)@ zg~jyGqt6CMp1u3)+8c8(aFMVQpAETH&9!(o8>3j3bx3|$rg2$bFy^ZjMki$XH&vz5 z;B?9Ib&Z%-MblN?kY&Q|7teu|ESs$Sm1M=Thz6xtHD&p4$X&~GltrDehP~L#PH6s! z{uAnsjDL4B9QlCrBpILCvY=?XEYD`UY2@;9-Wqme;Z{bkMn?^|c)NJyr%WkqE}Jxw zZw(Z$kn$1@D~pRX)~M-TFhW($wH*rG4dWK&S^Q~4y{(pHG7eyY)fAugHe;Mz00z_~ zWPS|I%P_}r-ycTYDI4X!jrMRKa1Wy2BG$hgL40Cur4~+q%_leFhn%?ZJT9ysderl< zrxwqxp4(tWV(@utu$DTxI{VL#-de}e)yWM$`pNXi(`&!0^L-n9x5FQ?`6G2cy}`#m bIsb?AYtp@|cdmXq_kvHq>_U96YvO+a@>h#A diff --git a/__pycache__/app.cpython-312.pyc b/__pycache__/app.cpython-312.pyc deleted file mode 100644 index 57aa43aa14360fcd6549fed555998a1d87ec399e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1369 zcmZWo&u<$=6n?Wi>s=c=X;Ua^B+4cdExx!nkRU=tj!LVNf(cQpmBnbgJB`=fA7*DZ ziK84+DN<|-N)bZg$Q9)u;D6vkpjFeQs7Rc+1*H;IPt2^n*b-0LZ{C}EGw+-4&HhxW zlt9=Q&L^#X1>jE^hI~Cc(P+<&Ee%~d(SGCqv-!Tf8iFqF;%f0PnOarS za$}*>@jb`pUKFM;HI{5Iyy1mzv@RA;HR@4J#}kwWucEZF;*n9}b!WWn@aAwLDej7X zP_U2-mf|8y?ZIv2Dm#XSd$55lZ5gU+T3+go4%@7kOktn4s2K<weYuRpRC~d||wo`s5YXZJS`@pkl34OE$Uy0|Fi-dsZkLka; zEg*7tW?RrMty)pTsT-c)kr!j7ncWG^QUIGf$>o|K?H;f2kiC;#>BMMJKH*rlHI_xvN_GOLLC3c_9rQqDT{hryMy8ZSI~F*Py;}*V>8lG1 zWHAc)aw}TD>O>*C8ZOZ=VJWub)wNU=f~SSS(-hqkNhziq9+ya+A7DUV-u8ttKTz$x z_<{(f3~XJ%v5F9S3U5D!OTWX#-=Opi>*)Obcb|b6j;8>Zdz-(g=l;O6KjW7l;n_nx z`~BRbx%tDn`5);6y!g0u_CDL+_NfSZEX@3 z2`8A-%ylG-yZTggS)^J{=~S3Ot>~kln%LI8hPf9ijr2t`6^Ue9))`)BFCf{!b=w!p zLY_oQbah-?WcprPSbml>Z*F0nEbM^O$qEkEL>~_(rKNV0JQ^*2G-J2VcI)A>mBxli z$M!qnVJi2GI+x*|v}aED!b2O0xOu$)I1wrh7e$-aX(M7;>kTFq?A%dvs>xgiA`OU-fTsouV zUu3{EOfcJ%E!9@78QlMgiFwbyNtYN!Mluo+jwss+90Qjk!~YS(sZu7Bo*OJ}2vcpA zY6rX2Pos=N9vJ-oNiLR-Q{&-8*qFJ4<}#R<5HEoZ!M1B~jmWPmSzUYEc++^d@!|Aq ziGR*>QvlBa&-tpJA%jWR#5;V^+roCDqkdQJm@>Gqo5jE8CPyj#zFHZr{LB~V=CoR* H4}I2e8J^z< diff --git a/__pycache__/form_data_storage.cpython-313.pyc b/__pycache__/form_data_storage.cpython-313.pyc deleted file mode 100644 index e0a335fca1f1c99df8da927e249300915fe65f1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 847 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 diff --git a/__pycache__/main_template.cpython-312.pyc b/__pycache__/main_template.cpython-312.pyc deleted file mode 100644 index 127717c941246fc8f6387dd92c643f1cbdc43b07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20078 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 diff --git a/__pycache__/title_bar.cpython-312.pyc b/__pycache__/title_bar.cpython-312.pyc deleted file mode 100644 index 6426f355b5566c8c51e75c53ff4b1fbe3af0ed51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8565 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 BrlSA= diff --git a/main_template.py b/main_template.py index f280a68..60dc06e 100644 --- a/main_template.py +++ b/main_template.py @@ -6,6 +6,19 @@ from widgets.title_bar import CustomTitleBar from widgets.project_details_right_widget import ProjectDetailsWidget from widgets.tutorial_widget_left import TutorialWidget +from widgets.structure_works_data.foundation_widget import Foundation +from widgets.structure_works_data.super_structure_widget import SuperStructure +from widgets.structure_works_data.sub_structure_widget import SubStructure +from widgets.structure_works_data.auxiliary_works_widget import AuxiliaryWorks +from widgets.financial_data import FinancialData +from widgets.carbon_emission_data.carbon_emission_data import CarbonEmissionData +from widgets.carbon_emission_data.carbon_emission_cost_data import CarbonEmissionCostData +from widgets.bridge_and_traffic_data import BridgeAndTrafficData +from widgets.maintenance_repair_data import MaintenanceRepairData +from widgets.demolition_and_recycling_data import DemolitionAndRecyclingData +from widgets.project_details_left_widget import ProjectDetailsLeft + +from PySide6.QtWidgets import QStackedWidget class UiMainWindow(object): def setupUi(self, MainWindow): @@ -31,7 +44,7 @@ def setupUi(self, MainWindow): # Set window stylesheet MainWindow.setStyleSheet(""" QMainWindow { - border: 1px solid #285A23; + border: none; } QMenuBar { background-color: #FAFAFA; @@ -81,6 +94,8 @@ def setupUi(self, MainWindow): # Create a central widget and main layout for the window self.central_widget = QWidget() + self.central_widget.setStyleSheet("border: none;") + self.central_widget.setObjectName("central_widget") MainWindow.setCentralWidget(self.central_widget) main_layout = QVBoxLayout(self.central_widget) main_layout.setContentsMargins(0, 0, 0, 0) @@ -156,9 +171,9 @@ def setupUi(self, MainWindow): self.main_content_area.setStyleSheet(""" #main_content_area { background-color: #FAFAFA; - border-left: 1px solid #285A23; - border-right: 1px solid #285A23; - border-bottom: 1px solid #285A23; + border: 1px solid #285A23; + border-top: 1px solid #BBBBBB; + } QLabel { color: #9F8888; @@ -359,14 +374,39 @@ def show_tutorial_widget(): self.left_panel_placeholder.layout().addWidget(self.current_left_widget) self.current_left_widget.closed.connect(lambda: self.remove_left_widget()) - def show_project_details_widget(): + def show_project_details_widget(widget_name=None): if self.current_right_widget: self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) self.current_right_widget.setParent(None) - self.current_right_widget = ProjectDetailsWidget() + self.widget_map = { + "Structure Works Data": Foundation, + "Foundation": Foundation, + "Super-Structure": SuperStructure, + "Sub-Structure": SubStructure, + "Miscellaneous": AuxiliaryWorks, + "Financial Data": FinancialData, + "Carbon Emission Data": CarbonEmissionData, + "Carbon Emission Cost Data": CarbonEmissionCostData, + "Bridge and Traffic Data": BridgeAndTrafficData, + "Maintenance and Repair": MaintenanceRepairData, + "Demolition and Recycling": DemolitionAndRecyclingData + } + if widget_name and widget_name in self.widget_map: + self.current_right_widget = self.widget_map[widget_name]() + self.remove_left_widget() + self.detail_stack = QStackedWidget() + self.current_left_widget = ProjectDetailsLeft(self.widget_map, parent=self) + self.current_left_widget.handle_button_selection(button_name=widget_name) + self.left_panel_placeholder.layout().addWidget(self.current_left_widget) + else: + self.current_right_widget = ProjectDetailsWidget() self.right_panel_placeholder.layout().addWidget(self.current_right_widget) - self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) - + if hasattr(self.current_right_widget, 'closed'): + self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) + # Connect param_buttons if present + if hasattr(self.current_right_widget, 'param_buttons'): + for btn in self.current_right_widget.param_buttons: + btn.clicked.connect(lambda checked, b=btn: show_project_details_widget(b.text().strip())) def remove_right_widget(): if self.current_right_widget: self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) @@ -382,7 +422,13 @@ def remove_left_widget(): self.remove_left_widget = remove_left_widget self.tutorial_tab.clicked.connect(show_tutorial_widget) - self.project_details_tab.clicked.connect(show_project_details_widget) - - # Remove expand_general_area and input_button_toggle methods from UiMainWindow - \ No newline at end of file + self.project_details_tab.clicked.connect(lambda: show_project_details_widget()) + + def show_project_detail_widgets(self, widget_name): + if self.current_right_widget: + self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) + self.current_right_widget.setParent(None) + if widget_name and widget_name in self.widget_map: + self.current_right_widget = self.widget_map[widget_name]() + self.right_panel_placeholder.layout().addWidget(self.current_right_widget) + \ No newline at end of file diff --git a/resources/file_button.png b/resources/file_button.png new file mode 100644 index 0000000000000000000000000000000000000000..a6b579f2f748ebc18b4f1b8a4e4d6ed0dba70722 GIT binary patch literal 1402 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 G`rX#2$<31ph`)pPIe-L>U4 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 L<~s(L1jl~?ghc7{ diff --git a/widgets/__pycache__/tutorial_widget_left.cpython-312.pyc b/widgets/__pycache__/tutorial_widget_left.cpython-312.pyc deleted file mode 100644 index 527d9430556ab994428e80a22fae90d389fec230..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11940 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 RywDUo_~V)13HW9x{|9c^JtY7D diff --git a/widgets/bridge_and_traffic_data.py b/widgets/bridge_and_traffic_data.py index 3d77d63..19dd675 100644 --- a/widgets/bridge_and_traffic_data.py +++ b/widgets/bridge_and_traffic_data.py @@ -1,12 +1,12 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys import os class BridgeAndTrafficData(QWidget): - def __init__(self): + def __init__(self, parent=None): super().__init__() self.text_box_width = 200 self.setStyleSheet(""" @@ -161,7 +161,7 @@ def __init__(self): left_panel_vlayout.setSpacing(0) top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Carbon Emission Cost Data ") + top_button_left_panel = QPushButton("Bridge and Traffic Data ") top_h_layout_left_panel.addWidget(top_button_left_panel) top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) @@ -388,7 +388,6 @@ def __init__(self): left_panel_vlayout.addWidget(self.scroll_area) - #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/widgets/carbon_emission_data/__pycache__/carbon_emission_cost_data.cpython-312.pyc b/widgets/carbon_emission_data/__pycache__/carbon_emission_cost_data.cpython-312.pyc deleted file mode 100644 index f641349351f88832f578416740ee546790febac8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12280 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 Date: Tue, 8 Jul 2025 15:18:20 +0530 Subject: [PATCH 09/22] Fixed closing Signal for all the widgets --- main_template.py | 4 +- widgets/bridge_and_traffic_data.py | 6 +++ .../carbon_emission_cost_data.py | 8 +++- .../carbon_emission_data.py | 13 +++--- widgets/demolition_and_recycling_data.py | 18 ++++---- widgets/financial_data.py | 42 +++++++++++-------- widgets/maintenance_repair_data.py | 8 +++- widgets/project_details_left_widget.py | 8 +++- .../auxiliary_works_widget.py | 8 +++- .../structure_works_data/foundation_widget.py | 8 +++- .../sub_structure_widget.py | 3 +- .../super_structure_widget.py | 7 +++- 12 files changed, 93 insertions(+), 40 deletions(-) diff --git a/main_template.py b/main_template.py index 60dc06e..a3f6951 100644 --- a/main_template.py +++ b/main_template.py @@ -398,11 +398,11 @@ def show_project_details_widget(widget_name=None): self.current_left_widget = ProjectDetailsLeft(self.widget_map, parent=self) self.current_left_widget.handle_button_selection(button_name=widget_name) self.left_panel_placeholder.layout().addWidget(self.current_left_widget) + self.current_left_widget.closed.connect(lambda: self.remove_left_widget()) else: self.current_right_widget = ProjectDetailsWidget() self.right_panel_placeholder.layout().addWidget(self.current_right_widget) - if hasattr(self.current_right_widget, 'closed'): - self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) + self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) # Connect param_buttons if present if hasattr(self.current_right_widget, 'param_buttons'): for btn in self.current_right_widget.param_buttons: diff --git a/widgets/bridge_and_traffic_data.py b/widgets/bridge_and_traffic_data.py index 19dd675..756589b 100644 --- a/widgets/bridge_and_traffic_data.py +++ b/widgets/bridge_and_traffic_data.py @@ -6,6 +6,7 @@ import os class BridgeAndTrafficData(QWidget): + closed = Signal() def __init__(self, parent=None): super().__init__() self.text_box_width = 200 @@ -166,6 +167,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -387,6 +389,10 @@ def __init__(self, parent=None): self.scroll_content_layout.addSpacerItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) left_panel_vlayout.addWidget(self.scroll_area) + + def close_widget(self): + self.closed.emit() + self.setParent(None) #----------------Standalone-Test-Code-------------------------------- diff --git a/widgets/carbon_emission_data/carbon_emission_cost_data.py b/widgets/carbon_emission_data/carbon_emission_cost_data.py index dba9939..c1e64d5 100644 --- a/widgets/carbon_emission_data/carbon_emission_cost_data.py +++ b/widgets/carbon_emission_data/carbon_emission_cost_data.py @@ -1,10 +1,11 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys class CarbonEmissionCostData(QWidget): + closed = Signal() def __init__(self, parent=None): super().__init__(parent) @@ -137,6 +138,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -265,6 +267,10 @@ def __init__(self, parent=None): left_panel_vlayout.addWidget(self.scroll_area) + def close_widget(self): + self.closed.emit() + self.setParent(None) + #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/widgets/carbon_emission_data/carbon_emission_data.py b/widgets/carbon_emission_data/carbon_emission_data.py index 7d1eee5..4650192 100644 --- a/widgets/carbon_emission_data/carbon_emission_data.py +++ b/widgets/carbon_emission_data/carbon_emission_data.py @@ -1,5 +1,5 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys @@ -33,10 +33,7 @@ def init_ui(self): self.material_grid_layout.addWidget(label, 0, col, alignment=Qt.AlignmentFlag.AlignRight) else: - self.material_grid_layout.addWidget(label, 0, col, alignment=Qt.AlignmentFlag.AlignCenter) - - - + self.material_grid_layout.addWidget(label, 0, col, alignment=Qt.AlignmentFlag.AlignCenter) self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) @@ -189,6 +186,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.adjustSize() class CarbonEmissionData(QWidget): + closed = Signal() def __init__(self): super().__init__() @@ -400,6 +398,7 @@ def __init__(self): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -490,6 +489,10 @@ def remove_component_layout(self, component_to_remove): def expand_scroll_area(self): self.central_widget.layout().invalidate() + def close_widget(self): + self.closed.emit() + self.setParent(None) + #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/widgets/demolition_and_recycling_data.py b/widgets/demolition_and_recycling_data.py index c9ac605..18b6a5f 100644 --- a/widgets/demolition_and_recycling_data.py +++ b/widgets/demolition_and_recycling_data.py @@ -1,11 +1,12 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys import os class DemolitionAndRecyclingData(QWidget): + closed = Signal() def __init__(self, parent=None): super().__init__() @@ -138,6 +139,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -278,6 +280,10 @@ def __init__(self, parent=None): left_panel_vlayout.addWidget(self.scroll_area) + def close_widget(self): + self.closed.emit() + self.setParent(None) + #----------------Standalone-Test-Code-------------------------------- class MyMainWindow(QMainWindow): @@ -297,10 +303,6 @@ def __init__(self): self.setWindowState(Qt.WindowMaximized) - -if __name__ == "__main__": - QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) - app = QApplication(sys.argv) - window = MyMainWindow() - window.show() - sys.exit(app.exec()) \ No newline at end of file + def close_widget(self): + self.closed.emit() + self.setParent(None) diff --git a/widgets/financial_data.py b/widgets/financial_data.py index 6470b06..08f9fd5 100644 --- a/widgets/financial_data.py +++ b/widgets/financial_data.py @@ -1,11 +1,12 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys import os class FinancialData(QWidget): + closed = Signal() def __init__(self, parent=None): super().__init__() @@ -138,6 +139,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -288,29 +290,33 @@ def __init__(self, parent=None): left_panel_vlayout.addWidget(self.scroll_area) + def close_widget(self): + self.closed.emit() + self.setParent(None) + #----------------Standalone-Test-Code-------------------------------- -class MyMainWindow(QMainWindow): - def __init__(self): - super().__init__() +# class MyMainWindow(QMainWindow): +# def __init__(self): +# super().__init__() - self.setStyleSheet("border: none") +# self.setStyleSheet("border: none") - self.central_widget = QWidget() - self.central_widget.setObjectName("central_widget") - self.setCentralWidget(self.central_widget) +# self.central_widget = QWidget() +# self.central_widget.setObjectName("central_widget") +# self.setCentralWidget(self.central_widget) - self.main_h_layout = QHBoxLayout(self.central_widget) - self.main_h_layout.addStretch(1) +# self.main_h_layout = QHBoxLayout(self.central_widget) +# self.main_h_layout.addStretch(1) - self.main_h_layout.addWidget(FinancialData(), 2) +# self.main_h_layout.addWidget(FinancialData(), 2) - self.setWindowState(Qt.WindowMaximized) +# self.setWindowState(Qt.WindowMaximized) -if __name__ == "__main__": - QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) - app = QApplication(sys.argv) - window = MyMainWindow() - window.show() - sys.exit(app.exec()) \ No newline at end of file +# if __name__ == "__main__": +# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) +# app = QApplication(sys.argv) +# window = MyMainWindow() +# window.show() +# sys.exit(app.exec()) \ No newline at end of file diff --git a/widgets/maintenance_repair_data.py b/widgets/maintenance_repair_data.py index ffb31cd..68ff967 100644 --- a/widgets/maintenance_repair_data.py +++ b/widgets/maintenance_repair_data.py @@ -1,5 +1,5 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys @@ -19,6 +19,7 @@ def init_ui(self): class MaintenanceRepairData(QWidget): + closed = Signal() def __init__(self, parent=None): super().__init__() @@ -153,6 +154,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -318,6 +320,10 @@ def __init__(self, parent=None): left_panel_vlayout.addWidget(self.scroll_area) + def close_widget(self): + self.closed.emit() + self.setParent(None) + #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/widgets/project_details_left_widget.py b/widgets/project_details_left_widget.py index 3a34d2c..07c3c8e 100644 --- a/widgets/project_details_left_widget.py +++ b/widgets/project_details_left_widget.py @@ -1,5 +1,5 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize, QPropertyAnimation, QEasingCurve +from PySide6.QtCore import QCoreApplication, Qt, QSize, QPropertyAnimation, QEasingCurve, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QTextEdit, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy) from PySide6.QtGui import QIcon, QTextDocument import sys @@ -11,6 +11,7 @@ from PySide6.QtWidgets import QStackedWidget class ProjectDetailsLeft(QWidget): + closed = Signal() """ The main application window that uses a custom title bar. """ @@ -168,6 +169,7 @@ def __init__(self, widget_map, parent=None): top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") top_button_left_panel.setLayoutDirection(Qt.RightToLeft) + top_button_left_panel.clicked.connect(self.close_widget) top_h_layout_left_panel.addWidget(top_button_left_panel, 2) top_h_layout_left_panel.addStretch(1) left_panel_vlayout.addLayout(top_h_layout_left_panel) @@ -367,3 +369,7 @@ def update_button_icon(self, button): def show_structure_widget(self, name, btn): self.parent.show_project_detail_widgets(name) self.handle_button_selection(btn) + + def close_widget(self): + self.closed.emit() + self.setParent(None) \ No newline at end of file diff --git a/widgets/structure_works_data/auxiliary_works_widget.py b/widgets/structure_works_data/auxiliary_works_widget.py index d0579df..8778974 100644 --- a/widgets/structure_works_data/auxiliary_works_widget.py +++ b/widgets/structure_works_data/auxiliary_works_widget.py @@ -1,5 +1,5 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys @@ -211,6 +211,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.adjustSize() # Adjust the size of the component widget class AuxiliaryWorks(QWidget): + closed = Signal() def __init__(self, parent=None): super().__init__(parent) self.setObjectName("central_panel_widget") @@ -418,6 +419,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -498,6 +500,10 @@ def remove_component_layout(self, component_to_remove): def expand_scroll_area(self): self.central_widget.layout().invalidate() + def close_widget(self): + self.closed.emit() + self.setParent(None) + #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/widgets/structure_works_data/foundation_widget.py b/widgets/structure_works_data/foundation_widget.py index 79ad1f0..0ff94d2 100644 --- a/widgets/structure_works_data/foundation_widget.py +++ b/widgets/structure_works_data/foundation_widget.py @@ -1,5 +1,5 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys @@ -211,6 +211,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.adjustSize() # Adjust the size of the component widget class Foundation(QWidget): + closed = Signal() def __init__(self, parent=None): super().__init__(parent) self.setObjectName("central_panel_widget") @@ -418,6 +419,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -498,6 +500,10 @@ def remove_component_layout(self, component_to_remove): def expand_scroll_area(self): self.central_widget.layout().invalidate() + def close_widget(self): + self.closed.emit() + self.setParent(None) + #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/widgets/structure_works_data/sub_structure_widget.py b/widgets/structure_works_data/sub_structure_widget.py index 240a4da..9ddf91d 100644 --- a/widgets/structure_works_data/sub_structure_widget.py +++ b/widgets/structure_works_data/sub_structure_widget.py @@ -210,7 +210,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.adjustSize() # Adjust the size of the component widget class SubStructure(QWidget): - close = Signal() + closed = Signal() def __init__(self, parent=None): super().__init__(parent) self.parent = parent @@ -419,6 +419,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_button_left_panel.setCursor(Qt.CursorShape.PointingHandCursor) top_h_layout_left_panel.addWidget(top_button_left_panel) diff --git a/widgets/structure_works_data/super_structure_widget.py b/widgets/structure_works_data/super_structure_widget.py index cecbacb..d011975 100644 --- a/widgets/structure_works_data/super_structure_widget.py +++ b/widgets/structure_works_data/super_structure_widget.py @@ -1,5 +1,5 @@ from PySide6.QtWidgets import QApplication, QMainWindow -from PySide6.QtCore import QCoreApplication, Qt, QSize +from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon import sys @@ -211,6 +211,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.adjustSize() # Adjust the size of the component widget class SuperStructure(QWidget): + closed = Signal() def __init__(self, parent=None): super().__init__(parent) self.setObjectName("central_panel_widget") @@ -418,6 +419,7 @@ def __init__(self, parent=None): top_button_left_panel.setIcon(QIcon("resources/close.png")) top_button_left_panel.setIconSize(QSize(13, 13)) top_button_left_panel.setObjectName("top_button_left_panel") + top_button_left_panel.clicked.connect(self.close_widget) top_button_left_panel.setLayoutDirection(Qt.RightToLeft) top_h_layout_left_panel.addWidget(top_button_left_panel) @@ -498,6 +500,9 @@ def remove_component_layout(self, component_to_remove): def expand_scroll_area(self): self.central_widget.layout().invalidate() + def close_widget(self): + self.closed.emit() + self.setParent(None) #----------------Standalone-Test-Code-------------------------------- From 54eb9ca1ac83bc453bfe86f00660d9dd4695d23a Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Wed, 9 Jul 2025 14:00:38 +0530 Subject: [PATCH 10/22] removing desktop_app folder that needed to be replaced by new desktop app --- src/osbridgelcca/desktop_app/app.py | 32 ------------------- .../desktop_app/assets/icons/logo.png | 1 - .../desktop_app/assets/icons/refresh_icon.svg | 1 - .../desktop_app/logic/calculations.py | 1 - .../desktop_app/logic/controller.py | 20 ------------ .../desktop_app/logic/database_connector.py | 1 - src/osbridgelcca/desktop_app/ui/input_form.py | 1 - .../desktop_app/ui/main_window.py | 1 - .../desktop_app/ui/results_view.py | 1 - .../desktop_app/ui/themes/dark_mode.qss | 1 - .../desktop_app/ui/themes/light_mode.qss | 1 - 11 files changed, 61 deletions(-) delete mode 100644 src/osbridgelcca/desktop_app/app.py delete mode 100644 src/osbridgelcca/desktop_app/assets/icons/logo.png delete mode 100644 src/osbridgelcca/desktop_app/assets/icons/refresh_icon.svg delete mode 100644 src/osbridgelcca/desktop_app/logic/calculations.py delete mode 100644 src/osbridgelcca/desktop_app/logic/controller.py delete mode 100644 src/osbridgelcca/desktop_app/logic/database_connector.py delete mode 100644 src/osbridgelcca/desktop_app/ui/input_form.py delete mode 100644 src/osbridgelcca/desktop_app/ui/main_window.py delete mode 100644 src/osbridgelcca/desktop_app/ui/results_view.py delete mode 100644 src/osbridgelcca/desktop_app/ui/themes/dark_mode.qss delete mode 100644 src/osbridgelcca/desktop_app/ui/themes/light_mode.qss diff --git a/src/osbridgelcca/desktop_app/app.py b/src/osbridgelcca/desktop_app/app.py deleted file mode 100644 index 62e7ee9..0000000 --- a/src/osbridgelcca/desktop_app/app.py +++ /dev/null @@ -1,32 +0,0 @@ -from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget -from desktop_app.logic.controller import Controller - - -class MainWindow(QMainWindow): - """Main application window.""" - - def __init__(self): - super().__init__() - self.setWindowTitle("OsBridgeLCCA - Desktop App") - self.setGeometry(100, 100, 800, 600) - - layout = QVBoxLayout() - self.run_button = QPushButton("Run LCCA Analysis") - self.run_button.clicked.connect(self.run_analysis) - layout.addWidget(self.run_button) - - container = QWidget() - container.setLayout(layout) - self.setCentralWidget(container) - - def run_analysis(self): - """Trigger analysis when button is clicked.""" - controller = Controller() - controller.run_calculations() - - -if __name__ == "__main__": - app = QApplication([]) - window = MainWindow() - window.show() - app.exec_() diff --git a/src/osbridgelcca/desktop_app/assets/icons/logo.png b/src/osbridgelcca/desktop_app/assets/icons/logo.png deleted file mode 100644 index 1023b80..0000000 --- a/src/osbridgelcca/desktop_app/assets/icons/logo.png +++ /dev/null @@ -1 +0,0 @@ -Placeholder for logo.png diff --git a/src/osbridgelcca/desktop_app/assets/icons/refresh_icon.svg b/src/osbridgelcca/desktop_app/assets/icons/refresh_icon.svg deleted file mode 100644 index 39306e0..0000000 --- a/src/osbridgelcca/desktop_app/assets/icons/refresh_icon.svg +++ /dev/null @@ -1 +0,0 @@ -Placeholder for refresh_icon.svg diff --git a/src/osbridgelcca/desktop_app/logic/calculations.py b/src/osbridgelcca/desktop_app/logic/calculations.py deleted file mode 100644 index eac1a9d..0000000 --- a/src/osbridgelcca/desktop_app/logic/calculations.py +++ /dev/null @@ -1 +0,0 @@ -# Placeholder for calculations logic diff --git a/src/osbridgelcca/desktop_app/logic/controller.py b/src/osbridgelcca/desktop_app/logic/controller.py deleted file mode 100644 index 1b3c19b..0000000 --- a/src/osbridgelcca/desktop_app/logic/controller.py +++ /dev/null @@ -1,20 +0,0 @@ -from core.bridge_lcc import BridgeLCC -from core.visualization import Visualization - - -class Controller: - """Handles UI logic and backend integration.""" - - def run_calculations(self): - """Run LCCA calculations and display results.""" - inputs = { - "bill_of_quantity": {"steel": 10000, "concrete": 5000}, - "maintenance_cost": 2000, - "vehicle_operating_cost": 5000, - "discount_rate": 0.05 - } - lcc = BridgeLCC("Sample Project", inputs) - results = lcc.calculate_lcc() - - fig = Visualization.plot_lcc_distribution(lcc.get_outputs()) - fig.show() diff --git a/src/osbridgelcca/desktop_app/logic/database_connector.py b/src/osbridgelcca/desktop_app/logic/database_connector.py deleted file mode 100644 index 8c79e9d..0000000 --- a/src/osbridgelcca/desktop_app/logic/database_connector.py +++ /dev/null @@ -1 +0,0 @@ -# Placeholder for database connector diff --git a/src/osbridgelcca/desktop_app/ui/input_form.py b/src/osbridgelcca/desktop_app/ui/input_form.py deleted file mode 100644 index 5516c75..0000000 --- a/src/osbridgelcca/desktop_app/ui/input_form.py +++ /dev/null @@ -1 +0,0 @@ -# Placeholder for input form UI diff --git a/src/osbridgelcca/desktop_app/ui/main_window.py b/src/osbridgelcca/desktop_app/ui/main_window.py deleted file mode 100644 index 27ae599..0000000 --- a/src/osbridgelcca/desktop_app/ui/main_window.py +++ /dev/null @@ -1 +0,0 @@ -# Placeholder for main window UI diff --git a/src/osbridgelcca/desktop_app/ui/results_view.py b/src/osbridgelcca/desktop_app/ui/results_view.py deleted file mode 100644 index 444c46e..0000000 --- a/src/osbridgelcca/desktop_app/ui/results_view.py +++ /dev/null @@ -1 +0,0 @@ -# Placeholder for results view UI diff --git a/src/osbridgelcca/desktop_app/ui/themes/dark_mode.qss b/src/osbridgelcca/desktop_app/ui/themes/dark_mode.qss deleted file mode 100644 index 8914700..0000000 --- a/src/osbridgelcca/desktop_app/ui/themes/dark_mode.qss +++ /dev/null @@ -1 +0,0 @@ -/* Placeholder for dark mode stylesheet */ diff --git a/src/osbridgelcca/desktop_app/ui/themes/light_mode.qss b/src/osbridgelcca/desktop_app/ui/themes/light_mode.qss deleted file mode 100644 index 71092d2..0000000 --- a/src/osbridgelcca/desktop_app/ui/themes/light_mode.qss +++ /dev/null @@ -1 +0,0 @@ -/* Placeholder for light mode stylesheet */ From 13fd50ee1e0e8111c446bce7025ef032f3c91de4 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Wed, 9 Jul 2025 14:25:00 +0530 Subject: [PATCH 11/22] Fixed the appearance of 'x' button in components of structure_works_data widgets The new desktop_app can be tested using 'python -m app' from this directory 'OsBridgeLCCA\src\osbridgelcca\desktop_app\' --- .../widgets/structure_works_data/auxiliary_works_widget.py | 2 ++ .../widgets/structure_works_data/foundation_widget.py | 2 ++ .../widgets/structure_works_data/sub_structure_widget.py | 2 ++ .../widgets/structure_works_data/super_structure_widget.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py index 8778974..b1e6481 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py @@ -39,6 +39,7 @@ def init_ui(self): border-radius: 12px; font-weight: bold; line-height:12px; + padding: 0px; color: #CC0000; } QPushButton:hover { @@ -141,6 +142,7 @@ def add_material_row(self): border-radius: 12px; font-weight: bold; line-height:12px; + padding: 0px; color: #CC0000; } QPushButton:hover { diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py index 0ff94d2..cdf193e 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py @@ -40,6 +40,7 @@ def init_ui(self): font-weight: bold; line-height:12px; color: #CC0000; + padding: 0px; } QPushButton:hover { background-color: #FF9999; @@ -141,6 +142,7 @@ def add_material_row(self): border-radius: 12px; font-weight: bold; line-height:12px; + padding: 0px; color: #CC0000; } QPushButton:hover { diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py index 9ddf91d..adfc183 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py @@ -38,6 +38,7 @@ def init_ui(self): border: 1px solid #FF9999; border-radius: 12px; font-weight: bold; + padding: 0px; color: #CC0000; } QPushButton:hover { @@ -139,6 +140,7 @@ def add_material_row(self): border: 1px solid #FF9999; border-radius: 12px; font-weight: bold; + padding: 0px; color: #CC0000; } QPushButton:hover { diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py index d011975..c62490f 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py @@ -39,6 +39,7 @@ def init_ui(self): border-radius: 12px; font-weight: bold; line-height:12px; + padding: 0px; color: #CC0000; } QPushButton:hover { @@ -141,6 +142,7 @@ def add_material_row(self): border-radius: 12px; font-weight: bold; line-height:12px; + padding: 0px; color: #CC0000; } QPushButton:hover { From 8680ab53f421748de6331ecf52b85d235596f36e Mon Sep 17 00:00:00 2001 From: Mohammad Suhail <112853656+mhsuhail00@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:03:05 +0530 Subject: [PATCH 12/22] Update main_template.py --- src/osbridgelcca/desktop_app/main_template.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/osbridgelcca/desktop_app/main_template.py b/src/osbridgelcca/desktop_app/main_template.py index a3f6951..8c20d02 100644 --- a/src/osbridgelcca/desktop_app/main_template.py +++ b/src/osbridgelcca/desktop_app/main_template.py @@ -1,3 +1,9 @@ +""" +: Prerna Praveen Vidyarthi +: FOSSEE Summer Fellowship 2025 +: https://github.com/prerna2024-cyber +: vidyarthiprerna637@gmail.com +""" from PySide6.QtCore import (QSize, Qt, QPropertyAnimation, QEasingCurve) from PySide6.QtGui import (QAction,QFont, QFontDatabase, QIcon) from PySide6.QtWidgets import (QApplication, QHBoxLayout, QTextEdit, QScrollArea, QSpacerItem, QSizePolicy, @@ -431,4 +437,4 @@ def show_project_detail_widgets(self, widget_name): if widget_name and widget_name in self.widget_map: self.current_right_widget = self.widget_map[widget_name]() self.right_panel_placeholder.layout().addWidget(self.current_right_widget) - \ No newline at end of file + From 559bb79caa90203c89380ea020172babce31575a Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Mon, 8 Sep 2025 02:44:06 +0530 Subject: [PATCH 13/22] Added Tabs in Project detail widgets --- src/osbridgelcca/desktop_app/main_template.py | 89 +++++--- .../widgets/bridge_and_traffic_data.py | 13 -- .../carbon_emission_cost_data.py | 13 -- .../carbon_emission_data.py | 13 -- .../widgets/demolition_and_recycling_data.py | 13 -- .../desktop_app/widgets/financial_data.py | 16 -- .../widgets/maintenance_repair_data.py | 13 -- .../widgets/project_details_left_widget.py | 2 +- .../auxiliary_works_widget.py | 13 -- .../structure_works_data/foundation_widget.py | 13 -- .../sub_structure_widget.py | 14 -- .../super_structure_widget.py | 13 -- .../desktop_app/widgets/tab_widget.py | 214 ++++++++++++++++++ .../desktop_app/widgets/tab_widget_backup.py | 205 +++++++++++++++++ 14 files changed, 481 insertions(+), 163 deletions(-) create mode 100644 src/osbridgelcca/desktop_app/widgets/tab_widget.py create mode 100644 src/osbridgelcca/desktop_app/widgets/tab_widget_backup.py diff --git a/src/osbridgelcca/desktop_app/main_template.py b/src/osbridgelcca/desktop_app/main_template.py index 8c20d02..af16b07 100644 --- a/src/osbridgelcca/desktop_app/main_template.py +++ b/src/osbridgelcca/desktop_app/main_template.py @@ -23,11 +23,30 @@ from widgets.maintenance_repair_data import MaintenanceRepairData from widgets.demolition_and_recycling_data import DemolitionAndRecyclingData from widgets.project_details_left_widget import ProjectDetailsLeft +from widgets.tab_widget import CustomTabWidget from PySide6.QtWidgets import QStackedWidget class UiMainWindow(object): def setupUi(self, MainWindow): + # To check if tab widget is there or no + self.tabs_active = False + # Contains name of widget and its index in tabs + self.active_tab_widgets = {} + self.widget_map = { + "Structure Works Data": Foundation, + "Foundation": Foundation, + "Super-Structure": SuperStructure, + "Sub-Structure": SubStructure, + "Miscellaneous": AuxiliaryWorks, + "Financial Data": FinancialData, + "Carbon Emission Data": CarbonEmissionData, + "Carbon Emission Cost Data": CarbonEmissionCostData, + "Bridge and Traffic Data": BridgeAndTrafficData, + "Maintenance and Repair": MaintenanceRepairData, + "Demolition and Recycling": DemolitionAndRecyclingData + } + if not MainWindow.objectName(): MainWindow.setObjectName(u"MainWindow") @@ -381,38 +400,45 @@ def show_tutorial_widget(): self.current_left_widget.closed.connect(lambda: self.remove_left_widget()) def show_project_details_widget(widget_name=None): - if self.current_right_widget: - self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) - self.current_right_widget.setParent(None) - self.widget_map = { - "Structure Works Data": Foundation, - "Foundation": Foundation, - "Super-Structure": SuperStructure, - "Sub-Structure": SubStructure, - "Miscellaneous": AuxiliaryWorks, - "Financial Data": FinancialData, - "Carbon Emission Data": CarbonEmissionData, - "Carbon Emission Cost Data": CarbonEmissionCostData, - "Bridge and Traffic Data": BridgeAndTrafficData, - "Maintenance and Repair": MaintenanceRepairData, - "Demolition and Recycling": DemolitionAndRecyclingData - } - if widget_name and widget_name in self.widget_map: - self.current_right_widget = self.widget_map[widget_name]() + if widget_name and widget_name in self.widget_map: + # Tabs are already visible + if self.tabs_active: + if self.active_tab_widgets.get(widget_name) is not None: + # change active tab to that tab + index = self.active_tab_widgets[widget_name] + self.current_right_widget.activate_tab(index) + else: + # Flush active tabs + self.remove_right_widget() + self.active_tab_widgets = {} + self.tabs_active = True + self.current_right_widget = CustomTabWidget(parent=self) + # Add Tab widget + self.right_panel_placeholder.layout().addWidget(self.current_right_widget) + + # Remove left widget first self.remove_left_widget() - self.detail_stack = QStackedWidget() + # Create left panel self.current_left_widget = ProjectDetailsLeft(self.widget_map, parent=self) - self.current_left_widget.handle_button_selection(button_name=widget_name) self.left_panel_placeholder.layout().addWidget(self.current_left_widget) self.current_left_widget.closed.connect(lambda: self.remove_left_widget()) + self.current_left_widget.handle_button_selection(button_name=widget_name) else: + # Switch to normal widget mode + if self.current_right_widget: + self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) + self.current_right_widget.setParent(None) + self.tabs_active = False + self.active_tab_widgets = {} # Clear tab tracking self.current_right_widget = ProjectDetailsWidget() - self.right_panel_placeholder.layout().addWidget(self.current_right_widget) - self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) + self.right_panel_placeholder.layout().addWidget(self.current_right_widget) + self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) + # Connect param_buttons if present if hasattr(self.current_right_widget, 'param_buttons'): for btn in self.current_right_widget.param_buttons: btn.clicked.connect(lambda checked, b=btn: show_project_details_widget(b.text().strip())) + def remove_right_widget(): if self.current_right_widget: self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) @@ -431,10 +457,17 @@ def remove_left_widget(): self.project_details_tab.clicked.connect(lambda: show_project_details_widget()) def show_project_detail_widgets(self, widget_name): - if self.current_right_widget: - self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) - self.current_right_widget.setParent(None) + """Public method to show project detail widgets""" if widget_name and widget_name in self.widget_map: - self.current_right_widget = self.widget_map[widget_name]() - self.right_panel_placeholder.layout().addWidget(self.current_right_widget) - + # Tabs are already visible + if self.tabs_active: + if self.active_tab_widgets.get(widget_name) is not None: + # change active tab to that tab + index = self.active_tab_widgets[widget_name] + self.current_right_widget.activate_tab(index) + else: + # add new tab + widget = self.widget_map[widget_name]() + index = self.current_right_widget.add_new_tab(widget, widget_name) + # add record of this widget + self.active_tab_widgets[widget_name] = index diff --git a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py index 756589b..ac4492c 100644 --- a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py +++ b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py @@ -161,19 +161,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Bridge and Traffic Data ") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py index c1e64d5..0248e19 100644 --- a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py +++ b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py @@ -132,19 +132,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Carbon Emission Cost Data ") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py index 4650192..dde9143 100644 --- a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py +++ b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py @@ -392,19 +392,6 @@ def __init__(self): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Carbon Emission Data") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py b/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py index 18b6a5f..3f5bafe 100644 --- a/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py +++ b/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py @@ -133,19 +133,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Demolition and Recycling Data ") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/financial_data.py b/src/osbridgelcca/desktop_app/widgets/financial_data.py index 08f9fd5..523b7b9 100644 --- a/src/osbridgelcca/desktop_app/widgets/financial_data.py +++ b/src/osbridgelcca/desktop_app/widgets/financial_data.py @@ -133,19 +133,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Financial Data ") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) @@ -164,9 +151,6 @@ def __init__(self, parent=None): self.general_layout.setContentsMargins(10, 20, 10, 10) self.general_layout.setSpacing(10) - # Change section title - top_button_left_panel.setText("Financial Data ") - # --- Financial Data Form --- grid_layout = QGridLayout() grid_layout.setHorizontalSpacing(10) diff --git a/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py b/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py index 68ff967..26231fb 100644 --- a/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py +++ b/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py @@ -148,19 +148,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Maintenance and Repair Data ") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/project_details_left_widget.py b/src/osbridgelcca/desktop_app/widgets/project_details_left_widget.py index 07c3c8e..1dc58d1 100644 --- a/src/osbridgelcca/desktop_app/widgets/project_details_left_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/project_details_left_widget.py @@ -15,7 +15,7 @@ class ProjectDetailsLeft(QWidget): """ The main application window that uses a custom title bar. """ - def __init__(self, widget_map, parent=None): + def __init__(self, widget_map, parent): super().__init__() self.widget_map = widget_map self.parent = parent diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py index b1e6481..6504aa2 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py @@ -415,19 +415,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Auxiliary Works") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py index cdf193e..4bf5e24 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py @@ -415,19 +415,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Foundation") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py index adfc183..0e5b9e6 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py @@ -415,20 +415,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Sub-Structure") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_button_left_panel.setCursor(Qt.CursorShape.PointingHandCursor) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py index c62490f..931721d 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py @@ -415,19 +415,6 @@ def __init__(self, parent=None): left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) - top_h_layout_left_panel = QHBoxLayout() - top_button_left_panel = QPushButton("Super-Structure") - top_h_layout_left_panel.addWidget(top_button_left_panel) - top_button_left_panel.setIcon(QIcon("resources/close.png")) - top_button_left_panel.setIconSize(QSize(13, 13)) - top_button_left_panel.setObjectName("top_button_left_panel") - top_button_left_panel.clicked.connect(self.close_widget) - top_button_left_panel.setLayoutDirection(Qt.RightToLeft) - top_h_layout_left_panel.addWidget(top_button_left_panel) - - top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) - left_panel_vlayout.addLayout(top_h_layout_left_panel) - self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) diff --git a/src/osbridgelcca/desktop_app/widgets/tab_widget.py b/src/osbridgelcca/desktop_app/widgets/tab_widget.py new file mode 100644 index 0000000..122bdea --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/tab_widget.py @@ -0,0 +1,214 @@ +from PySide6.QtWidgets import (QWidget, QVBoxLayout, QTabWidget, + QLabel, QMessageBox) +from PySide6.QtCore import Qt, Signal +import os + +class CustomTabWidget(QWidget): + """Combined widget that contains both tab bar and tab functionality""" + tab_close_requested = Signal(int) + + def __init__(self, parent): + super().__init__(None) + self.parent = parent + self.tab_counter = 0 + self.init_ui() + self.apply_styles() + + def init_ui(self): + """Initialize the user interface""" + layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + # Create the tab widget + self.tab_widget = QTabWidget() + self.tab_widget.setTabsClosable(True) + self.tab_widget.setMovable(True) + + # Connect signals + self.tab_widget.tabCloseRequested.connect(self.close_tab) + layout.addWidget(self.tab_widget) + + def apply_styles(self): + """Apply custom styles to the widget""" + # Build absolute path for close button image + try: + base_dir = os.path.dirname(os.path.abspath(__file__)) + close_img_path = os.path.join(base_dir, "..", "resources", "close.png") + close_img_path = os.path.normpath(close_img_path) + close_img_url = close_img_path.replace("\\", "/") # for Windows compatibility + except: + close_img_url = "" # Fallback if image not found + + style = f""" + CustomTabWidget {{ + background-color: transparent; + }} + QTabWidget::pane {{ + border: none; + background-color: transparent; + }} + QTabBar::tab {{ + background-color: #FDEFEF; + border-top: 1px solid #E0E0E0; + border-left: 1px solid #E0E0E0; + border-right: 1px solid #E0E0E0; + text-align: left; + padding: 4px 10px; + color: #000000; + min-width: 80px; + }} + QTabBar::tab:selected {{ + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + color: #000000; + }} + QTabBar::tab:hover {{ + background-color: #F0E6E6; + border-color: #808080; + }} + QTabBar::tab:selected:hover {{ + background-color: #F0E6E6; + border-color: #808080; + }} + QTabBar::tab:pressed {{ + background-color: #FFF3F3; + border-color: #606060; + }} + QTabBar::tab:selected:pressed {{ + background-color: #FFF3F3; + border-color: #606060; + }} + QTabBar::close-button {{ + subcontrol-position: right; + width: 13px; + height: 13px; + margin-right: 6px; + border: 1px solid #ff5252; + border-radius: 6px; + color: white; + font-weight: bold; + }} + QTabBar::close-button:hover {{ + border-color: #f44336; + }} + """ + + # If image exists, use it; otherwise use colored button + if close_img_url and os.path.exists(close_img_url.replace("file:///", "")): + style = style.replace( + """QTabBar::close-button { + subcontrol-position: right; + width: 13px; + height: 13px; + margin-right: 6px; + border: 1px solid #ff5252; + border-radius: 6px; + color: white; + font-weight: bold; + }""", + f"""QTabBar::close-button {{ + image: url({close_img_url}); + subcontrol-position: right; + width: 13px; + height: 13px; + margin-right: 6px; + background: transparent; + border: none; + }}""" + ) + + self.setStyleSheet(style) + + def addWidget(self, widget, title): + """Add a new tab with the given widget and title""" + if not widget: + return -1 + + self.tab_counter += 1 + if not title: + title = f"Tab {self.tab_counter}" + + # Add the tab + index = self.tab_widget.addTab(widget, title) + self.tab_widget.setCurrentIndex(index) + return index + + def activate_tab(self, index): + """Activate the tab at the specified index.""" + if 0 <= index < self.tab_widget.count(): + self.tab_widget.setCurrentIndex(index) + else: + print(f"Warning: Invalid tab index: {index}. Must be between 0 and {self.tab_widget.count() - 1}.") + + def add_new_tab(self, widget=None, title=None): + """Add a new tab (alternative method name for compatibility)""" + if widget is None: + # Create a default widget if none provided + widget = QWidget() + layout = QVBoxLayout(widget) + label = QLabel(f"Content for {title or f'Tab {self.tab_counter + 1}'}") + label.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(label) + + return self.addWidget(widget, title) + + def close_tab(self, index): + """Close a tab at the given index""" + if index < 0 or index >= self.tab_widget.count(): + return False + + if self.tab_widget.count() <= 1: + QMessageBox.information( + self, + 'Close Tab', + 'This is the last tab. Do you want to close it?', + QMessageBox.StandardButton.Ok + ) + return False + + # get title + widget_name = self.tab_widget.tabText(index) + + # Fix: Check if key exists before deleting + if widget_name in self.parent.active_tab_widgets: + del self.parent.active_tab_widgets[widget_name] + + # Update indices for remaining tabs + for tab in list(self.parent.active_tab_widgets.keys()): + if self.parent.active_tab_widgets[tab] > index: + self.parent.active_tab_widgets[tab] -= 1 + + self.tab_widget.removeTab(index) + self.tab_close_requested.emit(index) + return True + + def removeTab(self, index): + """Remove tab at given index""" + self.tab_widget.removeTab(index) + + def setCurrentIndex(self, index): + """Set the current active tab""" + self.tab_widget.setCurrentIndex(index) + + def currentIndex(self): + """Get the current active tab index""" + return self.tab_widget.currentIndex() + + def count(self): + """Get the number of tabs""" + return self.tab_widget.count() + + def widget(self, index): + """Get widget at given index""" + return self.tab_widget.widget(index) + + def setTabText(self, index, text): + """Set text for tab at given index""" + self.tab_widget.setTabText(index, text) + + def tabText(self, index): + """Get text for tab at given index""" + return self.tab_widget.tabText(index) diff --git a/src/osbridgelcca/desktop_app/widgets/tab_widget_backup.py b/src/osbridgelcca/desktop_app/widgets/tab_widget_backup.py new file mode 100644 index 0000000..9fb82a3 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/tab_widget_backup.py @@ -0,0 +1,205 @@ +from PySide6.QtWidgets import (QWidget, QVBoxLayout, QTabWidget, + QLabel, QMessageBox) +from PySide6.QtCore import Qt, Signal +import os + +class CustomTabWidget(QWidget): + """Combined widget that contains both tab bar and tab functionality""" + tab_close_requested = Signal(int) + + def __init__(self, parent): + super().__init__(None) + self.parent = parent + self.tab_counter = 0 + self.init_ui() + self.apply_styles() + + def init_ui(self): + """Initialize the user interface""" + layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + + # Create the tab widget + self.tab_widget = QTabWidget() + self.tab_widget.setTabsClosable(True) + self.tab_widget.setMovable(True) + + # Connect signals + self.tab_widget.tabCloseRequested.connect(self.close_tab) + layout.addWidget(self.tab_widget) + + def apply_styles(self): + """Apply custom styles to the widget""" + # Build absolute path for close button image + try: + base_dir = os.path.dirname(os.path.abspath(__file__)) + close_img_path = os.path.join(base_dir, "..", "resources", "close.png") + close_img_path = os.path.normpath(close_img_path) + close_img_url = close_img_path.replace("\\", "/") # for Windows compatibility + except: + close_img_url = "" # Fallback if image not found + + style = f""" + CustomTabWidget {{ + background-color: #f0f0f0; + }} + QTabWidget::pane {{ + border: 1px solid #c0c0c0; + background-color: white; + }} + QTabBar::tab {{ + background-color: #FDEFEF; + border-top: 1px solid #E0E0E0; + border-left: 1px solid #E0E0E0; + border-right: 1px solid #E0E0E0; + text-align: left; + padding: 4px 10px; + color: #000000; + min-width: 80px; + }} + QTabBar::tab:selected {{ + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + color: #000000; + }} + QTabBar::tab:hover {{ + background-color: #F0E6E6; + border-color: #808080; + }} + QTabBar::tab:selected:hover {{ + background-color: #F0E6E6; + border-color: #808080; + }} + QTabBar::tab:pressed {{ + background-color: #FFF3F3; + border-color: #606060; + }} + QTabBar::tab:selected:pressed {{ + background-color: #FFF3F3; + border-color: #606060; + }} + QTabBar::close-button {{ + subcontrol-position: right; + width: 13px; + height: 13px; + margin-right: 6px; + border: 1px solid #ff5252; + border-radius: 6px; + color: white; + font-weight: bold; + }} + QTabBar::close-button:hover {{ + border-color: #f44336; + }} + """ + + # If image exists, use it; otherwise use colored button + if close_img_url and os.path.exists(close_img_url.replace("file:///", "")): + style = style.replace( + """QTabBar::close-button { + subcontrol-position: right; + width: 13px; + height: 13px; + margin-right: 6px; + border: 1px solid #ff5252; + border-radius: 6px; + color: white; + font-weight: bold; + }""", + f"""QTabBar::close-button {{ + image: url({close_img_url}); + subcontrol-position: right; + width: 13px; + height: 13px; + margin-right: 6px; + background: transparent; + border: none; + }}""" + ) + + self.setStyleSheet(style) + + def addWidget(self, widget, title): + """Add a new tab with the given widget and title""" + self.tab_counter += 1 + if not title: + title = f"Tab {self.tab_counter}" + + # Add the tab + index = self.tab_widget.addTab(widget, title) + self.tab_widget.setCurrentIndex(index) + # return index + return index + + def activate_tab(self, index): + """Activate the tab at the specified index.""" + if 0 <= index < self.tab_counter: + self.setCurrentIndex(index) + else: + raise ValueError(f"Invalid tab index: {index}. Must be between 0 and {self.count() - 1}.") + + def add_new_tab(self, widget=None, title=None): + """Add a new tab (alternative method name for compatibility)""" + if widget is None: + # Create a default widget if none provided + widget = QWidget() + layout = QVBoxLayout(widget) + label = QLabel(f"Content for {title or f'Tab {self.tab_counter + 1}'}") + label.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(label) + self.addWidget(widget, title) + + def close_tab(self, index): + """Close a tab at the given index""" + if index < 0 or index >= self.tab_widget.count(): + return False + if self.tab_widget.count() <= 1: + QMessageBox.information( + self, + 'Close Tab', + 'This is the last tab. Do you want to close it?', + QMessageBox.StandardButton.Ok + ) + return False + + # get title + widget_name = self.tab_widget.tabText(index) + del self.parent.active_tab_widgets[widget_name] + + for tab in self.parent.active_tab_widgets.keys(): + if self.parent.active_tab_widgets[tab] > index: + self.parent.active_tab_widgets[tab] -= 1 + + self.tab_widget.removeTab(index) + self.tab_close_requested.emit(index) + + def removeTab(self, index): + """Remove tab at given index""" + self.tab_widget.removeTab(index) + + def setCurrentIndex(self, index): + """Set the current active tab""" + self.tab_widget.setCurrentIndex(index) + + def currentIndex(self): + """Get the current active tab index""" + return self.tab_widget.currentIndex() + + def count(self): + """Get the number of tabs""" + return self.tab_widget.count() + + def widget(self, index): + """Get widget at given index""" + return self.tab_widget.widget(index) + + def setTabText(self, index, text): + """Set text for tab at given index""" + self.tab_widget.setTabText(index, text) + + def tabText(self, index): + """Get text for tab at given index""" + return self.tab_widget.tabText(index) \ No newline at end of file From 2be68a9b65ef31c8df0f0112da1f89d12f5dda70 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Mon, 8 Sep 2025 12:13:39 +0530 Subject: [PATCH 14/22] Graph Integration --- .../desktop_app/dependencies/d3js.js | 2 + .../desktop_app/graphs/bar_graph.py | 436 ++++ .../desktop_app/graphs/bubble_graph.py | 231 ++ src/osbridgelcca/desktop_app/graphs/data.csv | 29 + src/osbridgelcca/desktop_app/graphs/data1.csv | 29 + .../graphs/horizontal_bar_graph.py | 345 +++ .../desktop_app/graphs/pie_chart.py | 569 +++++ .../desktop_app/graphs/radial_bar_graph.py | 205 ++ src/osbridgelcca/desktop_app/main_template.py | 24 +- .../desktop_app/widgets/comparison_widget.py | 873 +++++++ .../desktop_app/widgets/results_widget.py | 2117 +++++++++++++++++ 11 files changed, 4859 insertions(+), 1 deletion(-) create mode 100644 src/osbridgelcca/desktop_app/dependencies/d3js.js create mode 100644 src/osbridgelcca/desktop_app/graphs/bar_graph.py create mode 100644 src/osbridgelcca/desktop_app/graphs/bubble_graph.py create mode 100644 src/osbridgelcca/desktop_app/graphs/data.csv create mode 100644 src/osbridgelcca/desktop_app/graphs/data1.csv create mode 100644 src/osbridgelcca/desktop_app/graphs/horizontal_bar_graph.py create mode 100644 src/osbridgelcca/desktop_app/graphs/pie_chart.py create mode 100644 src/osbridgelcca/desktop_app/graphs/radial_bar_graph.py create mode 100644 src/osbridgelcca/desktop_app/widgets/comparison_widget.py create mode 100644 src/osbridgelcca/desktop_app/widgets/results_widget.py diff --git a/src/osbridgelcca/desktop_app/dependencies/d3js.js b/src/osbridgelcca/desktop_app/dependencies/d3js.js new file mode 100644 index 0000000..33bb880 --- /dev/null +++ b/src/osbridgelcca/desktop_app/dependencies/d3js.js @@ -0,0 +1,2 @@ +// https://d3js.org v7.9.0 Copyright 2010-2023 Mike Bostock +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).d3=t.d3||{})}(this,(function(t){"use strict";function n(t,n){return null==t||null==n?NaN:tn?1:t>=n?0:NaN}function e(t,n){return null==t||null==n?NaN:nt?1:n>=t?0:NaN}function r(t){let r,o,a;function u(t,n,e=0,i=t.length){if(e>>1;o(t[r],n)<0?e=r+1:i=r}while(en(t(e),r),a=(n,e)=>t(n)-e):(r=t===n||t===e?t:i,o=t,a=t),{left:u,center:function(t,n,e=0,r=t.length){const i=u(t,n,e,r-1);return i>e&&a(t[i-1],n)>-a(t[i],n)?i-1:i},right:function(t,n,e=0,i=t.length){if(e>>1;o(t[r],n)<=0?e=r+1:i=r}while(e{n(t,e,(r<<=2)+0,(i<<=2)+0,o<<=2),n(t,e,r+1,i+1,o),n(t,e,r+2,i+2,o),n(t,e,r+3,i+3,o)}}));function d(t){return function(n,e,r=e){if(!((e=+e)>=0))throw new RangeError("invalid rx");if(!((r=+r)>=0))throw new RangeError("invalid ry");let{data:i,width:o,height:a}=n;if(!((o=Math.floor(o))>=0))throw new RangeError("invalid width");if(!((a=Math.floor(void 0!==a?a:i.length/o))>=0))throw new RangeError("invalid height");if(!o||!a||!e&&!r)return n;const u=e&&t(e),c=r&&t(r),f=i.slice();return u&&c?(p(u,f,i,o,a),p(u,i,f,o,a),p(u,f,i,o,a),g(c,i,f,o,a),g(c,f,i,o,a),g(c,i,f,o,a)):u?(p(u,i,f,o,a),p(u,f,i,o,a),p(u,i,f,o,a)):c&&(g(c,i,f,o,a),g(c,f,i,o,a),g(c,i,f,o,a)),n}}function p(t,n,e,r,i){for(let o=0,a=r*i;o{if(!((o-=a)>=i))return;let u=t*r[i];const c=a*t;for(let t=i,n=i+c;t{if(!((a-=u)>=o))return;let c=n*i[o];const f=u*n,s=f+u;for(let t=o,n=o+f;t=n&&++e;else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(i=+i)>=i&&++e}return e}function _(t){return 0|t.length}function b(t){return!(t>0)}function m(t){return"object"!=typeof t||"length"in t?t:Array.from(t)}function x(t,n){let e,r=0,i=0,o=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(e=n-i,i+=e/++r,o+=e*(n-i));else{let a=-1;for(let u of t)null!=(u=n(u,++a,t))&&(u=+u)>=u&&(e=u-i,i+=e/++r,o+=e*(u-i))}if(r>1)return o/(r-1)}function w(t,n){const e=x(t,n);return e?Math.sqrt(e):e}function M(t,n){let e,r;if(void 0===n)for(const n of t)null!=n&&(void 0===e?n>=n&&(e=r=n):(e>n&&(e=n),r=o&&(e=r=o):(e>o&&(e=o),r0){for(o=t[--i];i>0&&(n=o,e=t[--i],o=n+e,r=e-(o-n),!r););i>0&&(r<0&&t[i-1]<0||r>0&&t[i-1]>0)&&(e=2*r,n=o+e,e==n-o&&(o=n))}return o}}class InternMap extends Map{constructor(t,n=N){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(A(this,t))}has(t){return super.has(A(this,t))}set(t,n){return super.set(S(this,t),n)}delete(t){return super.delete(E(this,t))}}class InternSet extends Set{constructor(t,n=N){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const n of t)this.add(n)}has(t){return super.has(A(this,t))}add(t){return super.add(S(this,t))}delete(t){return super.delete(E(this,t))}}function A({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):e}function S({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}function E({_intern:t,_key:n},e){const r=n(e);return t.has(r)&&(e=t.get(r),t.delete(r)),e}function N(t){return null!==t&&"object"==typeof t?t.valueOf():t}function k(t){return t}function C(t,...n){return F(t,k,k,n)}function P(t,...n){return F(t,Array.from,k,n)}function z(t,n){for(let e=1,r=n.length;et.pop().map((([n,e])=>[...t,n,e]))));return t}function $(t,n,...e){return F(t,k,n,e)}function D(t,n,...e){return F(t,Array.from,n,e)}function R(t){if(1!==t.length)throw new Error("duplicate key");return t[0]}function F(t,n,e,r){return function t(i,o){if(o>=r.length)return e(i);const a=new InternMap,u=r[o++];let c=-1;for(const t of i){const n=u(t,++c,i),e=a.get(n);e?e.push(t):a.set(n,[t])}for(const[n,e]of a)a.set(n,t(e,o));return n(a)}(t,0)}function q(t,n){return Array.from(n,(n=>t[n]))}function U(t,...n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");t=Array.from(t);let[e]=n;if(e&&2!==e.length||n.length>1){const r=Uint32Array.from(t,((t,n)=>n));return n.length>1?(n=n.map((n=>t.map(n))),r.sort(((t,e)=>{for(const r of n){const n=O(r[t],r[e]);if(n)return n}}))):(e=t.map(e),r.sort(((t,n)=>O(e[t],e[n])))),q(t,r)}return t.sort(I(e))}function I(t=n){if(t===n)return O;if("function"!=typeof t)throw new TypeError("compare is not a function");return(n,e)=>{const r=t(n,e);return r||0===r?r:(0===t(e,e))-(0===t(n,n))}}function O(t,n){return(null==t||!(t>=t))-(null==n||!(n>=n))||(tn?1:0)}var B=Array.prototype.slice;function Y(t){return()=>t}const L=Math.sqrt(50),j=Math.sqrt(10),H=Math.sqrt(2);function X(t,n,e){const r=(n-t)/Math.max(0,e),i=Math.floor(Math.log10(r)),o=r/Math.pow(10,i),a=o>=L?10:o>=j?5:o>=H?2:1;let u,c,f;return i<0?(f=Math.pow(10,-i)/a,u=Math.round(t*f),c=Math.round(n*f),u/fn&&--c,f=-f):(f=Math.pow(10,i)*a,u=Math.round(t/f),c=Math.round(n/f),u*fn&&--c),c0))return[];if((t=+t)===(n=+n))return[t];const r=n=i))return[];const u=o-i+1,c=new Array(u);if(r)if(a<0)for(let t=0;t0?(t=Math.floor(t/i)*i,n=Math.ceil(n/i)*i):i<0&&(t=Math.ceil(t*i)/i,n=Math.floor(n*i)/i),r=i}}function K(t){return Math.max(1,Math.ceil(Math.log(v(t))/Math.LN2)+1)}function Q(){var t=k,n=M,e=K;function r(r){Array.isArray(r)||(r=Array.from(r));var i,o,a,u=r.length,c=new Array(u);for(i=0;i=h)if(t>=h&&n===M){const t=V(l,h,e);isFinite(t)&&(t>0?h=(Math.floor(h/t)+1)*t:t<0&&(h=(Math.ceil(h*-t)+1)/-t))}else d.pop()}for(var p=d.length,g=0,y=p;d[g]<=l;)++g;for(;d[y-1]>h;)--y;(g||y0?d[i-1]:l,v.x1=i0)for(i=0;i=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function tt(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e=o)&&(e=o,r=i);return r}function nt(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function et(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e>n||void 0===e&&n>=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e>o||void 0===e&&o>=o)&&(e=o,r=i);return r}function rt(t,n,e=0,r=1/0,i){if(n=Math.floor(n),e=Math.floor(Math.max(0,e)),r=Math.floor(Math.min(t.length-1,r)),!(e<=n&&n<=r))return t;for(i=void 0===i?O:I(i);r>e;){if(r-e>600){const o=r-e+1,a=n-e+1,u=Math.log(o),c=.5*Math.exp(2*u/3),f=.5*Math.sqrt(u*c*(o-c)/o)*(a-o/2<0?-1:1);rt(t,n,Math.max(e,Math.floor(n-a*c/o+f)),Math.min(r,Math.floor(n+(o-a)*c/o+f)),i)}const o=t[n];let a=e,u=r;for(it(t,e,n),i(t[r],o)>0&&it(t,e,r);a0;)--u}0===i(t[e],o)?it(t,e,u):(++u,it(t,u,r)),u<=n&&(e=u+1),n<=u&&(r=u-1)}return t}function it(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function ot(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)>0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)>0:0===e(n,n))&&(r=n,i=!0);return r}function at(t,n,e){if(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r=+r)>=r&&(yield r)}}(t,e)),(r=t.length)&&!isNaN(n=+n)){if(n<=0||r<2)return nt(t);if(n>=1)return J(t);var r,i=(r-1)*n,o=Math.floor(i),a=J(rt(t,o).subarray(0,o+1));return a+(nt(t.subarray(o+1))-a)*(i-o)}}function ut(t,n,e=o){if((r=t.length)&&!isNaN(n=+n)){if(n<=0||r<2)return+e(t[0],0,t);if(n>=1)return+e(t[r-1],r-1,t);var r,i=(r-1)*n,a=Math.floor(i),u=+e(t[a],a,t);return u+(+e(t[a+1],a+1,t)-u)*(i-a)}}function ct(t,n,e=o){if(!isNaN(n=+n)){if(r=Float64Array.from(t,((n,r)=>o(e(t[r],r,t)))),n<=0)return et(r);if(n>=1)return tt(r);var r,i=Uint32Array.from(t,((t,n)=>n)),a=r.length-1,u=Math.floor(a*n);return rt(i,u,0,a,((t,n)=>O(r[t],r[n]))),(u=ot(i.subarray(0,u+1),(t=>r[t])))>=0?u:-1}}function ft(t){return Array.from(function*(t){for(const n of t)yield*n}(t))}function st(t,n){return[t,n]}function lt(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r+t(n)}function kt(t,n){return n=Math.max(0,t.bandwidth()-2*n)/2,t.round()&&(n=Math.round(n)),e=>+t(e)+n}function Ct(){return!this.__axis}function Pt(t,n){var e=[],r=null,i=null,o=6,a=6,u=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,f=t===xt||t===Tt?-1:1,s=t===Tt||t===wt?"x":"y",l=t===xt||t===Mt?St:Et;function h(h){var d=null==r?n.ticks?n.ticks.apply(n,e):n.domain():r,p=null==i?n.tickFormat?n.tickFormat.apply(n,e):mt:i,g=Math.max(o,0)+u,y=n.range(),v=+y[0]+c,_=+y[y.length-1]+c,b=(n.bandwidth?kt:Nt)(n.copy(),c),m=h.selection?h.selection():h,x=m.selectAll(".domain").data([null]),w=m.selectAll(".tick").data(d,n).order(),M=w.exit(),T=w.enter().append("g").attr("class","tick"),A=w.select("line"),S=w.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),w=w.merge(T),A=A.merge(T.append("line").attr("stroke","currentColor").attr(s+"2",f*o)),S=S.merge(T.append("text").attr("fill","currentColor").attr(s,f*g).attr("dy",t===xt?"0em":t===Mt?"0.71em":"0.32em")),h!==m&&(x=x.transition(h),w=w.transition(h),A=A.transition(h),S=S.transition(h),M=M.transition(h).attr("opacity",At).attr("transform",(function(t){return isFinite(t=b(t))?l(t+c):this.getAttribute("transform")})),T.attr("opacity",At).attr("transform",(function(t){var n=this.parentNode.__axis;return l((n&&isFinite(n=n(t))?n:b(t))+c)}))),M.remove(),x.attr("d",t===Tt||t===wt?a?"M"+f*a+","+v+"H"+c+"V"+_+"H"+f*a:"M"+c+","+v+"V"+_:a?"M"+v+","+f*a+"V"+c+"H"+_+"V"+f*a:"M"+v+","+c+"H"+_),w.attr("opacity",1).attr("transform",(function(t){return l(b(t)+c)})),A.attr(s+"2",f*o),S.attr(s,f*g).text(p),m.filter(Ct).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===wt?"start":t===Tt?"end":"middle"),m.each((function(){this.__axis=b}))}return h.scale=function(t){return arguments.length?(n=t,h):n},h.ticks=function(){return e=Array.from(arguments),h},h.tickArguments=function(t){return arguments.length?(e=null==t?[]:Array.from(t),h):e.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:Array.from(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(o=a=+t,h):o},h.tickSizeInner=function(t){return arguments.length?(o=+t,h):o},h.tickSizeOuter=function(t){return arguments.length?(a=+t,h):a},h.tickPadding=function(t){return arguments.length?(u=+t,h):u},h.offset=function(t){return arguments.length?(c=+t,h):c},h}var zt={value:()=>{}};function $t(){for(var t,n=0,e=arguments.length,r={};n=0&&(n=t.slice(e+1),t=t.slice(0,e)),t&&!r.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))),a=-1,u=o.length;if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++a0)for(var e,r,i=new Array(e),o=0;o=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Ut.hasOwnProperty(n)?{space:Ut[n],local:t}:t}function Ot(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===qt&&n.documentElement.namespaceURI===qt?n.createElement(t):n.createElementNS(e,t)}}function Bt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Yt(t){var n=It(t);return(n.local?Bt:Ot)(n)}function Lt(){}function jt(t){return null==t?Lt:function(){return this.querySelector(t)}}function Ht(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function Xt(){return[]}function Gt(t){return null==t?Xt:function(){return this.querySelectorAll(t)}}function Vt(t){return function(){return this.matches(t)}}function Wt(t){return function(n){return n.matches(t)}}var Zt=Array.prototype.find;function Kt(){return this.firstElementChild}var Qt=Array.prototype.filter;function Jt(){return Array.from(this.children)}function tn(t){return new Array(t.length)}function nn(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function en(t,n,e,r,i,o){for(var a,u=0,c=n.length,f=o.length;un?1:t>=n?0:NaN}function cn(t){return function(){this.removeAttribute(t)}}function fn(t){return function(){this.removeAttributeNS(t.space,t.local)}}function sn(t,n){return function(){this.setAttribute(t,n)}}function ln(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function hn(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function dn(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function pn(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function gn(t){return function(){this.style.removeProperty(t)}}function yn(t,n,e){return function(){this.style.setProperty(t,n,e)}}function vn(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function _n(t,n){return t.style.getPropertyValue(n)||pn(t).getComputedStyle(t,null).getPropertyValue(n)}function bn(t){return function(){delete this[t]}}function mn(t,n){return function(){this[t]=n}}function xn(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function wn(t){return t.trim().split(/^|\s+/)}function Mn(t){return t.classList||new Tn(t)}function Tn(t){this._node=t,this._names=wn(t.getAttribute("class")||"")}function An(t,n){for(var e=Mn(t),r=-1,i=n.length;++r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Gn=[null];function Vn(t,n){this._groups=t,this._parents=n}function Wn(){return new Vn([[document.documentElement]],Gn)}function Zn(t){return"string"==typeof t?new Vn([[document.querySelector(t)]],[document.documentElement]):new Vn([[t]],Gn)}Vn.prototype=Wn.prototype={constructor:Vn,select:function(t){"function"!=typeof t&&(t=jt(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=m&&(m=b+1);!(_=y[m])&&++m=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=un);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?gn:"function"==typeof n?vn:yn)(t,n,null==e?"":e)):_n(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?bn:"function"==typeof n?xn:mn)(t,n)):this.node()[t]},classed:function(t,n){var e=wn(t+"");if(arguments.length<2){for(var r=Mn(this.node()),i=-1,o=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}(t+""),a=o.length;if(!(arguments.length<2)){for(u=n?Ln:Yn,r=0;r()=>t;function fe(t,{sourceEvent:n,subject:e,target:r,identifier:i,active:o,x:a,y:u,dx:c,dy:f,dispatch:s}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},subject:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:a,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:s}})}function se(t){return!t.ctrlKey&&!t.button}function le(){return this.parentNode}function he(t,n){return null==n?{x:t.x,y:t.y}:n}function de(){return navigator.maxTouchPoints||"ontouchstart"in this}function pe(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function ge(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function ye(){}fe.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var ve=.7,_e=1/ve,be="\\s*([+-]?\\d+)\\s*",me="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",xe="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",we=/^#([0-9a-f]{3,8})$/,Me=new RegExp(`^rgb\\(${be},${be},${be}\\)$`),Te=new RegExp(`^rgb\\(${xe},${xe},${xe}\\)$`),Ae=new RegExp(`^rgba\\(${be},${be},${be},${me}\\)$`),Se=new RegExp(`^rgba\\(${xe},${xe},${xe},${me}\\)$`),Ee=new RegExp(`^hsl\\(${me},${xe},${xe}\\)$`),Ne=new RegExp(`^hsla\\(${me},${xe},${xe},${me}\\)$`),ke={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function Ce(){return this.rgb().formatHex()}function Pe(){return this.rgb().formatRgb()}function ze(t){var n,e;return t=(t+"").trim().toLowerCase(),(n=we.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?$e(n):3===e?new qe(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?De(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?De(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=Me.exec(t))?new qe(n[1],n[2],n[3],1):(n=Te.exec(t))?new qe(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=Ae.exec(t))?De(n[1],n[2],n[3],n[4]):(n=Se.exec(t))?De(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=Ee.exec(t))?Le(n[1],n[2]/100,n[3]/100,1):(n=Ne.exec(t))?Le(n[1],n[2]/100,n[3]/100,n[4]):ke.hasOwnProperty(t)?$e(ke[t]):"transparent"===t?new qe(NaN,NaN,NaN,0):null}function $e(t){return new qe(t>>16&255,t>>8&255,255&t,1)}function De(t,n,e,r){return r<=0&&(t=n=e=NaN),new qe(t,n,e,r)}function Re(t){return t instanceof ye||(t=ze(t)),t?new qe((t=t.rgb()).r,t.g,t.b,t.opacity):new qe}function Fe(t,n,e,r){return 1===arguments.length?Re(t):new qe(t,n,e,null==r?1:r)}function qe(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Ue(){return`#${Ye(this.r)}${Ye(this.g)}${Ye(this.b)}`}function Ie(){const t=Oe(this.opacity);return`${1===t?"rgb(":"rgba("}${Be(this.r)}, ${Be(this.g)}, ${Be(this.b)}${1===t?")":`, ${t})`}`}function Oe(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function Be(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function Ye(t){return((t=Be(t))<16?"0":"")+t.toString(16)}function Le(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Xe(t,n,e,r)}function je(t){if(t instanceof Xe)return new Xe(t.h,t.s,t.l,t.opacity);if(t instanceof ye||(t=ze(t)),!t)return new Xe;if(t instanceof Xe)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,c=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e0&&c<1?0:a,new Xe(a,u,c,t.opacity)}function He(t,n,e,r){return 1===arguments.length?je(t):new Xe(t,n,e,null==r?1:r)}function Xe(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Ge(t){return(t=(t||0)%360)<0?t+360:t}function Ve(t){return Math.max(0,Math.min(1,t||0))}function We(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}pe(ye,ze,{copy(t){return Object.assign(new this.constructor,this,t)},displayable(){return this.rgb().displayable()},hex:Ce,formatHex:Ce,formatHex8:function(){return this.rgb().formatHex8()},formatHsl:function(){return je(this).formatHsl()},formatRgb:Pe,toString:Pe}),pe(qe,Fe,ge(ye,{brighter(t){return t=null==t?_e:Math.pow(_e,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=null==t?ve:Math.pow(ve,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new qe(Be(this.r),Be(this.g),Be(this.b),Oe(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Ue,formatHex:Ue,formatHex8:function(){return`#${Ye(this.r)}${Ye(this.g)}${Ye(this.b)}${Ye(255*(isNaN(this.opacity)?1:this.opacity))}`},formatRgb:Ie,toString:Ie})),pe(Xe,He,ge(ye,{brighter(t){return t=null==t?_e:Math.pow(_e,t),new Xe(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=null==t?ve:Math.pow(ve,t),new Xe(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new qe(We(t>=240?t-240:t+120,i,r),We(t,i,r),We(t<120?t+240:t-120,i,r),this.opacity)},clamp(){return new Xe(Ge(this.h),Ve(this.s),Ve(this.l),Oe(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=Oe(this.opacity);return`${1===t?"hsl(":"hsla("}${Ge(this.h)}, ${100*Ve(this.s)}%, ${100*Ve(this.l)}%${1===t?")":`, ${t})`}`}}));const Ze=Math.PI/180,Ke=180/Math.PI,Qe=.96422,Je=1,tr=.82521,nr=4/29,er=6/29,rr=3*er*er,ir=er*er*er;function or(t){if(t instanceof ur)return new ur(t.l,t.a,t.b,t.opacity);if(t instanceof pr)return gr(t);t instanceof qe||(t=Re(t));var n,e,r=lr(t.r),i=lr(t.g),o=lr(t.b),a=cr((.2225045*r+.7168786*i+.0606169*o)/Je);return r===i&&i===o?n=e=a:(n=cr((.4360747*r+.3850649*i+.1430804*o)/Qe),e=cr((.0139322*r+.0971045*i+.7141733*o)/tr)),new ur(116*a-16,500*(n-a),200*(a-e),t.opacity)}function ar(t,n,e,r){return 1===arguments.length?or(t):new ur(t,n,e,null==r?1:r)}function ur(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function cr(t){return t>ir?Math.pow(t,1/3):t/rr+nr}function fr(t){return t>er?t*t*t:rr*(t-nr)}function sr(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function lr(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function hr(t){if(t instanceof pr)return new pr(t.h,t.c,t.l,t.opacity);if(t instanceof ur||(t=or(t)),0===t.a&&0===t.b)return new pr(NaN,0=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r()=>t;function Cr(t,n){return function(e){return t+e*n}}function Pr(t,n){var e=n-t;return e?Cr(t,e>180||e<-180?e-360*Math.round(e/360):e):kr(isNaN(t)?n:t)}function zr(t){return 1==(t=+t)?$r:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):kr(isNaN(n)?e:n)}}function $r(t,n){var e=n-t;return e?Cr(t,e):kr(isNaN(t)?n:t)}var Dr=function t(n){var e=zr(n);function r(t,n){var r=e((t=Fe(t)).r,(n=Fe(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=$r(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function Rr(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;eo&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,c.push({i:a,x:Yr(e,r)})),o=Hr.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Yr(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Yr(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,c),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:Yr(t,e)},{i:u-2,x:Yr(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,c),o=a=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(void 0,t),n=n._next;--yi}function Ci(){xi=(mi=Mi.now())+wi,yi=vi=0;try{ki()}finally{yi=0,function(){var t,n,e=pi,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:pi=n);gi=t,zi(r)}(),xi=0}}function Pi(){var t=Mi.now(),n=t-mi;n>bi&&(wi-=n,mi=t)}function zi(t){yi||(vi&&(vi=clearTimeout(vi)),t-xi>24?(t<1/0&&(vi=setTimeout(Ci,t-Mi.now()-wi)),_i&&(_i=clearInterval(_i))):(_i||(mi=Mi.now(),_i=setInterval(Pi,bi)),yi=1,Ti(Ci)))}function $i(t,n,e){var r=new Ei;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}Ei.prototype=Ni.prototype={constructor:Ei,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?Ai():+e)+(null==n?0:+n),this._next||gi===this||(gi?gi._next=this:pi=this,gi=this),this._call=t,this._time=e,zi()},stop:function(){this._call&&(this._call=null,this._time=1/0,zi())}};var Di=$t("start","end","cancel","interrupt"),Ri=[],Fi=0,qi=1,Ui=2,Ii=3,Oi=4,Bi=5,Yi=6;function Li(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=qi,e.timer.restart(a,e.delay,e.time),e.delay<=t&&a(t-e.delay)}function a(o){var f,s,l,h;if(e.state!==qi)return c();for(f in i)if((h=i[f]).name===e.name){if(h.state===Ii)return $i(a);h.state===Oi?(h.state=Yi,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[f]):+fFi)throw new Error("too late; already scheduled");return e}function Hi(t,n){var e=Xi(t,n);if(e.state>Ii)throw new Error("too late; already running");return e}function Xi(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Gi(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>Ui&&e.state=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?ji:Hi;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}(e,t,n))},attr:function(t,n){var e=It(t),r="transform"===e?ni:Ki;return this.attrTween(t,"function"==typeof n?(e.local?ro:eo)(e,r,Zi(this,"attr."+t,n)):null==n?(e.local?Ji:Qi)(e):(e.local?no:to)(e,r,n))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=It(t);return this.tween(e,(r.local?io:oo)(r,n))},style:function(t,n,e){var r="transform"==(t+="")?ti:Ki;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=_n(this,t),a=(this.style.removeProperty(t),_n(this,t));return o===a?null:o===e&&a===r?i:i=n(e=o,r=a)}}(t,r)).on("end.style."+t,lo(t)):"function"==typeof n?this.styleTween(t,function(t,n,e){var r,i,o;return function(){var a=_n(this,t),u=e(this),c=u+"";return null==u&&(this.style.removeProperty(t),c=u=_n(this,t)),a===c?null:a===r&&c===i?o:(i=c,o=n(r=a,u))}}(t,r,Zi(this,"style."+t,n))).each(function(t,n){var e,r,i,o,a="style."+n,u="end."+a;return function(){var c=Hi(this,t),f=c.on,s=null==c.value[a]?o||(o=lo(n)):void 0;f===e&&i===s||(r=(e=f).copy()).on(u,i=s),c.on=r}}(this._id,t)):this.styleTween(t,function(t,n,e){var r,i,o=e+"";return function(){var a=_n(this,t);return a===o?null:a===r?i:i=n(r=a,e)}}(t,r,n),e).on("end.style."+t,null)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&function(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}(t,o,e)),r}return o._value=n,o}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(Zi(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var n="text";if(arguments.length<1)return(n=this.tween(n))&&n._value;if(null==t)return this.tween(n,null);if("function"!=typeof t)throw new Error;return this.tween(n,function(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&function(t){return function(n){this.textContent=t.call(this,n)}}(r)),n}return r._value=t,r}(t))},remove:function(){return this.on("end.remove",function(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}(this._id))},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=Xi(this.node(),e).tween,o=0,a=i.length;o()=>t;function Qo(t,{sourceEvent:n,target:e,selection:r,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},selection:{value:r,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function Jo(t){t.preventDefault(),t.stopImmediatePropagation()}var ta={name:"drag"},na={name:"space"},ea={name:"handle"},ra={name:"center"};const{abs:ia,max:oa,min:aa}=Math;function ua(t){return[+t[0],+t[1]]}function ca(t){return[ua(t[0]),ua(t[1])]}var fa={name:"x",handles:["w","e"].map(va),input:function(t,n){return null==t?null:[[+t[0],n[0][1]],[+t[1],n[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},sa={name:"y",handles:["n","s"].map(va),input:function(t,n){return null==t?null:[[n[0][0],+t[0]],[n[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},la={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(va),input:function(t){return null==t?null:ca(t)},output:function(t){return t}},ha={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},da={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},pa={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},ga={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},ya={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function va(t){return{type:t}}function _a(t){return!t.ctrlKey&&!t.button}function ba(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function ma(){return navigator.maxTouchPoints||"ontouchstart"in this}function xa(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function wa(t){var n,e=ba,r=_a,i=ma,o=!0,a=$t("start","brush","end"),u=6;function c(n){var e=n.property("__brush",g).selectAll(".overlay").data([va("overlay")]);e.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",ha.overlay).merge(e).each((function(){var t=xa(this).extent;Zn(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),n.selectAll(".selection").data([va("selection")]).enter().append("rect").attr("class","selection").attr("cursor",ha.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=n.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return ha[t.type]})),n.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",h).filter(i).on("touchstart.brush",h).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(){var t=Zn(this),n=xa(this).selection;n?(t.selectAll(".selection").style("display",null).attr("x",n[0][0]).attr("y",n[0][1]).attr("width",n[1][0]-n[0][0]).attr("height",n[1][1]-n[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?n[1][0]-u/2:n[0][0]-u/2})).attr("y",(function(t){return"s"===t.type[0]?n[1][1]-u/2:n[0][1]-u/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?n[1][0]-n[0][0]+u:u})).attr("height",(function(t){return"e"===t.type||"w"===t.type?n[1][1]-n[0][1]+u:u}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function s(t,n,e){var r=t.__brush.emitter;return!r||e&&r.clean?new l(t,n,e):r}function l(t,n,e){this.that=t,this.args=n,this.state=t.__brush,this.active=0,this.clean=e}function h(e){if((!n||e.touches)&&r.apply(this,arguments)){var i,a,u,c,l,h,d,p,g,y,v,_=this,b=e.target.__data__.type,m="selection"===(o&&e.metaKey?b="overlay":b)?ta:o&&e.altKey?ra:ea,x=t===sa?null:ga[b],w=t===fa?null:ya[b],M=xa(_),T=M.extent,A=M.selection,S=T[0][0],E=T[0][1],N=T[1][0],k=T[1][1],C=0,P=0,z=x&&w&&o&&e.shiftKey,$=Array.from(e.touches||[e],(t=>{const n=t.identifier;return(t=ne(t,_)).point0=t.slice(),t.identifier=n,t}));Gi(_);var D=s(_,arguments,!0).beforestart();if("overlay"===b){A&&(g=!0);const n=[$[0],$[1]||$[0]];M.selection=A=[[i=t===sa?S:aa(n[0][0],n[1][0]),u=t===fa?E:aa(n[0][1],n[1][1])],[l=t===sa?N:oa(n[0][0],n[1][0]),d=t===fa?k:oa(n[0][1],n[1][1])]],$.length>1&&I(e)}else i=A[0][0],u=A[0][1],l=A[1][0],d=A[1][1];a=i,c=u,h=l,p=d;var R=Zn(_).attr("pointer-events","none"),F=R.selectAll(".overlay").attr("cursor",ha[b]);if(e.touches)D.moved=U,D.ended=O;else{var q=Zn(e.view).on("mousemove.brush",U,!0).on("mouseup.brush",O,!0);o&&q.on("keydown.brush",(function(t){switch(t.keyCode){case 16:z=x&&w;break;case 18:m===ea&&(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=ra,I(t));break;case 32:m!==ea&&m!==ra||(x<0?l=h-C:x>0&&(i=a-C),w<0?d=p-P:w>0&&(u=c-P),m=na,F.attr("cursor",ha.selection),I(t));break;default:return}Jo(t)}),!0).on("keyup.brush",(function(t){switch(t.keyCode){case 16:z&&(y=v=z=!1,I(t));break;case 18:m===ra&&(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=ea,I(t));break;case 32:m===na&&(t.altKey?(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=ra):(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=ea),F.attr("cursor",ha[b]),I(t));break;default:return}Jo(t)}),!0),ae(e.view)}f.call(_),D.start(e,m.name)}function U(t){for(const n of t.changedTouches||[t])for(const t of $)t.identifier===n.identifier&&(t.cur=ne(n,_));if(z&&!y&&!v&&1===$.length){const t=$[0];ia(t.cur[0]-t[0])>ia(t.cur[1]-t[1])?v=!0:y=!0}for(const t of $)t.cur&&(t[0]=t.cur[0],t[1]=t.cur[1]);g=!0,Jo(t),I(t)}function I(t){const n=$[0],e=n.point0;var r;switch(C=n[0]-e[0],P=n[1]-e[1],m){case na:case ta:x&&(C=oa(S-i,aa(N-l,C)),a=i+C,h=l+C),w&&(P=oa(E-u,aa(k-d,P)),c=u+P,p=d+P);break;case ea:$[1]?(x&&(a=oa(S,aa(N,$[0][0])),h=oa(S,aa(N,$[1][0])),x=1),w&&(c=oa(E,aa(k,$[0][1])),p=oa(E,aa(k,$[1][1])),w=1)):(x<0?(C=oa(S-i,aa(N-i,C)),a=i+C,h=l):x>0&&(C=oa(S-l,aa(N-l,C)),a=i,h=l+C),w<0?(P=oa(E-u,aa(k-u,P)),c=u+P,p=d):w>0&&(P=oa(E-d,aa(k-d,P)),c=u,p=d+P));break;case ra:x&&(a=oa(S,aa(N,i-C*x)),h=oa(S,aa(N,l+C*x))),w&&(c=oa(E,aa(k,u-P*w)),p=oa(E,aa(k,d+P*w)))}ht+e))}function za(t,n){var e=0,r=null,i=null,o=null;function a(a){var u,c=a.length,f=new Array(c),s=Pa(0,c),l=new Array(c*c),h=new Array(c),d=0;a=Float64Array.from({length:c*c},n?(t,n)=>a[n%c][n/c|0]:(t,n)=>a[n/c|0][n%c]);for(let n=0;nr(f[t],f[n])));for(const e of s){const r=n;if(t){const t=Pa(1+~c,c).filter((t=>t<0?a[~t*c+e]:a[e*c+t]));i&&t.sort(((t,n)=>i(t<0?-a[~t*c+e]:a[e*c+t],n<0?-a[~n*c+e]:a[e*c+n])));for(const r of t)if(r<0){(l[~r*c+e]||(l[~r*c+e]={source:null,target:null})).target={index:e,startAngle:n,endAngle:n+=a[~r*c+e]*d,value:a[~r*c+e]}}else{(l[e*c+r]||(l[e*c+r]={source:null,target:null})).source={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]}}h[e]={index:e,startAngle:r,endAngle:n,value:f[e]}}else{const t=Pa(0,c).filter((t=>a[e*c+t]||a[t*c+e]));i&&t.sort(((t,n)=>i(a[e*c+t],a[e*c+n])));for(const r of t){let t;if(e=0))throw new Error(`invalid digits: ${t}`);if(n>15)return qa;const e=10**n;return function(t){this._+=t[0];for(let n=1,r=t.length;nRa)if(Math.abs(s*u-c*f)>Ra&&i){let h=e-o,d=r-a,p=u*u+c*c,g=h*h+d*d,y=Math.sqrt(p),v=Math.sqrt(l),_=i*Math.tan(($a-Math.acos((p+l-g)/(2*y*v)))/2),b=_/v,m=_/y;Math.abs(b-1)>Ra&&this._append`L${t+b*f},${n+b*s}`,this._append`A${i},${i},0,0,${+(s*h>f*d)},${this._x1=t+m*u},${this._y1=n+m*c}`}else this._append`L${this._x1=t},${this._y1=n}`;else;}arc(t,n,e,r,i,o){if(t=+t,n=+n,o=!!o,(e=+e)<0)throw new Error(`negative radius: ${e}`);let a=e*Math.cos(r),u=e*Math.sin(r),c=t+a,f=n+u,s=1^o,l=o?r-i:i-r;null===this._x1?this._append`M${c},${f}`:(Math.abs(this._x1-c)>Ra||Math.abs(this._y1-f)>Ra)&&this._append`L${c},${f}`,e&&(l<0&&(l=l%Da+Da),l>Fa?this._append`A${e},${e},0,1,${s},${t-a},${n-u}A${e},${e},0,1,${s},${this._x1=c},${this._y1=f}`:l>Ra&&this._append`A${e},${e},0,${+(l>=$a)},${s},${this._x1=t+e*Math.cos(i)},${this._y1=n+e*Math.sin(i)}`)}rect(t,n,e,r){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${e=+e}v${+r}h${-e}Z`}toString(){return this._}};function Ia(){return new Ua}Ia.prototype=Ua.prototype;var Oa=Array.prototype.slice;function Ba(t){return function(){return t}}function Ya(t){return t.source}function La(t){return t.target}function ja(t){return t.radius}function Ha(t){return t.startAngle}function Xa(t){return t.endAngle}function Ga(){return 0}function Va(){return 10}function Wa(t){var n=Ya,e=La,r=ja,i=ja,o=Ha,a=Xa,u=Ga,c=null;function f(){var f,s=n.apply(this,arguments),l=e.apply(this,arguments),h=u.apply(this,arguments)/2,d=Oa.call(arguments),p=+r.apply(this,(d[0]=s,d)),g=o.apply(this,d)-Ea,y=a.apply(this,d)-Ea,v=+i.apply(this,(d[0]=l,d)),_=o.apply(this,d)-Ea,b=a.apply(this,d)-Ea;if(c||(c=f=Ia()),h>Ca&&(Ma(y-g)>2*h+Ca?y>g?(g+=h,y-=h):(g-=h,y+=h):g=y=(g+y)/2,Ma(b-_)>2*h+Ca?b>_?(_+=h,b-=h):(_-=h,b+=h):_=b=(_+b)/2),c.moveTo(p*Ta(g),p*Aa(g)),c.arc(0,0,p,g,y),g!==_||y!==b)if(t){var m=v-+t.apply(this,arguments),x=(_+b)/2;c.quadraticCurveTo(0,0,m*Ta(_),m*Aa(_)),c.lineTo(v*Ta(x),v*Aa(x)),c.lineTo(m*Ta(b),m*Aa(b))}else c.quadraticCurveTo(0,0,v*Ta(_),v*Aa(_)),c.arc(0,0,v,_,b);if(c.quadraticCurveTo(0,0,p*Ta(g),p*Aa(g)),c.closePath(),f)return c=null,f+""||null}return t&&(f.headRadius=function(n){return arguments.length?(t="function"==typeof n?n:Ba(+n),f):t}),f.radius=function(t){return arguments.length?(r=i="function"==typeof t?t:Ba(+t),f):r},f.sourceRadius=function(t){return arguments.length?(r="function"==typeof t?t:Ba(+t),f):r},f.targetRadius=function(t){return arguments.length?(i="function"==typeof t?t:Ba(+t),f):i},f.startAngle=function(t){return arguments.length?(o="function"==typeof t?t:Ba(+t),f):o},f.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:Ba(+t),f):a},f.padAngle=function(t){return arguments.length?(u="function"==typeof t?t:Ba(+t),f):u},f.source=function(t){return arguments.length?(n=t,f):n},f.target=function(t){return arguments.length?(e=t,f):e},f.context=function(t){return arguments.length?(c=null==t?null:t,f):c},f}var Za=Array.prototype.slice;function Ka(t,n){return t-n}var Qa=t=>()=>t;function Ja(t,n){for(var e,r=-1,i=n.length;++rr!=d>r&&e<(h-f)*(r-s)/(d-s)+f&&(i=-i)}return i}function nu(t,n,e){var r,i,o,a;return function(t,n,e){return(n[0]-t[0])*(e[1]-t[1])==(e[0]-t[0])*(n[1]-t[1])}(t,n,e)&&(i=t[r=+(t[0]===n[0])],o=e[r],a=n[r],i<=o&&o<=a||a<=o&&o<=i)}function eu(){}var ru=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function iu(){var t=1,n=1,e=K,r=u;function i(t){var n=e(t);if(Array.isArray(n))n=n.slice().sort(Ka);else{const e=M(t,ou);for(n=G(...Z(e[0],e[1],n),n);n[n.length-1]>=e[1];)n.pop();for(;n[1]o(t,n)))}function o(e,i){const o=null==i?NaN:+i;if(isNaN(o))throw new Error(`invalid value: ${i}`);var u=[],c=[];return function(e,r,i){var o,u,c,f,s,l,h=new Array,d=new Array;o=u=-1,f=au(e[0],r),ru[f<<1].forEach(p);for(;++o=r,ru[s<<2].forEach(p);for(;++o0?u.push([t]):c.push(t)})),c.forEach((function(t){for(var n,e=0,r=u.length;e0&&o0&&a=0&&o>=0))throw new Error("invalid size");return t=r,n=o,i},i.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Array.isArray(t)?Qa(Za.call(t)):Qa(t),i):e},i.smooth=function(t){return arguments.length?(r=t?u:eu,i):r===u},i}function ou(t){return isFinite(t)?t:NaN}function au(t,n){return null!=t&&+t>=n}function uu(t){return null==t||isNaN(t=+t)?-1/0:t}function cu(t,n,e,r){const i=r-n,o=e-n,a=isFinite(i)||isFinite(o)?i/o:Math.sign(i)/Math.sign(o);return isNaN(a)?t:t+a-.5}function fu(t){return t[0]}function su(t){return t[1]}function lu(){return 1}const hu=134217729,du=33306690738754706e-32;function pu(t,n,e,r,i){let o,a,u,c,f=n[0],s=r[0],l=0,h=0;s>f==s>-f?(o=f,f=n[++l]):(o=s,s=r[++h]);let d=0;if(lf==s>-f?(a=f+o,u=o-(a-f),f=n[++l]):(a=s+o,u=o-(a-s),s=r[++h]),o=a,0!==u&&(i[d++]=u);lf==s>-f?(a=o+f,c=a-o,u=o-(a-c)+(f-c),f=n[++l]):(a=o+s,c=a-o,u=o-(a-c)+(s-c),s=r[++h]),o=a,0!==u&&(i[d++]=u);for(;l=33306690738754716e-32*f?c:-function(t,n,e,r,i,o,a){let u,c,f,s,l,h,d,p,g,y,v,_,b,m,x,w,M,T;const A=t-i,S=e-i,E=n-o,N=r-o;m=A*N,h=hu*A,d=h-(h-A),p=A-d,h=hu*N,g=h-(h-N),y=N-g,x=p*y-(m-d*g-p*g-d*y),w=E*S,h=hu*E,d=h-(h-E),p=E-d,h=hu*S,g=h-(h-S),y=S-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,_u[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,_u[1]=b-(v+l)+(l-w),T=_+v,l=T-_,_u[2]=_-(T-l)+(v-l),_u[3]=T;let k=function(t,n){let e=n[0];for(let r=1;r=C||-k>=C)return k;if(l=t-A,u=t-(A+l)+(l-i),l=e-S,f=e-(S+l)+(l-i),l=n-E,c=n-(E+l)+(l-o),l=r-N,s=r-(N+l)+(l-o),0===u&&0===c&&0===f&&0===s)return k;if(C=vu*a+du*Math.abs(k),k+=A*s+N*u-(E*f+S*c),k>=C||-k>=C)return k;m=u*N,h=hu*u,d=h-(h-u),p=u-d,h=hu*N,g=h-(h-N),y=N-g,x=p*y-(m-d*g-p*g-d*y),w=c*S,h=hu*c,d=h-(h-c),p=c-d,h=hu*S,g=h-(h-S),y=S-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const P=pu(4,_u,4,wu,bu);m=A*s,h=hu*A,d=h-(h-A),p=A-d,h=hu*s,g=h-(h-s),y=s-g,x=p*y-(m-d*g-p*g-d*y),w=E*f,h=hu*E,d=h-(h-E),p=E-d,h=hu*f,g=h-(h-f),y=f-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const z=pu(P,bu,4,wu,mu);m=u*s,h=hu*u,d=h-(h-u),p=u-d,h=hu*s,g=h-(h-s),y=s-g,x=p*y-(m-d*g-p*g-d*y),w=c*f,h=hu*c,d=h-(h-c),p=c-d,h=hu*f,g=h-(h-f),y=f-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const $=pu(z,mu,4,wu,xu);return xu[$-1]}(t,n,e,r,i,o,f)}const Tu=Math.pow(2,-52),Au=new Uint32Array(512);class Su{static from(t,n=zu,e=$u){const r=t.length,i=new Float64Array(2*r);for(let o=0;o>1;if(n>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const e=Math.max(2*n-5,0);this._triangles=new Uint32Array(3*e),this._halfedges=new Int32Array(3*e),this._hashSize=Math.ceil(Math.sqrt(n)),this._hullPrev=new Uint32Array(n),this._hullNext=new Uint32Array(n),this._hullTri=new Uint32Array(n),this._hullHash=new Int32Array(this._hashSize),this._ids=new Uint32Array(n),this._dists=new Float64Array(n),this.update()}update(){const{coords:t,_hullPrev:n,_hullNext:e,_hullTri:r,_hullHash:i}=this,o=t.length>>1;let a=1/0,u=1/0,c=-1/0,f=-1/0;for(let n=0;nc&&(c=e),r>f&&(f=r),this._ids[n]=n}const s=(a+c)/2,l=(u+f)/2;let h,d,p;for(let n=0,e=1/0;n0&&(d=n,e=r)}let v=t[2*d],_=t[2*d+1],b=1/0;for(let n=0;nr&&(n[e++]=i,r=o)}return this.hull=n.subarray(0,e),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(Mu(g,y,v,_,m,x)<0){const t=d,n=v,e=_;d=p,v=m,_=x,p=t,m=n,x=e}const w=function(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c),d=t+(f*s-u*l)*h,p=n+(a*l-c*s)*h;return{x:d,y:p}}(g,y,v,_,m,x);this._cx=w.x,this._cy=w.y;for(let n=0;n0&&Math.abs(f-o)<=Tu&&Math.abs(s-a)<=Tu)continue;if(o=f,a=s,c===h||c===d||c===p)continue;let l=0;for(let t=0,n=this._hashKey(f,s);t=0;)if(y=g,y===l){y=-1;break}if(-1===y)continue;let v=this._addTriangle(y,c,e[y],-1,-1,r[y]);r[c]=this._legalize(v+2),r[y]=v,M++;let _=e[y];for(;g=e[_],Mu(f,s,t[2*_],t[2*_+1],t[2*g],t[2*g+1])<0;)v=this._addTriangle(_,c,g,r[c],-1,r[_]),r[c]=this._legalize(v+2),e[_]=_,M--,_=g;if(y===l)for(;g=n[y],Mu(f,s,t[2*g],t[2*g+1],t[2*y],t[2*y+1])<0;)v=this._addTriangle(g,c,y,-1,r[y],r[g]),this._legalize(v+2),r[g]=v,e[y]=y,M--,y=g;this._hullStart=n[c]=y,e[y]=n[_]=c,e[c]=_,i[this._hashKey(f,s)]=c,i[this._hashKey(t[2*y],t[2*y+1])]=y}this.hull=new Uint32Array(M);for(let t=0,n=this._hullStart;t0?3-e:1+e)/4}(t-this._cx,n-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:n,_halfedges:e,coords:r}=this;let i=0,o=0;for(;;){const a=e[t],u=t-t%3;if(o=u+(t+2)%3,-1===a){if(0===i)break;t=Au[--i];continue}const c=a-a%3,f=u+(t+1)%3,s=c+(a+2)%3,l=n[o],h=n[t],d=n[f],p=n[s];if(Nu(r[2*l],r[2*l+1],r[2*h],r[2*h+1],r[2*d],r[2*d+1],r[2*p],r[2*p+1])){n[t]=p,n[a]=l;const r=e[s];if(-1===r){let n=this._hullStart;do{if(this._hullTri[n]===s){this._hullTri[n]=t;break}n=this._hullPrev[n]}while(n!==this._hullStart)}this._link(t,r),this._link(a,e[o]),this._link(o,s);const u=c+(a+1)%3;i=e&&n[t[a]]>o;)t[a+1]=t[a--];t[a+1]=r}else{let i=e+1,o=r;Pu(t,e+r>>1,i),n[t[e]]>n[t[r]]&&Pu(t,e,r),n[t[i]]>n[t[r]]&&Pu(t,i,r),n[t[e]]>n[t[i]]&&Pu(t,e,i);const a=t[i],u=n[a];for(;;){do{i++}while(n[t[i]]u);if(o=o-e?(Cu(t,n,i,r),Cu(t,n,e,o-1)):(Cu(t,n,e,o-1),Cu(t,n,i,r))}}function Pu(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function zu(t){return t[0]}function $u(t){return t[1]}const Du=1e-6;class Ru{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,n){this._+=`L${this._x1=+t},${this._y1=+n}`}arc(t,n,e){const r=(t=+t)+(e=+e),i=n=+n;if(e<0)throw new Error("negative radius");null===this._x1?this._+=`M${r},${i}`:(Math.abs(this._x1-r)>Du||Math.abs(this._y1-i)>Du)&&(this._+="L"+r+","+i),e&&(this._+=`A${e},${e},0,1,1,${t-e},${n}A${e},${e},0,1,1,${this._x1=r},${this._y1=i}`)}rect(t,n,e,r){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${+e}v${+r}h${-e}Z`}value(){return this._||null}}class Fu{constructor(){this._=[]}moveTo(t,n){this._.push([t,n])}closePath(){this._.push(this._[0].slice())}lineTo(t,n){this._.push([t,n])}value(){return this._.length?this._:null}}class qu{constructor(t,[n,e,r,i]=[0,0,960,500]){if(!((r=+r)>=(n=+n)&&(i=+i)>=(e=+e)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(2*t.points.length),this.vectors=new Float64Array(2*t.points.length),this.xmax=r,this.xmin=n,this.ymax=i,this.ymin=e,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:n,triangles:e},vectors:r}=this;let i,o;const a=this.circumcenters=this._circumcenters.subarray(0,e.length/3*2);for(let r,u,c=0,f=0,s=e.length;c1;)i-=2;for(let t=2;t0){if(n>=this.ymax)return null;(i=(this.ymax-n)/r)0){if(t>=this.xmax)return null;(i=(this.xmax-t)/e)this.xmax?2:0)|(nthis.ymax?8:0)}_simplify(t){if(t&&t.length>4){for(let n=0;n2&&function(t){const{triangles:n,coords:e}=t;for(let t=0;t1e-10)return!1}return!0}(t)){this.collinear=Int32Array.from({length:n.length/2},((t,n)=>n)).sort(((t,e)=>n[2*t]-n[2*e]||n[2*t+1]-n[2*e+1]));const t=this.collinear[0],e=this.collinear[this.collinear.length-1],r=[n[2*t],n[2*t+1],n[2*e],n[2*e+1]],i=1e-8*Math.hypot(r[3]-r[1],r[2]-r[0]);for(let t=0,e=n.length/2;t0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=r[0],o[r[0]]=1,2===r.length&&(o[r[1]]=0,this.triangles[1]=r[1],this.triangles[2]=r[1]))}voronoi(t){return new qu(this,t)}*neighbors(t){const{inedges:n,hull:e,_hullIndex:r,halfedges:i,triangles:o,collinear:a}=this;if(a){const n=a.indexOf(t);return n>0&&(yield a[n-1]),void(n=0&&i!==e&&i!==r;)e=i;return i}_step(t,n,e){const{inedges:r,hull:i,_hullIndex:o,halfedges:a,triangles:u,points:c}=this;if(-1===r[t]||!c.length)return(t+1)%(c.length>>1);let f=t,s=Iu(n-c[2*t],2)+Iu(e-c[2*t+1],2);const l=r[t];let h=l;do{let r=u[h];const l=Iu(n-c[2*r],2)+Iu(e-c[2*r+1],2);if(l9999?"+"+Ku(n,6):Ku(n,4))+"-"+Ku(t.getUTCMonth()+1,2)+"-"+Ku(t.getUTCDate(),2)+(o?"T"+Ku(e,2)+":"+Ku(r,2)+":"+Ku(i,2)+"."+Ku(o,3)+"Z":i?"T"+Ku(e,2)+":"+Ku(r,2)+":"+Ku(i,2)+"Z":r||e?"T"+Ku(e,2)+":"+Ku(r,2)+"Z":"")}function Ju(t){var n=new RegExp('["'+t+"\n\r]"),e=t.charCodeAt(0);function r(t,n){var r,i=[],o=t.length,a=0,u=0,c=o<=0,f=!1;function s(){if(c)return Hu;if(f)return f=!1,ju;var n,r,i=a;if(t.charCodeAt(i)===Xu){for(;a++=o?c=!0:(r=t.charCodeAt(a++))===Gu?f=!0:r===Vu&&(f=!0,t.charCodeAt(a)===Gu&&++a),t.slice(i+1,n-1).replace(/""/g,'"')}for(;amc(n,e).then((n=>(new DOMParser).parseFromString(n,t)))}var Sc=Ac("application/xml"),Ec=Ac("text/html"),Nc=Ac("image/svg+xml");function kc(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,a,u,c,f,s,l,h,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,_=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a,i=d,!(d=d[l=s<<1|f]))return i[l]=p,t;if(u=+t._x.call(null,d.data),c=+t._y.call(null,d.data),n===u&&e===c)return p.next=d,i?i[l]=p:t._root=p,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a}while((l=s<<1|f)==(h=(c>=a)<<1|u>=o));return i[h]=d,i[l]=p,t}function Cc(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Pc(t){return t[0]}function zc(t){return t[1]}function $c(t,n,e){var r=new Dc(null==n?Pc:n,null==e?zc:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Dc(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function Rc(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var Fc=$c.prototype=Dc.prototype;function qc(t){return function(){return t}}function Uc(t){return 1e-6*(t()-.5)}function Ic(t){return t.x+t.vx}function Oc(t){return t.y+t.vy}function Bc(t){return t.index}function Yc(t,n){var e=t.get(n);if(!e)throw new Error("node not found: "+n);return e}Fc.copy=function(){var t,n,e=new Dc(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Rc(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Rc(n));return e},Fc.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t);return kc(this.cover(n,e),n,e,t)},Fc.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),c=1/0,f=1/0,s=-1/0,l=-1/0;for(e=0;es&&(s=r),il&&(l=i));if(c>s||f>l)return this;for(this.cover(c,f).cover(s,l),e=0;et||t>=i||r>n||n>=o;)switch(u=(nh||(o=c.y0)>d||(a=c.x1)=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-f],p[p.length-1-f]=c)}else{var _=t-+this._x.call(null,g.data),b=n-+this._y.call(null,g.data),m=_*_+b*b;if(m=(u=(p+y)/2))?p=u:y=u,(s=a>=(c=(g+v)/2))?g=c:v=c,n=d,!(d=d[l=s<<1|f]))return this;if(!d.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[h]=d:this._root=d),this):(this._root=i,this)},Fc.removeAll=function(t){for(var n=0,e=t.length;n1?r[0]+r.slice(2):r,+t.slice(e+1)]}function Zc(t){return(t=Wc(Math.abs(t)))?t[1]:NaN}var Kc,Qc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Jc(t){if(!(n=Qc.exec(t)))throw new Error("invalid format: "+t);var n;return new tf({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function tf(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function nf(t,n){var e=Wc(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}Jc.prototype=tf.prototype,tf.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var ef={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>nf(100*t,n),r:nf,s:function(t,n){var e=Wc(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(Kc=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+Wc(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function rf(t){return t}var of,af=Array.prototype.map,uf=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function cf(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?rf:(n=af.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],a=0,u=n[0],c=0;i>0&&u>0&&(c+u+1>r&&(u=Math.max(1,r-c)),o.push(t.substring(i-=u,i+u)),!((c+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",a=void 0===t.decimal?".":t.decimal+"",u=void 0===t.numerals?rf:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(af.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",s=void 0===t.nan?"NaN":t.nan+"";function l(t){var n=(t=Jc(t)).fill,e=t.align,l=t.sign,h=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,_=t.type;"n"===_?(g=!0,_="g"):ef[_]||(void 0===y&&(y=12),v=!0,_="g"),(d||"0"===n&&"="===e)&&(d=!0,n="0",e="=");var b="$"===h?i:"#"===h&&/[boxX]/.test(_)?"0"+_.toLowerCase():"",m="$"===h?o:/[%p]/.test(_)?c:"",x=ef[_],w=/[defgprs%]/.test(_);function M(t){var i,o,c,h=b,M=m;if("c"===_)M=x(t)+M,t="";else{var T=(t=+t)<0||1/t<0;if(t=isNaN(t)?s:x(Math.abs(t),y),v&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),T&&0==+t&&"+"!==l&&(T=!1),h=(T?"("===l?l:f:"-"===l||"("===l?"":l)+h,M=("s"===_?uf[8+Kc/3]:"")+M+(T&&"("===l?")":""),w)for(i=-1,o=t.length;++i(c=t.charCodeAt(i))||c>57){M=(46===c?a+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var A=h.length+t.length+M.length,S=A>1)+h+t+M+S.slice(A);break;default:t=S+h+t+M}return u(t)}return y=void 0===y?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),M.toString=function(){return t+""},M}return{format:l,formatPrefix:function(t,n){var e=l(((t=Jc(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(Zc(n)/3))),i=Math.pow(10,-r),o=uf[8+r/3];return function(t){return e(i*t)+o}}}}function ff(n){return of=cf(n),t.format=of.format,t.formatPrefix=of.formatPrefix,of}function sf(t){return Math.max(0,-Zc(Math.abs(t)))}function lf(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Zc(n)/3)))-Zc(Math.abs(t)))}function hf(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Zc(n)-Zc(t))+1}t.format=void 0,t.formatPrefix=void 0,ff({thousands:",",grouping:[3],currency:["$",""]});var df=1e-6,pf=1e-12,gf=Math.PI,yf=gf/2,vf=gf/4,_f=2*gf,bf=180/gf,mf=gf/180,xf=Math.abs,wf=Math.atan,Mf=Math.atan2,Tf=Math.cos,Af=Math.ceil,Sf=Math.exp,Ef=Math.hypot,Nf=Math.log,kf=Math.pow,Cf=Math.sin,Pf=Math.sign||function(t){return t>0?1:t<0?-1:0},zf=Math.sqrt,$f=Math.tan;function Df(t){return t>1?0:t<-1?gf:Math.acos(t)}function Rf(t){return t>1?yf:t<-1?-yf:Math.asin(t)}function Ff(t){return(t=Cf(t/2))*t}function qf(){}function Uf(t,n){t&&Of.hasOwnProperty(t.type)&&Of[t.type](t,n)}var If={Feature:function(t,n){Uf(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r=0?1:-1,i=r*e,o=Tf(n=(n*=mf)/2+vf),a=Cf(n),u=Vf*a,c=Gf*o+u*Tf(i),f=u*r*Cf(i);as.add(Mf(f,c)),Xf=t,Gf=o,Vf=a}function ds(t){return[Mf(t[1],t[0]),Rf(t[2])]}function ps(t){var n=t[0],e=t[1],r=Tf(e);return[r*Tf(n),r*Cf(n),Cf(e)]}function gs(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function ys(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function vs(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function _s(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function bs(t){var n=zf(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}var ms,xs,ws,Ms,Ts,As,Ss,Es,Ns,ks,Cs,Ps,zs,$s,Ds,Rs,Fs={point:qs,lineStart:Is,lineEnd:Os,polygonStart:function(){Fs.point=Bs,Fs.lineStart=Ys,Fs.lineEnd=Ls,rs=new T,cs.polygonStart()},polygonEnd:function(){cs.polygonEnd(),Fs.point=qs,Fs.lineStart=Is,Fs.lineEnd=Os,as<0?(Wf=-(Kf=180),Zf=-(Qf=90)):rs>df?Qf=90:rs<-df&&(Zf=-90),os[0]=Wf,os[1]=Kf},sphere:function(){Wf=-(Kf=180),Zf=-(Qf=90)}};function qs(t,n){is.push(os=[Wf=t,Kf=t]),nQf&&(Qf=n)}function Us(t,n){var e=ps([t*mf,n*mf]);if(es){var r=ys(es,e),i=ys([r[1],-r[0],0],r);bs(i),i=ds(i);var o,a=t-Jf,u=a>0?1:-1,c=i[0]*bf*u,f=xf(a)>180;f^(u*JfQf&&(Qf=o):f^(u*Jf<(c=(c+360)%360-180)&&cQf&&(Qf=n)),f?tjs(Wf,Kf)&&(Kf=t):js(t,Kf)>js(Wf,Kf)&&(Wf=t):Kf>=Wf?(tKf&&(Kf=t)):t>Jf?js(Wf,t)>js(Wf,Kf)&&(Kf=t):js(t,Kf)>js(Wf,Kf)&&(Wf=t)}else is.push(os=[Wf=t,Kf=t]);nQf&&(Qf=n),es=e,Jf=t}function Is(){Fs.point=Us}function Os(){os[0]=Wf,os[1]=Kf,Fs.point=qs,es=null}function Bs(t,n){if(es){var e=t-Jf;rs.add(xf(e)>180?e+(e>0?360:-360):e)}else ts=t,ns=n;cs.point(t,n),Us(t,n)}function Ys(){cs.lineStart()}function Ls(){Bs(ts,ns),cs.lineEnd(),xf(rs)>df&&(Wf=-(Kf=180)),os[0]=Wf,os[1]=Kf,es=null}function js(t,n){return(n-=t)<0?n+360:n}function Hs(t,n){return t[0]-n[0]}function Xs(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:ngf&&(t-=Math.round(t/_f)*_f),[t,n]}function ul(t,n,e){return(t%=_f)?n||e?ol(fl(t),sl(n,e)):fl(t):n||e?sl(n,e):al}function cl(t){return function(n,e){return xf(n+=t)>gf&&(n-=Math.round(n/_f)*_f),[n,e]}}function fl(t){var n=cl(t);return n.invert=cl(-t),n}function sl(t,n){var e=Tf(t),r=Cf(t),i=Tf(n),o=Cf(n);function a(t,n){var a=Tf(n),u=Tf(t)*a,c=Cf(t)*a,f=Cf(n),s=f*e+u*r;return[Mf(c*i-s*o,u*e-f*r),Rf(s*i+c*o)]}return a.invert=function(t,n){var a=Tf(n),u=Tf(t)*a,c=Cf(t)*a,f=Cf(n),s=f*i-c*o;return[Mf(c*i+f*o,u*e+s*r),Rf(s*e-u*r)]},a}function ll(t){function n(n){return(n=t(n[0]*mf,n[1]*mf))[0]*=bf,n[1]*=bf,n}return t=ul(t[0]*mf,t[1]*mf,t.length>2?t[2]*mf:0),n.invert=function(n){return(n=t.invert(n[0]*mf,n[1]*mf))[0]*=bf,n[1]*=bf,n},n}function hl(t,n,e,r,i,o){if(e){var a=Tf(n),u=Cf(n),c=r*e;null==i?(i=n+r*_f,o=n-c/2):(i=dl(a,i),o=dl(a,o),(r>0?io)&&(i+=r*_f));for(var f,s=i;r>0?s>o:s1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function gl(t,n){return xf(t[0]-n[0])=0;--o)i.point((s=f[o])[0],s[1]);else r(h.x,h.p.x,-1,i);h=h.p}f=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}}function _l(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,E=S*A,N=E>gf,k=y*w;if(c.add(Mf(k*S*Cf(E),v*M+k*Tf(E))),a+=N?A+S*_f:A,N^p>=e^m>=e){var C=ys(ps(d),ps(b));bs(C);var P=ys(o,C);bs(P);var z=(N^A>=0?-1:1)*Rf(P[2]);(r>z||r===z&&(C[0]||C[1]))&&(u+=N^A>=0?1:-1)}}return(a<-df||a0){for(l||(i.polygonStart(),l=!0),i.lineStart(),t=0;t1&&2&c&&h.push(h.pop().concat(h.shift())),a.push(h.filter(wl))}return h}}function wl(t){return t.length>1}function Ml(t,n){return((t=t.x)[0]<0?t[1]-yf-df:yf-t[1])-((n=n.x)[0]<0?n[1]-yf-df:yf-n[1])}al.invert=al;var Tl=xl((function(){return!0}),(function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,a){var u=o>0?gf:-gf,c=xf(o-e);xf(c-gf)0?yf:-yf),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),t.point(o,r),n=0):i!==u&&c>=gf&&(xf(e-i)df?wf((Cf(n)*(o=Tf(r))*Cf(e)-Cf(r)*(i=Tf(n))*Cf(t))/(i*o*a)):(n+r)/2}(e,r,o,a),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),n=0),t.point(e=o,r=a),i=u},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}}),(function(t,n,e,r){var i;if(null==t)i=e*yf,r.point(-gf,i),r.point(0,i),r.point(gf,i),r.point(gf,0),r.point(gf,-i),r.point(0,-i),r.point(-gf,-i),r.point(-gf,0),r.point(-gf,i);else if(xf(t[0]-n[0])>df){var o=t[0]0,i=xf(n)>df;function o(t,e){return Tf(t)*Tf(e)>n}function a(t,e,r){var i=[1,0,0],o=ys(ps(t),ps(e)),a=gs(o,o),u=o[0],c=a-u*u;if(!c)return!r&&t;var f=n*a/c,s=-n*u/c,l=ys(i,o),h=_s(i,f);vs(h,_s(o,s));var d=l,p=gs(h,d),g=gs(d,d),y=p*p-g*(gs(h,h)-1);if(!(y<0)){var v=zf(y),_=_s(d,(-p-v)/g);if(vs(_,h),_=ds(_),!r)return _;var b,m=t[0],x=e[0],w=t[1],M=e[1];x0^_[1]<(xf(_[0]-m)gf^(m<=_[0]&&_[0]<=x)){var S=_s(d,(-p+v)/g);return vs(S,h),[_,ds(S)]}}}function u(n,e){var i=r?t:gf-t,o=0;return n<-i?o|=1:n>i&&(o|=2),e<-i?o|=4:e>i&&(o|=8),o}return xl(o,(function(t){var n,e,c,f,s;return{lineStart:function(){f=c=!1,s=1},point:function(l,h){var d,p=[l,h],g=o(l,h),y=r?g?0:u(l,h):g?u(l+(l<0?gf:-gf),h):0;if(!n&&(f=c=g)&&t.lineStart(),g!==c&&(!(d=a(n,p))||gl(n,d)||gl(p,d))&&(p[2]=1),g!==c)s=0,g?(t.lineStart(),d=a(p,n),t.point(d[0],d[1])):(d=a(n,p),t.point(d[0],d[1],2),t.lineEnd()),n=d;else if(i&&n&&r^g){var v;y&e||!(v=a(p,n,!0))||(s=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1],3)))}!g||n&&gl(n,p)||t.point(p[0],p[1]),n=p,c=g,e=y},lineEnd:function(){c&&t.lineEnd(),n=null},clean:function(){return s|(f&&c)<<1}}}),(function(n,r,i,o){hl(o,t,e,i,n,r)}),r?[0,-t]:[-gf,t-gf])}var Sl,El,Nl,kl,Cl=1e9,Pl=-Cl;function zl(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,u,f){var s=0,l=0;if(null==i||(s=a(i,u))!==(l=a(o,u))||c(i,o)<0^u>0)do{f.point(0===s||3===s?t:e,s>1?r:n)}while((s=(s+u+4)%4)!==l);else f.point(o[0],o[1])}function a(r,i){return xf(r[0]-t)0?0:3:xf(r[0]-e)0?2:1:xf(r[1]-n)0?1:0:i>0?3:2}function u(t,n){return c(t.x,n.x)}function c(t,n){var e=a(t,1),r=a(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(a){var c,f,s,l,h,d,p,g,y,v,_,b=a,m=pl(),x={point:w,lineStart:function(){x.point=M,f&&f.push(s=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(M(l,h),d&&y&&m.rejoin(),c.push(m.result()));x.point=w,y&&b.lineEnd()},polygonStart:function(){b=m,c=[],f=[],_=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=f.length;er&&(h-o)*(r-a)>(d-a)*(t-o)&&++n:d<=r&&(h-o)*(r-a)<(d-a)*(t-o)&&--n;return n}(),e=_&&n,i=(c=ft(c)).length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&vl(c,u,n,o,a),a.polygonEnd());b=a,c=f=s=null}};function w(t,n){i(t,n)&&b.point(t,n)}function M(o,a){var u=i(o,a);if(f&&s.push([o,a]),v)l=o,h=a,d=u,v=!1,u&&(b.lineStart(),b.point(o,a));else if(u&&y)b.point(o,a);else{var c=[p=Math.max(Pl,Math.min(Cl,p)),g=Math.max(Pl,Math.min(Cl,g))],m=[o=Math.max(Pl,Math.min(Cl,o)),a=Math.max(Pl,Math.min(Cl,a))];!function(t,n,e,r,i,o){var a,u=t[0],c=t[1],f=0,s=1,l=n[0]-u,h=n[1]-c;if(a=e-u,l||!(a>0)){if(a/=l,l<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=i-u,l||!(a<0)){if(a/=l,l<0){if(a>s)return;a>f&&(f=a)}else if(l>0){if(a0)){if(a/=h,h<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=o-c,h||!(a<0)){if(a/=h,h<0){if(a>s)return;a>f&&(f=a)}else if(h>0){if(a0&&(t[0]=u+f*l,t[1]=c+f*h),s<1&&(n[0]=u+s*l,n[1]=c+s*h),!0}}}}}(c,m,t,n,e,r)?u&&(b.lineStart(),b.point(o,a),_=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(m[0],m[1]),u||b.lineEnd(),_=!1)}p=o,g=a,y=u}return x}}var $l={sphere:qf,point:qf,lineStart:function(){$l.point=Rl,$l.lineEnd=Dl},lineEnd:qf,polygonStart:qf,polygonEnd:qf};function Dl(){$l.point=$l.lineEnd=qf}function Rl(t,n){El=t*=mf,Nl=Cf(n*=mf),kl=Tf(n),$l.point=Fl}function Fl(t,n){t*=mf;var e=Cf(n*=mf),r=Tf(n),i=xf(t-El),o=Tf(i),a=r*Cf(i),u=kl*e-Nl*r*o,c=Nl*e+kl*r*o;Sl.add(Mf(zf(a*a+u*u),c)),El=t,Nl=e,kl=r}function ql(t){return Sl=new T,Lf(t,$l),+Sl}var Ul=[null,null],Il={type:"LineString",coordinates:Ul};function Ol(t,n){return Ul[0]=t,Ul[1]=n,ql(Il)}var Bl={Feature:function(t,n){return Ll(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r0&&(i=Ol(t[o],t[o-1]))>0&&e<=i&&r<=i&&(e+r-i)*(1-Math.pow((e-r)/i,2))df})).map(c)).concat(lt(Af(o/d)*d,i,d).filter((function(t){return xf(t%g)>df})).map(f))}return v.lines=function(){return _().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[s(r).concat(l(a).slice(1),s(e).reverse().slice(1),l(u).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],u=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),u>a&&(t=u,u=a,a=t),v.precision(y)):[[r,u],[e,a]]},v.extentMinor=function(e){return arguments.length?(n=+e[0][0],t=+e[1][0],o=+e[0][1],i=+e[1][1],n>t&&(e=n,n=t,t=e),o>i&&(e=o,o=i,i=e),v.precision(y)):[[n,o],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(h=+t[0],d=+t[1],v):[h,d]},v.precision=function(h){return arguments.length?(y=+h,c=Wl(o,i,90),f=Zl(n,t,y),s=Wl(u,a,90),l=Zl(r,e,y),v):y},v.extentMajor([[-180,-90+df],[180,90-df]]).extentMinor([[-180,-80-df],[180,80+df]])}var Ql,Jl,th,nh,eh=t=>t,rh=new T,ih=new T,oh={point:qf,lineStart:qf,lineEnd:qf,polygonStart:function(){oh.lineStart=ah,oh.lineEnd=fh},polygonEnd:function(){oh.lineStart=oh.lineEnd=oh.point=qf,rh.add(xf(ih)),ih=new T},result:function(){var t=rh/2;return rh=new T,t}};function ah(){oh.point=uh}function uh(t,n){oh.point=ch,Ql=th=t,Jl=nh=n}function ch(t,n){ih.add(nh*t-th*n),th=t,nh=n}function fh(){ch(Ql,Jl)}var sh=oh,lh=1/0,hh=lh,dh=-lh,ph=dh,gh={point:function(t,n){tdh&&(dh=t);nph&&(ph=n)},lineStart:qf,lineEnd:qf,polygonStart:qf,polygonEnd:qf,result:function(){var t=[[lh,hh],[dh,ph]];return dh=ph=-(hh=lh=1/0),t}};var yh,vh,_h,bh,mh=gh,xh=0,wh=0,Mh=0,Th=0,Ah=0,Sh=0,Eh=0,Nh=0,kh=0,Ch={point:Ph,lineStart:zh,lineEnd:Rh,polygonStart:function(){Ch.lineStart=Fh,Ch.lineEnd=qh},polygonEnd:function(){Ch.point=Ph,Ch.lineStart=zh,Ch.lineEnd=Rh},result:function(){var t=kh?[Eh/kh,Nh/kh]:Sh?[Th/Sh,Ah/Sh]:Mh?[xh/Mh,wh/Mh]:[NaN,NaN];return xh=wh=Mh=Th=Ah=Sh=Eh=Nh=kh=0,t}};function Ph(t,n){xh+=t,wh+=n,++Mh}function zh(){Ch.point=$h}function $h(t,n){Ch.point=Dh,Ph(_h=t,bh=n)}function Dh(t,n){var e=t-_h,r=n-bh,i=zf(e*e+r*r);Th+=i*(_h+t)/2,Ah+=i*(bh+n)/2,Sh+=i,Ph(_h=t,bh=n)}function Rh(){Ch.point=Ph}function Fh(){Ch.point=Uh}function qh(){Ih(yh,vh)}function Uh(t,n){Ch.point=Ih,Ph(yh=_h=t,vh=bh=n)}function Ih(t,n){var e=t-_h,r=n-bh,i=zf(e*e+r*r);Th+=i*(_h+t)/2,Ah+=i*(bh+n)/2,Sh+=i,Eh+=(i=bh*t-_h*n)*(_h+t),Nh+=i*(bh+n),kh+=3*i,Ph(_h=t,bh=n)}var Oh=Ch;function Bh(t){this._context=t}Bh.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,_f)}},result:qf};var Yh,Lh,jh,Hh,Xh,Gh=new T,Vh={point:qf,lineStart:function(){Vh.point=Wh},lineEnd:function(){Yh&&Zh(Lh,jh),Vh.point=qf},polygonStart:function(){Yh=!0},polygonEnd:function(){Yh=null},result:function(){var t=+Gh;return Gh=new T,t}};function Wh(t,n){Vh.point=Zh,Lh=Hh=t,jh=Xh=n}function Zh(t,n){Hh-=t,Xh-=n,Gh.add(zf(Hh*Hh+Xh*Xh)),Hh=t,Xh=n}var Kh=Vh;let Qh,Jh,td,nd;class ed{constructor(t){this._append=null==t?rd:function(t){const n=Math.floor(t);if(!(n>=0))throw new RangeError(`invalid digits: ${t}`);if(n>15)return rd;if(n!==Qh){const t=10**n;Qh=n,Jh=function(n){let e=1;this._+=n[0];for(const r=n.length;e4*n&&g--){var m=a+h,x=u+d,w=c+p,M=zf(m*m+x*x+w*w),T=Rf(w/=M),A=xf(xf(w)-1)n||xf((v*k+_*C)/b-.5)>.3||a*h+u*d+c*p2?t[2]%360*mf:0,k()):[y*bf,v*bf,_*bf]},E.angle=function(t){return arguments.length?(b=t%360*mf,k()):b*bf},E.reflectX=function(t){return arguments.length?(m=t?-1:1,k()):m<0},E.reflectY=function(t){return arguments.length?(x=t?-1:1,k()):x<0},E.precision=function(t){return arguments.length?(a=dd(u,S=t*t),C()):zf(S)},E.fitExtent=function(t,n){return ud(E,t,n)},E.fitSize=function(t,n){return cd(E,t,n)},E.fitWidth=function(t,n){return fd(E,t,n)},E.fitHeight=function(t,n){return sd(E,t,n)},function(){return n=t.apply(this,arguments),E.invert=n.invert&&N,k()}}function _d(t){var n=0,e=gf/3,r=vd(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*mf,e=t[1]*mf):[n*bf,e*bf]},i}function bd(t,n){var e=Cf(t),r=(e+Cf(n))/2;if(xf(r)0?n<-yf+df&&(n=-yf+df):n>yf-df&&(n=yf-df);var e=i/kf(Nd(n),r);return[e*Cf(r*t),i-e*Tf(r*t)]}return o.invert=function(t,n){var e=i-n,o=Pf(r)*zf(t*t+e*e),a=Mf(t,xf(e))*Pf(e);return e*r<0&&(a-=gf*Pf(t)*Pf(e)),[a/r,2*wf(kf(i/o,1/r))-yf]},o}function Cd(t,n){return[t,n]}function Pd(t,n){var e=Tf(t),r=t===n?Cf(t):(e-Tf(n))/(n-t),i=e/r+t;if(xf(r)=0;)n+=e[r].value;else n=1;t.value=n}function Gd(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=Wd)):void 0===n&&(n=Vd);for(var e,r,i,o,a,u=new Qd(t),c=[u];e=c.pop();)if((i=n(e.data))&&(a=(i=Array.from(i)).length))for(e.children=i,o=a-1;o>=0;--o)c.push(r=i[o]=new Qd(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(Kd)}function Vd(t){return t.children}function Wd(t){return Array.isArray(t)?t[1]:null}function Zd(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Kd(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function Qd(t){this.data=t,this.depth=this.height=0,this.parent=null}function Jd(t){return null==t?null:tp(t)}function tp(t){if("function"!=typeof t)throw new Error;return t}function np(){return 0}function ep(t){return function(){return t}}qd.invert=function(t,n){for(var e,r=n,i=r*r,o=i*i*i,a=0;a<12&&(o=(i=(r-=e=(r*(zd+$d*i+o*(Dd+Rd*i))-n)/(zd+3*$d*i+o*(7*Dd+9*Rd*i)))*r)*i*i,!(xf(e)df&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},Od.invert=Md(Rf),Bd.invert=Md((function(t){return 2*wf(t)})),Yd.invert=function(t,n){return[-n,2*wf(Sf(t))-yf]},Qd.prototype=Gd.prototype={constructor:Qd,count:function(){return this.eachAfter(Xd)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,a=[o],u=[],c=-1;o=a.pop();)if(u.push(o),e=o.children)for(r=0,i=e.length;r=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;t=e.pop(),n=r.pop();for(;t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Gd(this).eachBefore(Zd)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;e(t=(rp*t+ip)%op)/op}function up(t,n){for(var e,r,i=0,o=(t=function(t,n){let e,r,i=t.length;for(;i;)r=n()*i--|0,e=t[i],t[i]=t[r],t[r]=e;return t}(Array.from(t),n)).length,a=[];i0&&e*e>r*r+i*i}function lp(t,n){for(var e=0;e1e-6?(E+Math.sqrt(E*E-4*S*N))/(2*S):N/E);return{x:r+w+M*k,y:i+T+A*k,r:k}}function gp(t,n,e){var r,i,o,a,u=t.x-n.x,c=t.y-n.y,f=u*u+c*c;f?(i=n.r+e.r,i*=i,a=t.r+e.r,i>(a*=a)?(r=(f+a-i)/(2*f),o=Math.sqrt(Math.max(0,a/f-r*r)),e.x=t.x-r*u-o*c,e.y=t.y-r*c+o*u):(r=(f+i-a)/(2*f),o=Math.sqrt(Math.max(0,i/f-r*r)),e.x=n.x+r*u-o*c,e.y=n.y+r*c+o*u)):(e.x=n.x+e.r,e.y=n.y)}function yp(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function vp(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function _p(t){this._=t,this.next=null,this.previous=null}function bp(t,n){if(!(o=(t=function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}(t)).length))return 0;var e,r,i,o,a,u,c,f,s,l,h;if((e=t[0]).x=0,e.y=0,!(o>1))return e.r;if(r=t[1],e.x=-r.r,r.x=e.r,r.y=0,!(o>2))return e.r+r.r;gp(r,e,i=t[2]),e=new _p(e),r=new _p(r),i=new _p(i),e.next=i.previous=r,r.next=e.previous=i,i.next=r.previous=e;t:for(c=3;c1&&!zp(t,n););return t.slice(0,n)}function zp(t,n){if("/"===t[n]){let e=0;for(;n>0&&"\\"===t[--n];)++e;if(!(1&e))return!0}return!1}function $p(t,n){return t.parent===n.parent?1:2}function Dp(t){var n=t.children;return n?n[0]:t.t}function Rp(t){var n=t.children;return n?n[n.length-1]:t.t}function Fp(t,n,e){var r=e/(n.i-t.i);n.c-=r,n.s+=e,t.c+=r,n.z+=e,n.m+=e}function qp(t,n,e){return t.a.parent===n.parent?t.a:e}function Up(t,n){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=n}function Ip(t,n,e,r,i){for(var o,a=t.children,u=-1,c=a.length,f=t.value&&(i-e)/t.value;++uh&&(h=u),y=s*s*g,(d=Math.max(h/y,y/l))>p){s-=u;break}p=d}v.push(a={value:s,dice:c1?n:1)},e}(Op);var Lp=function t(n){function e(t,e,r,i,o){if((a=t._squarify)&&a.ratio===n)for(var a,u,c,f,s,l=-1,h=a.length,d=t.value;++l1?n:1)},e}(Op);function jp(t,n,e){return(n[0]-t[0])*(e[1]-t[1])-(n[1]-t[1])*(e[0]-t[0])}function Hp(t,n){return t[0]-n[0]||t[1]-n[1]}function Xp(t){const n=t.length,e=[0,1];let r,i=2;for(r=2;r1&&jp(t[e[i-2]],t[e[i-1]],t[r])<=0;)--i;e[i++]=r}return e.slice(0,i)}var Gp=Math.random,Vp=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Gp),Wp=function t(n){function e(t,e){return arguments.length<2&&(e=t,t=0),t=Math.floor(t),e=Math.floor(e)-t,function(){return Math.floor(n()*e+t)}}return e.source=t,e}(Gp),Zp=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Gp),Kp=function t(n){var e=Zp.source(n);function r(){var t=e.apply(this,arguments);return function(){return Math.exp(t())}}return r.source=t,r}(Gp),Qp=function t(n){function e(t){return(t=+t)<=0?()=>0:function(){for(var e=0,r=t;r>1;--r)e+=n();return e+r*n()}}return e.source=t,e}(Gp),Jp=function t(n){var e=Qp.source(n);function r(t){if(0==(t=+t))return n;var r=e(t);return function(){return r()/t}}return r.source=t,r}(Gp),tg=function t(n){function e(t){return function(){return-Math.log1p(-n())/t}}return e.source=t,e}(Gp),ng=function t(n){function e(t){if((t=+t)<0)throw new RangeError("invalid alpha");return t=1/-t,function(){return Math.pow(1-n(),t)}}return e.source=t,e}(Gp),eg=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return function(){return Math.floor(n()+t)}}return e.source=t,e}(Gp),rg=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return 0===t?()=>1/0:1===t?()=>1:(t=Math.log1p(-t),function(){return 1+Math.floor(Math.log1p(-n())/t)})}return e.source=t,e}(Gp),ig=function t(n){var e=Zp.source(n)();function r(t,r){if((t=+t)<0)throw new RangeError("invalid k");if(0===t)return()=>0;if(r=null==r?1:+r,1===t)return()=>-Math.log1p(-n())*r;var i=(t<1?t+1:t)-1/3,o=1/(3*Math.sqrt(i)),a=t<1?()=>Math.pow(n(),1/t):()=>1;return function(){do{do{var t=e(),u=1+o*t}while(u<=0);u*=u*u;var c=1-n()}while(c>=1-.0331*t*t*t*t&&Math.log(c)>=.5*t*t+i*(1-u+Math.log(u)));return i*u*a()*r}}return r.source=t,r}(Gp),og=function t(n){var e=ig.source(n);function r(t,n){var r=e(t),i=e(n);return function(){var t=r();return 0===t?0:t/(t+i())}}return r.source=t,r}(Gp),ag=function t(n){var e=rg.source(n),r=og.source(n);function i(t,n){return t=+t,(n=+n)>=1?()=>t:n<=0?()=>0:function(){for(var i=0,o=t,a=n;o*a>16&&o*(1-a)>16;){var u=Math.floor((o+1)*a),c=r(u,o-u+1)();c<=a?(i+=u,o-=u,a=(a-c)/(1-c)):(o=u-1,a/=c)}for(var f=a<.5,s=e(f?a:1-a),l=s(),h=0;l<=o;++h)l+=s();return i+(f?h:o-h)}}return i.source=t,i}(Gp),ug=function t(n){function e(t,e,r){var i;return 0==(t=+t)?i=t=>-Math.log(t):(t=1/t,i=n=>Math.pow(n,t)),e=null==e?0:+e,r=null==r?1:+r,function(){return e+r*i(-Math.log1p(-n()))}}return e.source=t,e}(Gp),cg=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){return t+e*Math.tan(Math.PI*n())}}return e.source=t,e}(Gp),fg=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){var r=n();return t+e*Math.log(r/(1-r))}}return e.source=t,e}(Gp),sg=function t(n){var e=ig.source(n),r=ag.source(n);function i(t){return function(){for(var i=0,o=t;o>16;){var a=Math.floor(.875*o),u=e(a)();if(u>o)return i+r(a-1,o/u)();i+=a,o-=u}for(var c=-Math.log1p(-n()),f=0;c<=o;++f)c-=Math.log1p(-n());return i+f}}return i.source=t,i}(Gp);const lg=1/4294967296;function hg(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function dg(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this}const pg=Symbol("implicit");function gg(){var t=new InternMap,n=[],e=[],r=pg;function i(i){let o=t.get(i);if(void 0===o){if(r!==pg)return r;t.set(i,o=n.push(i)-1)}return e[o%e.length]}return i.domain=function(e){if(!arguments.length)return n.slice();n=[],t=new InternMap;for(const r of e)t.has(r)||t.set(r,n.push(r)-1);return i},i.range=function(t){return arguments.length?(e=Array.from(t),i):e.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return gg(n,e).unknown(r)},hg.apply(i,arguments),i}function yg(){var t,n,e=gg().unknown(void 0),r=e.domain,i=e.range,o=0,a=1,u=!1,c=0,f=0,s=.5;function l(){var e=r().length,l=an&&(e=t,t=n,n=e),function(e){return Math.max(t,Math.min(n,e))}}(a[0],a[t-1])),r=t>2?Mg:wg,i=o=null,l}function l(n){return null==n||isNaN(n=+n)?e:(i||(i=r(a.map(t),u,c)))(t(f(n)))}return l.invert=function(e){return f(n((o||(o=r(u,a.map(t),Yr)))(e)))},l.domain=function(t){return arguments.length?(a=Array.from(t,_g),s()):a.slice()},l.range=function(t){return arguments.length?(u=Array.from(t),s()):u.slice()},l.rangeRound=function(t){return u=Array.from(t),c=Vr,s()},l.clamp=function(t){return arguments.length?(f=!!t||mg,s()):f!==mg},l.interpolate=function(t){return arguments.length?(c=t,s()):c},l.unknown=function(t){return arguments.length?(e=t,l):e},function(e,r){return t=e,n=r,s()}}function Sg(){return Ag()(mg,mg)}function Eg(n,e,r,i){var o,a=W(n,e,r);switch((i=Jc(null==i?",f":i)).type){case"s":var u=Math.max(Math.abs(n),Math.abs(e));return null!=i.precision||isNaN(o=lf(a,u))||(i.precision=o),t.formatPrefix(i,u);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=hf(a,Math.max(Math.abs(n),Math.abs(e))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=sf(a))||(i.precision=o-2*("%"===i.type))}return t.format(i)}function Ng(t){var n=t.domain;return t.ticks=function(t){var e=n();return G(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return Eg(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),a=0,u=o.length-1,c=o[a],f=o[u],s=10;for(f0;){if((i=V(c,f,e))===r)return o[a]=c,o[u]=f,n(o);if(i>0)c=Math.floor(c/i)*i,f=Math.ceil(f/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,f=Math.floor(f*i)/i}r=i}return t},t}function kg(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return a-t(-n,e)}function Fg(n){const e=n(Cg,Pg),r=e.domain;let i,o,a=10;function u(){return i=function(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),n=>Math.log(n)/t)}(a),o=function(t){return 10===t?Dg:t===Math.E?Math.exp:n=>Math.pow(t,n)}(a),r()[0]<0?(i=Rg(i),o=Rg(o),n(zg,$g)):n(Cg,Pg),e}return e.base=function(t){return arguments.length?(a=+t,u()):a},e.domain=function(t){return arguments.length?(r(t),u()):r()},e.ticks=t=>{const n=r();let e=n[0],u=n[n.length-1];const c=u0){for(;l<=h;++l)for(f=1;fu)break;p.push(s)}}else for(;l<=h;++l)for(f=a-1;f>=1;--f)if(s=l>0?f/o(-l):f*o(l),!(su)break;p.push(s)}2*p.length{if(null==n&&(n=10),null==r&&(r=10===a?"s":","),"function"!=typeof r&&(a%1||null!=(r=Jc(r)).precision||(r.trim=!0),r=t.format(r)),n===1/0)return r;const u=Math.max(1,a*n/e.ticks().length);return t=>{let n=t/o(Math.round(i(t)));return n*ar(kg(r(),{floor:t=>o(Math.floor(i(t))),ceil:t=>o(Math.ceil(i(t)))})),e}function qg(t){return function(n){return Math.sign(n)*Math.log1p(Math.abs(n/t))}}function Ug(t){return function(n){return Math.sign(n)*Math.expm1(Math.abs(n))*t}}function Ig(t){var n=1,e=t(qg(n),Ug(n));return e.constant=function(e){return arguments.length?t(qg(n=+e),Ug(n)):n},Ng(e)}function Og(t){return function(n){return n<0?-Math.pow(-n,t):Math.pow(n,t)}}function Bg(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function Yg(t){return t<0?-t*t:t*t}function Lg(t){var n=t(mg,mg),e=1;return n.exponent=function(n){return arguments.length?1===(e=+n)?t(mg,mg):.5===e?t(Bg,Yg):t(Og(e),Og(1/e)):e},Ng(n)}function jg(){var t=Lg(Ag());return t.copy=function(){return Tg(t,jg()).exponent(t.exponent())},hg.apply(t,arguments),t}function Hg(t){return Math.sign(t)*t*t}const Xg=new Date,Gg=new Date;function Vg(t,n,e,r){function i(n){return t(n=0===arguments.length?new Date:new Date(+n)),n}return i.floor=n=>(t(n=new Date(+n)),n),i.ceil=e=>(t(e=new Date(e-1)),n(e,1),t(e),e),i.round=t=>{const n=i(t),e=i.ceil(t);return t-n(n(t=new Date(+t),null==e?1:Math.floor(e)),t),i.range=(e,r,o)=>{const a=[];if(e=i.ceil(e),o=null==o?1:Math.floor(o),!(e0))return a;let u;do{a.push(u=new Date(+e)),n(e,o),t(e)}while(uVg((n=>{if(n>=n)for(;t(n),!e(n);)n.setTime(n-1)}),((t,r)=>{if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})),e&&(i.count=(n,r)=>(Xg.setTime(+n),Gg.setTime(+r),t(Xg),t(Gg),Math.floor(e(Xg,Gg))),i.every=t=>(t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?n=>r(n)%t==0:n=>i.count(0,n)%t==0):i:null)),i}const Wg=Vg((()=>{}),((t,n)=>{t.setTime(+t+n)}),((t,n)=>n-t));Wg.every=t=>(t=Math.floor(t),isFinite(t)&&t>0?t>1?Vg((n=>{n.setTime(Math.floor(n/t)*t)}),((n,e)=>{n.setTime(+n+e*t)}),((n,e)=>(e-n)/t)):Wg:null);const Zg=Wg.range,Kg=1e3,Qg=6e4,Jg=36e5,ty=864e5,ny=6048e5,ey=2592e6,ry=31536e6,iy=Vg((t=>{t.setTime(t-t.getMilliseconds())}),((t,n)=>{t.setTime(+t+n*Kg)}),((t,n)=>(n-t)/Kg),(t=>t.getUTCSeconds())),oy=iy.range,ay=Vg((t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Kg)}),((t,n)=>{t.setTime(+t+n*Qg)}),((t,n)=>(n-t)/Qg),(t=>t.getMinutes())),uy=ay.range,cy=Vg((t=>{t.setUTCSeconds(0,0)}),((t,n)=>{t.setTime(+t+n*Qg)}),((t,n)=>(n-t)/Qg),(t=>t.getUTCMinutes())),fy=cy.range,sy=Vg((t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Kg-t.getMinutes()*Qg)}),((t,n)=>{t.setTime(+t+n*Jg)}),((t,n)=>(n-t)/Jg),(t=>t.getHours())),ly=sy.range,hy=Vg((t=>{t.setUTCMinutes(0,0,0)}),((t,n)=>{t.setTime(+t+n*Jg)}),((t,n)=>(n-t)/Jg),(t=>t.getUTCHours())),dy=hy.range,py=Vg((t=>t.setHours(0,0,0,0)),((t,n)=>t.setDate(t.getDate()+n)),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qg)/ty),(t=>t.getDate()-1)),gy=py.range,yy=Vg((t=>{t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+n)}),((t,n)=>(n-t)/ty),(t=>t.getUTCDate()-1)),vy=yy.range,_y=Vg((t=>{t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+n)}),((t,n)=>(n-t)/ty),(t=>Math.floor(t/ty))),by=_y.range;function my(t){return Vg((n=>{n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)}),((t,n)=>{t.setDate(t.getDate()+7*n)}),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qg)/ny))}const xy=my(0),wy=my(1),My=my(2),Ty=my(3),Ay=my(4),Sy=my(5),Ey=my(6),Ny=xy.range,ky=wy.range,Cy=My.range,Py=Ty.range,zy=Ay.range,$y=Sy.range,Dy=Ey.range;function Ry(t){return Vg((n=>{n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+7*n)}),((t,n)=>(n-t)/ny))}const Fy=Ry(0),qy=Ry(1),Uy=Ry(2),Iy=Ry(3),Oy=Ry(4),By=Ry(5),Yy=Ry(6),Ly=Fy.range,jy=qy.range,Hy=Uy.range,Xy=Iy.range,Gy=Oy.range,Vy=By.range,Wy=Yy.range,Zy=Vg((t=>{t.setDate(1),t.setHours(0,0,0,0)}),((t,n)=>{t.setMonth(t.getMonth()+n)}),((t,n)=>n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())),(t=>t.getMonth())),Ky=Zy.range,Qy=Vg((t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCMonth(t.getUTCMonth()+n)}),((t,n)=>n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())),(t=>t.getUTCMonth())),Jy=Qy.range,tv=Vg((t=>{t.setMonth(0,1),t.setHours(0,0,0,0)}),((t,n)=>{t.setFullYear(t.getFullYear()+n)}),((t,n)=>n.getFullYear()-t.getFullYear()),(t=>t.getFullYear()));tv.every=t=>isFinite(t=Math.floor(t))&&t>0?Vg((n=>{n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)}),((n,e)=>{n.setFullYear(n.getFullYear()+e*t)})):null;const nv=tv.range,ev=Vg((t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCFullYear(t.getUTCFullYear()+n)}),((t,n)=>n.getUTCFullYear()-t.getUTCFullYear()),(t=>t.getUTCFullYear()));ev.every=t=>isFinite(t=Math.floor(t))&&t>0?Vg((n=>{n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)}),((n,e)=>{n.setUTCFullYear(n.getUTCFullYear()+e*t)})):null;const rv=ev.range;function iv(t,n,e,i,o,a){const u=[[iy,1,Kg],[iy,5,5e3],[iy,15,15e3],[iy,30,3e4],[a,1,Qg],[a,5,3e5],[a,15,9e5],[a,30,18e5],[o,1,Jg],[o,3,108e5],[o,6,216e5],[o,12,432e5],[i,1,ty],[i,2,1728e5],[e,1,ny],[n,1,ey],[n,3,7776e6],[t,1,ry]];function c(n,e,i){const o=Math.abs(e-n)/i,a=r((([,,t])=>t)).right(u,o);if(a===u.length)return t.every(W(n/ry,e/ry,i));if(0===a)return Wg.every(Math.max(W(n,e,i),1));const[c,f]=u[o/u[a-1][2]=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:k_,s:C_,S:Zv,u:Kv,U:Qv,V:t_,w:n_,W:e_,x:null,X:null,y:r_,Y:o_,Z:u_,"%":N_},m={a:function(t){return a[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return u[t.getUTCMonth()]},c:null,d:c_,e:c_,f:d_,g:T_,G:S_,H:f_,I:s_,j:l_,L:h_,m:p_,M:g_,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:k_,s:C_,S:y_,u:v_,U:__,V:m_,w:x_,W:w_,x:null,X:null,y:M_,Y:A_,Z:E_,"%":N_},x={a:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=p.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=v.exec(n.slice(e));return r?(t.m=_.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return T(t,n,e,r)},d:zv,e:zv,f:Uv,g:Nv,G:Ev,H:Dv,I:Dv,j:$v,L:qv,m:Pv,M:Rv,p:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.p=s.get(r[0].toLowerCase()),e+r[0].length):-1},q:Cv,Q:Ov,s:Bv,S:Fv,u:Mv,U:Tv,V:Av,w:wv,W:Sv,x:function(t,n,r){return T(t,e,n,r)},X:function(t,n,e){return T(t,r,n,e)},y:Nv,Y:Ev,Z:kv,"%":Iv};function w(t,n){return function(e){var r,i,o,a=[],u=-1,c=0,f=t.length;for(e instanceof Date||(e=new Date(+e));++u53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=sv(lv(o.y,0,1))).getUTCDay(),r=i>4||0===i?qy.ceil(r):qy(r),r=yy.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=fv(lv(o.y,0,1))).getDay(),r=i>4||0===i?wy.ceil(r):wy(r),r=py.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?sv(lv(o.y,0,1)).getUTCDay():fv(lv(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,sv(o)):fv(o)}}function T(t,n,e,r){for(var i,o,a=0,u=n.length,c=e.length;a=c)return-1;if(37===(i=n.charCodeAt(a++))){if(i=n.charAt(a++),!(o=x[i in pv?n.charAt(a++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return b.x=w(e,b),b.X=w(r,b),b.c=w(n,b),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",b);return n.toString=function(){return t},n},parse:function(t){var n=M(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=M(t+="",!0);return n.toString=function(){return t},n}}}var dv,pv={"-":"",_:" ",0:"0"},gv=/^\s*\d+/,yv=/^%/,vv=/[\\^$*+?|[\]().{}]/g;function _v(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o[t.toLowerCase(),n])))}function wv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function Mv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function Tv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function Av(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function Sv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function Ev(t,n,e){var r=gv.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function Nv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function kv(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function Cv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function Pv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function zv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function $v(t,n,e){var r=gv.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Dv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Rv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Fv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function qv(t,n,e){var r=gv.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function Uv(t,n,e){var r=gv.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Iv(t,n,e){var r=yv.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Ov(t,n,e){var r=gv.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function Bv(t,n,e){var r=gv.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function Yv(t,n){return _v(t.getDate(),n,2)}function Lv(t,n){return _v(t.getHours(),n,2)}function jv(t,n){return _v(t.getHours()%12||12,n,2)}function Hv(t,n){return _v(1+py.count(tv(t),t),n,3)}function Xv(t,n){return _v(t.getMilliseconds(),n,3)}function Gv(t,n){return Xv(t,n)+"000"}function Vv(t,n){return _v(t.getMonth()+1,n,2)}function Wv(t,n){return _v(t.getMinutes(),n,2)}function Zv(t,n){return _v(t.getSeconds(),n,2)}function Kv(t){var n=t.getDay();return 0===n?7:n}function Qv(t,n){return _v(xy.count(tv(t)-1,t),n,2)}function Jv(t){var n=t.getDay();return n>=4||0===n?Ay(t):Ay.ceil(t)}function t_(t,n){return t=Jv(t),_v(Ay.count(tv(t),t)+(4===tv(t).getDay()),n,2)}function n_(t){return t.getDay()}function e_(t,n){return _v(wy.count(tv(t)-1,t),n,2)}function r_(t,n){return _v(t.getFullYear()%100,n,2)}function i_(t,n){return _v((t=Jv(t)).getFullYear()%100,n,2)}function o_(t,n){return _v(t.getFullYear()%1e4,n,4)}function a_(t,n){var e=t.getDay();return _v((t=e>=4||0===e?Ay(t):Ay.ceil(t)).getFullYear()%1e4,n,4)}function u_(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+_v(n/60|0,"0",2)+_v(n%60,"0",2)}function c_(t,n){return _v(t.getUTCDate(),n,2)}function f_(t,n){return _v(t.getUTCHours(),n,2)}function s_(t,n){return _v(t.getUTCHours()%12||12,n,2)}function l_(t,n){return _v(1+yy.count(ev(t),t),n,3)}function h_(t,n){return _v(t.getUTCMilliseconds(),n,3)}function d_(t,n){return h_(t,n)+"000"}function p_(t,n){return _v(t.getUTCMonth()+1,n,2)}function g_(t,n){return _v(t.getUTCMinutes(),n,2)}function y_(t,n){return _v(t.getUTCSeconds(),n,2)}function v_(t){var n=t.getUTCDay();return 0===n?7:n}function __(t,n){return _v(Fy.count(ev(t)-1,t),n,2)}function b_(t){var n=t.getUTCDay();return n>=4||0===n?Oy(t):Oy.ceil(t)}function m_(t,n){return t=b_(t),_v(Oy.count(ev(t),t)+(4===ev(t).getUTCDay()),n,2)}function x_(t){return t.getUTCDay()}function w_(t,n){return _v(qy.count(ev(t)-1,t),n,2)}function M_(t,n){return _v(t.getUTCFullYear()%100,n,2)}function T_(t,n){return _v((t=b_(t)).getUTCFullYear()%100,n,2)}function A_(t,n){return _v(t.getUTCFullYear()%1e4,n,4)}function S_(t,n){var e=t.getUTCDay();return _v((t=e>=4||0===e?Oy(t):Oy.ceil(t)).getUTCFullYear()%1e4,n,4)}function E_(){return"+0000"}function N_(){return"%"}function k_(t){return+t}function C_(t){return Math.floor(+t/1e3)}function P_(n){return dv=hv(n),t.timeFormat=dv.format,t.timeParse=dv.parse,t.utcFormat=dv.utcFormat,t.utcParse=dv.utcParse,dv}t.timeFormat=void 0,t.timeParse=void 0,t.utcFormat=void 0,t.utcParse=void 0,P_({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var z_="%Y-%m-%dT%H:%M:%S.%LZ";var $_=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(z_),D_=$_;var R_=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(z_),F_=R_;function q_(t){return new Date(t)}function U_(t){return t instanceof Date?+t:+new Date(+t)}function I_(t,n,e,r,i,o,a,u,c,f){var s=Sg(),l=s.invert,h=s.domain,d=f(".%L"),p=f(":%S"),g=f("%I:%M"),y=f("%I %p"),v=f("%a %d"),_=f("%b %d"),b=f("%B"),m=f("%Y");function x(t){return(c(t)Fr(t[t.length-1]),ib=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(H_),ob=rb(ib),ab=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(H_),ub=rb(ab),cb=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(H_),fb=rb(cb),sb=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(H_),lb=rb(sb),hb=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(H_),db=rb(hb),pb=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(H_),gb=rb(pb),yb=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(H_),vb=rb(yb),_b=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(H_),bb=rb(_b),mb=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(H_),xb=rb(mb),wb=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(H_),Mb=rb(wb),Tb=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(H_),Ab=rb(Tb),Sb=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(H_),Eb=rb(Sb),Nb=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(H_),kb=rb(Nb),Cb=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(H_),Pb=rb(Cb),zb=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(H_),$b=rb(zb),Db=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(H_),Rb=rb(Db),Fb=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(H_),qb=rb(Fb),Ub=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(H_),Ib=rb(Ub),Ob=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(H_),Bb=rb(Ob),Yb=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(H_),Lb=rb(Yb),jb=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(H_),Hb=rb(jb),Xb=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(H_),Gb=rb(Xb),Vb=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(H_),Wb=rb(Vb),Zb=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(H_),Kb=rb(Zb),Qb=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(H_),Jb=rb(Qb),tm=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(H_),nm=rb(tm),em=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(H_),rm=rb(em);var im=hi(Tr(300,.5,0),Tr(-240,.5,1)),om=hi(Tr(-100,.75,.35),Tr(80,1.5,.8)),am=hi(Tr(260,.75,.35),Tr(80,1.5,.8)),um=Tr();var cm=Fe(),fm=Math.PI/3,sm=2*Math.PI/3;function lm(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}var hm=lm(H_("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),dm=lm(H_("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),pm=lm(H_("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),gm=lm(H_("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function ym(t){return function(){return t}}const vm=Math.abs,_m=Math.atan2,bm=Math.cos,mm=Math.max,xm=Math.min,wm=Math.sin,Mm=Math.sqrt,Tm=1e-12,Am=Math.PI,Sm=Am/2,Em=2*Am;function Nm(t){return t>=1?Sm:t<=-1?-Sm:Math.asin(t)}function km(t){let n=3;return t.digits=function(e){if(!arguments.length)return n;if(null==e)n=null;else{const t=Math.floor(e);if(!(t>=0))throw new RangeError(`invalid digits: ${e}`);n=t}return t},()=>new Ua(n)}function Cm(t){return t.innerRadius}function Pm(t){return t.outerRadius}function zm(t){return t.startAngle}function $m(t){return t.endAngle}function Dm(t){return t&&t.padAngle}function Rm(t,n,e,r,i,o,a){var u=t-e,c=n-r,f=(a?o:-o)/Mm(u*u+c*c),s=f*c,l=-f*u,h=t+s,d=n+l,p=e+s,g=r+l,y=(h+p)/2,v=(d+g)/2,_=p-h,b=g-d,m=_*_+b*b,x=i-o,w=h*g-p*d,M=(b<0?-1:1)*Mm(mm(0,x*x*m-w*w)),T=(w*b-_*M)/m,A=(-w*_-b*M)/m,S=(w*b+_*M)/m,E=(-w*_+b*M)/m,N=T-y,k=A-v,C=S-y,P=E-v;return N*N+k*k>C*C+P*P&&(T=S,A=E),{cx:T,cy:A,x01:-s,y01:-l,x11:T*(i/x-1),y11:A*(i/x-1)}}var Fm=Array.prototype.slice;function qm(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Um(t){this._context=t}function Im(t){return new Um(t)}function Om(t){return t[0]}function Bm(t){return t[1]}function Ym(t,n){var e=ym(!0),r=null,i=Im,o=null,a=km(u);function u(u){var c,f,s,l=(u=qm(u)).length,h=!1;for(null==r&&(o=i(s=a())),c=0;c<=l;++c)!(c=l;--h)u.point(v[h],_[h]);u.lineEnd(),u.areaEnd()}y&&(v[s]=+t(d,s,f),_[s]=+n(d,s,f),u.point(r?+r(d,s,f):v[s],e?+e(d,s,f):_[s]))}if(p)return u=null,p+""||null}function s(){return Ym().defined(i).curve(a).context(o)}return t="function"==typeof t?t:void 0===t?Om:ym(+t),n="function"==typeof n?n:ym(void 0===n?0:+n),e="function"==typeof e?e:void 0===e?Bm:ym(+e),f.x=function(n){return arguments.length?(t="function"==typeof n?n:ym(+n),r=null,f):t},f.x0=function(n){return arguments.length?(t="function"==typeof n?n:ym(+n),f):t},f.x1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:ym(+t),f):r},f.y=function(t){return arguments.length?(n="function"==typeof t?t:ym(+t),e=null,f):n},f.y0=function(t){return arguments.length?(n="function"==typeof t?t:ym(+t),f):n},f.y1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:ym(+t),f):e},f.lineX0=f.lineY0=function(){return s().x(t).y(n)},f.lineY1=function(){return s().x(t).y(e)},f.lineX1=function(){return s().x(r).y(n)},f.defined=function(t){return arguments.length?(i="function"==typeof t?t:ym(!!t),f):i},f.curve=function(t){return arguments.length?(a=t,null!=o&&(u=a(o)),f):a},f.context=function(t){return arguments.length?(null==t?o=u=null:u=a(o=t),f):o},f}function jm(t,n){return nt?1:n>=t?0:NaN}function Hm(t){return t}Um.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var Xm=Vm(Im);function Gm(t){this._curve=t}function Vm(t){function n(n){return new Gm(t(n))}return n._curve=t,n}function Wm(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(Vm(t)):n()._curve},t}function Zm(){return Wm(Ym().curve(Xm))}function Km(){var t=Lm().curve(Xm),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Wm(e())},delete t.lineX0,t.lineEndAngle=function(){return Wm(r())},delete t.lineX1,t.lineInnerRadius=function(){return Wm(i())},delete t.lineY0,t.lineOuterRadius=function(){return Wm(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(Vm(t)):n()._curve},t}function Qm(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}Gm.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};class Jm{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}class tx{constructor(t){this._context=t}lineStart(){this._point=0}lineEnd(){}point(t,n){if(t=+t,n=+n,0===this._point)this._point=1;else{const e=Qm(this._x0,this._y0),r=Qm(this._x0,this._y0=(this._y0+n)/2),i=Qm(t,this._y0),o=Qm(t,n);this._context.moveTo(...e),this._context.bezierCurveTo(...r,...i,...o)}this._x0=t,this._y0=n}}function nx(t){return new Jm(t,!0)}function ex(t){return new Jm(t,!1)}function rx(t){return new tx(t)}function ix(t){return t.source}function ox(t){return t.target}function ax(t){let n=ix,e=ox,r=Om,i=Bm,o=null,a=null,u=km(c);function c(){let c;const f=Fm.call(arguments),s=n.apply(this,f),l=e.apply(this,f);if(null==o&&(a=t(c=u())),a.lineStart(),f[0]=s,a.point(+r.apply(this,f),+i.apply(this,f)),f[0]=l,a.point(+r.apply(this,f),+i.apply(this,f)),a.lineEnd(),c)return a=null,c+""||null}return c.source=function(t){return arguments.length?(n=t,c):n},c.target=function(t){return arguments.length?(e=t,c):e},c.x=function(t){return arguments.length?(r="function"==typeof t?t:ym(+t),c):r},c.y=function(t){return arguments.length?(i="function"==typeof t?t:ym(+t),c):i},c.context=function(n){return arguments.length?(null==n?o=a=null:a=t(o=n),c):o},c}const ux=Mm(3);var cx={draw(t,n){const e=.59436*Mm(n+xm(n/28,.75)),r=e/2,i=r*ux;t.moveTo(0,e),t.lineTo(0,-e),t.moveTo(-i,-r),t.lineTo(i,r),t.moveTo(-i,r),t.lineTo(i,-r)}},fx={draw(t,n){const e=Mm(n/Am);t.moveTo(e,0),t.arc(0,0,e,0,Em)}},sx={draw(t,n){const e=Mm(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}};const lx=Mm(1/3),hx=2*lx;var dx={draw(t,n){const e=Mm(n/hx),r=e*lx;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},px={draw(t,n){const e=.62625*Mm(n);t.moveTo(0,-e),t.lineTo(e,0),t.lineTo(0,e),t.lineTo(-e,0),t.closePath()}},gx={draw(t,n){const e=.87559*Mm(n-xm(n/7,2));t.moveTo(-e,0),t.lineTo(e,0),t.moveTo(0,e),t.lineTo(0,-e)}},yx={draw(t,n){const e=Mm(n),r=-e/2;t.rect(r,r,e,e)}},vx={draw(t,n){const e=.4431*Mm(n);t.moveTo(e,e),t.lineTo(e,-e),t.lineTo(-e,-e),t.lineTo(-e,e),t.closePath()}};const _x=wm(Am/10)/wm(7*Am/10),bx=wm(Em/10)*_x,mx=-bm(Em/10)*_x;var xx={draw(t,n){const e=Mm(.8908130915292852*n),r=bx*e,i=mx*e;t.moveTo(0,-e),t.lineTo(r,i);for(let n=1;n<5;++n){const o=Em*n/5,a=bm(o),u=wm(o);t.lineTo(u*e,-a*e),t.lineTo(a*r-u*i,u*r+a*i)}t.closePath()}};const wx=Mm(3);var Mx={draw(t,n){const e=-Mm(n/(3*wx));t.moveTo(0,2*e),t.lineTo(-wx*e,-e),t.lineTo(wx*e,-e),t.closePath()}};const Tx=Mm(3);var Ax={draw(t,n){const e=.6824*Mm(n),r=e/2,i=e*Tx/2;t.moveTo(0,-e),t.lineTo(i,r),t.lineTo(-i,r),t.closePath()}};const Sx=-.5,Ex=Mm(3)/2,Nx=1/Mm(12),kx=3*(Nx/2+1);var Cx={draw(t,n){const e=Mm(n/kx),r=e/2,i=e*Nx,o=r,a=e*Nx+e,u=-o,c=a;t.moveTo(r,i),t.lineTo(o,a),t.lineTo(u,c),t.lineTo(Sx*r-Ex*i,Ex*r+Sx*i),t.lineTo(Sx*o-Ex*a,Ex*o+Sx*a),t.lineTo(Sx*u-Ex*c,Ex*u+Sx*c),t.lineTo(Sx*r+Ex*i,Sx*i-Ex*r),t.lineTo(Sx*o+Ex*a,Sx*a-Ex*o),t.lineTo(Sx*u+Ex*c,Sx*c-Ex*u),t.closePath()}},Px={draw(t,n){const e=.6189*Mm(n-xm(n/6,1.7));t.moveTo(-e,-e),t.lineTo(e,e),t.moveTo(-e,e),t.lineTo(e,-e)}};const zx=[fx,sx,dx,yx,xx,Mx,Cx],$x=[fx,gx,Px,Ax,cx,vx,px];function Dx(){}function Rx(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function Fx(t){this._context=t}function qx(t){this._context=t}function Ux(t){this._context=t}function Ix(t,n){this._basis=new Fx(t),this._beta=n}Fx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Rx(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Rx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},qx.prototype={areaStart:Dx,areaEnd:Dx,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:Rx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Ux.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:Rx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Ix.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],a=t[e]-i,u=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*a),this._beta*n[c]+(1-this._beta)*(o+r*u));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var Ox=function t(n){function e(t){return 1===n?new Fx(t):new Ix(t,n)}return e.beta=function(n){return t(+n)},e}(.85);function Bx(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function Yx(t,n){this._context=t,this._k=(1-n)/6}Yx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Bx(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:Bx(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Lx=function t(n){function e(t){return new Yx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function jx(t,n){this._context=t,this._k=(1-n)/6}jx.prototype={areaStart:Dx,areaEnd:Dx,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Bx(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Hx=function t(n){function e(t){return new jx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Xx(t,n){this._context=t,this._k=(1-n)/6}Xx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Bx(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Gx=function t(n){function e(t){return new Xx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Vx(t,n,e){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>Tm){var u=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*u-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*u-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>Tm){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,s=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-n*t._l12_2a)/s,a=(a*f+t._y1*t._l23_2a-e*t._l12_2a)/s}t._context.bezierCurveTo(r,i,o,a,t._x2,t._y2)}function Wx(t,n){this._context=t,this._alpha=n}Wx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Vx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Zx=function t(n){function e(t){return n?new Wx(t,n):new Yx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Kx(t,n){this._context=t,this._alpha=n}Kx.prototype={areaStart:Dx,areaEnd:Dx,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Vx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Qx=function t(n){function e(t){return n?new Kx(t,n):new jx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Jx(t,n){this._context=t,this._alpha=n}Jx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Vx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var tw=function t(n){function e(t){return n?new Jx(t,n):new Xx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function nw(t){this._context=t}function ew(t){return t<0?-1:1}function rw(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),a=(e-t._y1)/(i||r<0&&-0),u=(o*i+a*r)/(r+i);return(ew(o)+ew(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(u))||0}function iw(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function ow(t,n,e){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,u=(o-r)/3;t._context.bezierCurveTo(r+u,i+u*n,o-u,a-u*e,o,a)}function aw(t){this._context=t}function uw(t){this._context=new cw(t)}function cw(t){this._context=t}function fw(t){this._context=t}function sw(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(a[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,a=t[n[0]],u=a.length;o=0;)e[n]=n;return e}function pw(t,n){return t[n]}function gw(t){const n=[];return n.key=t,n}function yw(t){var n=t.map(vw);return dw(t).sort((function(t,e){return n[t]-n[e]}))}function vw(t){for(var n,e=-1,r=0,i=t.length,o=-1/0;++eo&&(o=n,r=e);return r}function _w(t){var n=t.map(bw);return dw(t).sort((function(t,e){return n[t]-n[e]}))}function bw(t){for(var n,e=0,r=-1,i=t.length;++r=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var mw=t=>()=>t;function xw(t,{sourceEvent:n,target:e,transform:r,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:i}})}function ww(t,n,e){this.k=t,this.x=n,this.y=e}ww.prototype={constructor:ww,scale:function(t){return 1===t?this:new ww(this.k*t,this.x,this.y)},translate:function(t,n){return 0===t&0===n?this:new ww(this.k,this.x+this.k*t,this.y+this.k*n)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Mw=new ww(1,0,0);function Tw(t){for(;!t.__zoom;)if(!(t=t.parentNode))return Mw;return t.__zoom}function Aw(t){t.stopImmediatePropagation()}function Sw(t){t.preventDefault(),t.stopImmediatePropagation()}function Ew(t){return!(t.ctrlKey&&"wheel"!==t.type||t.button)}function Nw(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function kw(){return this.__zoom||Mw}function Cw(t){return-t.deltaY*(1===t.deltaMode?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function Pw(){return navigator.maxTouchPoints||"ontouchstart"in this}function zw(t,n,e){var r=t.invertX(n[0][0])-e[0][0],i=t.invertX(n[1][0])-e[1][0],o=t.invertY(n[0][1])-e[0][1],a=t.invertY(n[1][1])-e[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}Tw.prototype=ww.prototype,t.Adder=T,t.Delaunay=Lu,t.FormatSpecifier=tf,t.InternMap=InternMap,t.InternSet=InternSet,t.Node=Qd,t.Path=Ua,t.Voronoi=qu,t.ZoomTransform=ww,t.active=function(t,n){var e,r,i=t.__transition;if(i)for(r in n=null==n?null:n+"",i)if((e=i[r]).state>qi&&e.name===n)return new po([[t]],Zo,n,+r);return null},t.arc=function(){var t=Cm,n=Pm,e=ym(0),r=null,i=zm,o=$m,a=Dm,u=null,c=km(f);function f(){var f,s,l=+t.apply(this,arguments),h=+n.apply(this,arguments),d=i.apply(this,arguments)-Sm,p=o.apply(this,arguments)-Sm,g=vm(p-d),y=p>d;if(u||(u=f=c()),hTm)if(g>Em-Tm)u.moveTo(h*bm(d),h*wm(d)),u.arc(0,0,h,d,p,!y),l>Tm&&(u.moveTo(l*bm(p),l*wm(p)),u.arc(0,0,l,p,d,y));else{var v,_,b=d,m=p,x=d,w=p,M=g,T=g,A=a.apply(this,arguments)/2,S=A>Tm&&(r?+r.apply(this,arguments):Mm(l*l+h*h)),E=xm(vm(h-l)/2,+e.apply(this,arguments)),N=E,k=E;if(S>Tm){var C=Nm(S/l*wm(A)),P=Nm(S/h*wm(A));(M-=2*C)>Tm?(x+=C*=y?1:-1,w-=C):(M=0,x=w=(d+p)/2),(T-=2*P)>Tm?(b+=P*=y?1:-1,m-=P):(T=0,b=m=(d+p)/2)}var z=h*bm(b),$=h*wm(b),D=l*bm(w),R=l*wm(w);if(E>Tm){var F,q=h*bm(m),U=h*wm(m),I=l*bm(x),O=l*wm(x);if(g1?0:t<-1?Am:Math.acos(t)}((B*L+Y*j)/(Mm(B*B+Y*Y)*Mm(L*L+j*j)))/2),X=Mm(F[0]*F[0]+F[1]*F[1]);N=xm(E,(l-X)/(H-1)),k=xm(E,(h-X)/(H+1))}else N=k=0}T>Tm?k>Tm?(v=Rm(I,O,z,$,h,k,y),_=Rm(q,U,D,R,h,k,y),u.moveTo(v.cx+v.x01,v.cy+v.y01),kTm&&M>Tm?N>Tm?(v=Rm(D,R,q,U,l,-N,y),_=Rm(z,$,I,O,l,-N,y),u.lineTo(v.cx+v.x01,v.cy+v.y01),N=0))throw new RangeError("invalid r");let e=t.length;if(!((e=Math.floor(e))>=0))throw new RangeError("invalid length");if(!e||!n)return t;const r=y(n),i=t.slice();return r(t,i,0,e,1),r(i,t,0,e,1),r(t,i,0,e,1),t},t.blur2=l,t.blurImage=h,t.brush=function(){return wa(la)},t.brushSelection=function(t){var n=t.__brush;return n?n.dim.output(n.selection):null},t.brushX=function(){return wa(fa)},t.brushY=function(){return wa(sa)},t.buffer=function(t,n){return fetch(t,n).then(_c)},t.chord=function(){return za(!1,!1)},t.chordDirected=function(){return za(!0,!1)},t.chordTranspose=function(){return za(!1,!0)},t.cluster=function(){var t=Ld,n=1,e=1,r=!1;function i(i){var o,a=0;i.eachAfter((function(n){var e=n.children;e?(n.x=function(t){return t.reduce(jd,0)/t.length}(e),n.y=function(t){return 1+t.reduce(Hd,0)}(e)):(n.x=o?a+=t(n,o):0,n.y=0,o=n)}));var u=function(t){for(var n;n=t.children;)t=n[0];return t}(i),c=function(t){for(var n;n=t.children;)t=n[n.length-1];return t}(i),f=u.x-t(u,c)/2,s=c.x+t(c,u)/2;return i.eachAfter(r?function(t){t.x=(t.x-i.x)*n,t.y=(i.y-t.y)*e}:function(t){t.x=(t.x-f)/(s-f)*n,t.y=(1-(i.y?t.y/i.y:1))*e})}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.color=ze,t.contourDensity=function(){var t=fu,n=su,e=lu,r=960,i=500,o=20,a=2,u=3*o,c=r+2*u>>a,f=i+2*u>>a,s=Qa(20);function h(r){var i=new Float32Array(c*f),s=Math.pow(2,-a),h=-1;for(const o of r){var d=(t(o,++h,r)+u)*s,p=(n(o,h,r)+u)*s,g=+e(o,h,r);if(g&&d>=0&&d=0&&pt*r)))(n).map(((t,n)=>(t.value=+e[n],p(t))))}function p(t){return t.coordinates.forEach(g),t}function g(t){t.forEach(y)}function y(t){t.forEach(v)}function v(t){t[0]=t[0]*Math.pow(2,a)-u,t[1]=t[1]*Math.pow(2,a)-u}function _(){return c=r+2*(u=3*o)>>a,f=i+2*u>>a,d}return d.contours=function(t){var n=h(t),e=iu().size([c,f]),r=Math.pow(2,2*a),i=t=>{t=+t;var i=p(e.contour(n,t*r));return i.value=t,i};return Object.defineProperty(i,"max",{get:()=>J(n)/r}),i},d.x=function(n){return arguments.length?(t="function"==typeof n?n:Qa(+n),d):t},d.y=function(t){return arguments.length?(n="function"==typeof t?t:Qa(+t),d):n},d.weight=function(t){return arguments.length?(e="function"==typeof t?t:Qa(+t),d):e},d.size=function(t){if(!arguments.length)return[r,i];var n=+t[0],e=+t[1];if(!(n>=0&&e>=0))throw new Error("invalid size");return r=n,i=e,_()},d.cellSize=function(t){if(!arguments.length)return 1<=1))throw new Error("invalid cell size");return a=Math.floor(Math.log(t)/Math.LN2),_()},d.thresholds=function(t){return arguments.length?(s="function"==typeof t?t:Array.isArray(t)?Qa(Za.call(t)):Qa(t),d):s},d.bandwidth=function(t){if(!arguments.length)return Math.sqrt(o*(o+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return o=(Math.sqrt(4*t*t+1)-1)/2,_()},d},t.contours=iu,t.count=v,t.create=function(t){return Zn(Yt(t).call(document.documentElement))},t.creator=Yt,t.cross=function(...t){const n="function"==typeof t[t.length-1]&&function(t){return n=>t(...n)}(t.pop()),e=(t=t.map(m)).map(_),r=t.length-1,i=new Array(r+1).fill(0),o=[];if(r<0||e.some(b))return o;for(;;){o.push(i.map(((n,e)=>t[e][n])));let a=r;for(;++i[a]===e[a];){if(0===a)return n?o.map(n):o;i[a--]=0}}},t.csv=wc,t.csvFormat=rc,t.csvFormatBody=ic,t.csvFormatRow=ac,t.csvFormatRows=oc,t.csvFormatValue=uc,t.csvParse=nc,t.csvParseRows=ec,t.cubehelix=Tr,t.cumsum=function(t,n){var e=0,r=0;return Float64Array.from(t,void 0===n?t=>e+=+t||0:i=>e+=+n(i,r++,t)||0)},t.curveBasis=function(t){return new Fx(t)},t.curveBasisClosed=function(t){return new qx(t)},t.curveBasisOpen=function(t){return new Ux(t)},t.curveBumpX=nx,t.curveBumpY=ex,t.curveBundle=Ox,t.curveCardinal=Lx,t.curveCardinalClosed=Hx,t.curveCardinalOpen=Gx,t.curveCatmullRom=Zx,t.curveCatmullRomClosed=Qx,t.curveCatmullRomOpen=tw,t.curveLinear=Im,t.curveLinearClosed=function(t){return new nw(t)},t.curveMonotoneX=function(t){return new aw(t)},t.curveMonotoneY=function(t){return new uw(t)},t.curveNatural=function(t){return new fw(t)},t.curveStep=function(t){return new lw(t,.5)},t.curveStepAfter=function(t){return new lw(t,1)},t.curveStepBefore=function(t){return new lw(t,0)},t.descending=e,t.deviation=w,t.difference=function(t,...n){t=new InternSet(t);for(const e of n)for(const n of e)t.delete(n);return t},t.disjoint=function(t,n){const e=n[Symbol.iterator](),r=new InternSet;for(const n of t){if(r.has(n))return!1;let t,i;for(;({value:t,done:i}=e.next())&&!i;){if(Object.is(n,t))return!1;r.add(t)}}return!0},t.dispatch=$t,t.drag=function(){var t,n,e,r,i=se,o=le,a=he,u=de,c={},f=$t("start","drag","end"),s=0,l=0;function h(t){t.on("mousedown.drag",d).filter(u).on("touchstart.drag",y).on("touchmove.drag",v,ee).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(a,u){if(!r&&i.call(this,a,u)){var c=b(this,o.call(this,a,u),a,u,"mouse");c&&(Zn(a.view).on("mousemove.drag",p,re).on("mouseup.drag",g,re),ae(a.view),ie(a),e=!1,t=a.clientX,n=a.clientY,c("start",a))}}function p(r){if(oe(r),!e){var i=r.clientX-t,o=r.clientY-n;e=i*i+o*o>l}c.mouse("drag",r)}function g(t){Zn(t.view).on("mousemove.drag mouseup.drag",null),ue(t.view,e),oe(t),c.mouse("end",t)}function y(t,n){if(i.call(this,t,n)){var e,r,a=t.changedTouches,u=o.call(this,t,n),c=a.length;for(e=0;e+t,t.easePoly=wo,t.easePolyIn=mo,t.easePolyInOut=wo,t.easePolyOut=xo,t.easeQuad=_o,t.easeQuadIn=function(t){return t*t},t.easeQuadInOut=_o,t.easeQuadOut=function(t){return t*(2-t)},t.easeSin=Ao,t.easeSinIn=function(t){return 1==+t?1:1-Math.cos(t*To)},t.easeSinInOut=Ao,t.easeSinOut=function(t){return Math.sin(t*To)},t.every=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(!n(r,++e,t))return!1;return!0},t.extent=M,t.fcumsum=function(t,n){const e=new T;let r=-1;return Float64Array.from(t,void 0===n?t=>e.add(+t||0):i=>e.add(+n(i,++r,t)||0))},t.filter=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");const e=[];let r=-1;for(const i of t)n(i,++r,t)&&e.push(i);return e},t.flatGroup=function(t,...n){return z(P(t,...n),n)},t.flatRollup=function(t,n,...e){return z(D(t,n,...e),e)},t.forceCenter=function(t,n){var e,r=1;function i(){var i,o,a=e.length,u=0,c=0;for(i=0;if+p||os+p||ac.index){var g=f-u.x-u.vx,y=s-u.y-u.vy,v=g*g+y*y;vt.r&&(t.r=t[n].r)}function c(){if(n){var r,i,o=n.length;for(e=new Array(o),r=0;r[u(t,n,r),t])));for(a=0,i=new Array(f);a=u)){(t.data!==n||t.next)&&(0===l&&(p+=(l=Uc(e))*l),0===h&&(p+=(h=Uc(e))*h),p(t=(Lc*t+jc)%Hc)/Hc}();function l(){h(),f.call("tick",n),e1?(null==e?u.delete(t):u.set(t,p(e)),n):u.get(t)},find:function(n,e,r){var i,o,a,u,c,f=0,s=t.length;for(null==r?r=1/0:r*=r,f=0;f1?(f.on(t,e),n):f.on(t)}}},t.forceX=function(t){var n,e,r,i=qc(.1);function o(t){for(var i,o=0,a=n.length;o=.12&&i<.234&&r>=-.425&&r<-.214?u:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:a).invert(t)},s.stream=function(e){return t&&n===e?t:(r=[a.stream(n=e),u.stream(e),c.stream(e)],i=r.length,t={point:function(t,n){for(var e=-1;++ejs(r[0],r[1])&&(r[1]=i[1]),js(i[0],r[1])>js(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(u=js(r[1],i[0]))>a&&(a=u,Wf=i[0],Kf=r[1])}return is=os=null,Wf===1/0||Zf===1/0?[[NaN,NaN],[NaN,NaN]]:[[Wf,Zf],[Kf,Qf]]},t.geoCentroid=function(t){ms=xs=ws=Ms=Ts=As=Ss=Es=0,Ns=new T,ks=new T,Cs=new T,Lf(t,Gs);var n=+Ns,e=+ks,r=+Cs,i=Ef(n,e,r);return i=0))throw new RangeError(`invalid digits: ${t}`);i=n}return null===n&&(r=new ed(i)),a},a.projection(t).digits(i).context(n)},t.geoProjection=yd,t.geoProjectionMutator=vd,t.geoRotation=ll,t.geoStereographic=function(){return yd(Bd).scale(250).clipAngle(142)},t.geoStereographicRaw=Bd,t.geoStream=Lf,t.geoTransform=function(t){return{stream:id(t)}},t.geoTransverseMercator=function(){var t=Ed(Yd),n=t.center,e=t.rotate;return t.center=function(t){return arguments.length?n([-t[1],t[0]]):[(t=n())[1],-t[0]]},t.rotate=function(t){return arguments.length?e([t[0],t[1],t.length>2?t[2]+90:90]):[(t=e())[0],t[1],t[2]-90]},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=Yd,t.gray=function(t,n){return new ur(t,0,0,null==n?1:n)},t.greatest=ot,t.greatestIndex=function(t,e=n){if(1===e.length)return tt(t,e);let r,i=-1,o=-1;for(const n of t)++o,(i<0?0===e(n,n):e(n,r)>0)&&(r=n,i=o);return i},t.group=C,t.groupSort=function(t,e,r){return(2!==e.length?U($(t,e,r),(([t,e],[r,i])=>n(e,i)||n(t,r))):U(C(t,r),(([t,r],[i,o])=>e(r,o)||n(t,i)))).map((([t])=>t))},t.groups=P,t.hcl=dr,t.hierarchy=Gd,t.histogram=Q,t.hsl=He,t.html=Ec,t.image=function(t,n){return new Promise((function(e,r){var i=new Image;for(var o in n)i[o]=n[o];i.onerror=r,i.onload=function(){e(i)},i.src=t}))},t.index=function(t,...n){return F(t,k,R,n)},t.indexes=function(t,...n){return F(t,Array.from,R,n)},t.interpolate=Gr,t.interpolateArray=function(t,n){return(Ir(n)?Ur:Or)(t,n)},t.interpolateBasis=Er,t.interpolateBasisClosed=Nr,t.interpolateBlues=Gb,t.interpolateBrBG=ob,t.interpolateBuGn=Mb,t.interpolateBuPu=Ab,t.interpolateCividis=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},t.interpolateCool=am,t.interpolateCubehelix=li,t.interpolateCubehelixDefault=im,t.interpolateCubehelixLong=hi,t.interpolateDate=Br,t.interpolateDiscrete=function(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}},t.interpolateGnBu=Eb,t.interpolateGreens=Wb,t.interpolateGreys=Kb,t.interpolateHcl=ci,t.interpolateHclLong=fi,t.interpolateHsl=oi,t.interpolateHslLong=ai,t.interpolateHue=function(t,n){var e=Pr(+t,+n);return function(t){var n=e(t);return n-360*Math.floor(n/360)}},t.interpolateInferno=pm,t.interpolateLab=function(t,n){var e=$r((t=ar(t)).l,(n=ar(n)).l),r=$r(t.a,n.a),i=$r(t.b,n.b),o=$r(t.opacity,n.opacity);return function(n){return t.l=e(n),t.a=r(n),t.b=i(n),t.opacity=o(n),t+""}},t.interpolateMagma=dm,t.interpolateNumber=Yr,t.interpolateNumberArray=Ur,t.interpolateObject=Lr,t.interpolateOrRd=kb,t.interpolateOranges=rm,t.interpolatePRGn=ub,t.interpolatePiYG=fb,t.interpolatePlasma=gm,t.interpolatePuBu=$b,t.interpolatePuBuGn=Pb,t.interpolatePuOr=lb,t.interpolatePuRd=Rb,t.interpolatePurples=Jb,t.interpolateRainbow=function(t){(t<0||t>1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return um.h=360*t-100,um.s=1.5-1.5*n,um.l=.8-.9*n,um+""},t.interpolateRdBu=db,t.interpolateRdGy=gb,t.interpolateRdPu=qb,t.interpolateRdYlBu=vb,t.interpolateRdYlGn=bb,t.interpolateReds=nm,t.interpolateRgb=Dr,t.interpolateRgbBasis=Fr,t.interpolateRgbBasisClosed=qr,t.interpolateRound=Vr,t.interpolateSinebow=function(t){var n;return t=(.5-t)*Math.PI,cm.r=255*(n=Math.sin(t))*n,cm.g=255*(n=Math.sin(t+fm))*n,cm.b=255*(n=Math.sin(t+sm))*n,cm+""},t.interpolateSpectral=xb,t.interpolateString=Xr,t.interpolateTransformCss=ti,t.interpolateTransformSvg=ni,t.interpolateTurbo=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"},t.interpolateViridis=hm,t.interpolateWarm=om,t.interpolateYlGn=Bb,t.interpolateYlGnBu=Ib,t.interpolateYlOrBr=Lb,t.interpolateYlOrRd=Hb,t.interpolateZoom=ri,t.interrupt=Gi,t.intersection=function(t,...n){t=new InternSet(t),n=n.map(vt);t:for(const e of t)for(const r of n)if(!r.has(e)){t.delete(e);continue t}return t},t.interval=function(t,n,e){var r=new Ei,i=n;return null==n?(r.restart(t,n,e),r):(r._restart=r.restart,r.restart=function(t,n,e){n=+n,e=null==e?Ai():+e,r._restart((function o(a){a+=i,r._restart(o,i+=n,e),t(a)}),n,e)},r.restart(t,n,e),r)},t.isoFormat=D_,t.isoParse=F_,t.json=function(t,n){return fetch(t,n).then(Tc)},t.lab=ar,t.lch=function(t,n,e,r){return 1===arguments.length?hr(t):new pr(e,n,t,null==r?1:r)},t.least=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)<0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)<0:0===e(n,n))&&(r=n,i=!0);return r},t.leastIndex=ht,t.line=Ym,t.lineRadial=Zm,t.link=ax,t.linkHorizontal=function(){return ax(nx)},t.linkRadial=function(){const t=ax(rx);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.linkVertical=function(){return ax(ex)},t.local=Qn,t.map=function(t,n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");if("function"!=typeof n)throw new TypeError("mapper is not a function");return Array.from(t,((e,r)=>n(e,r,t)))},t.matcher=Vt,t.max=J,t.maxIndex=tt,t.mean=function(t,n){let e=0,r=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(++e,r+=n);else{let i=-1;for(let o of t)null!=(o=n(o,++i,t))&&(o=+o)>=o&&(++e,r+=o)}if(e)return r/e},t.median=function(t,n){return at(t,.5,n)},t.medianIndex=function(t,n){return ct(t,.5,n)},t.merge=ft,t.min=nt,t.minIndex=et,t.mode=function(t,n){const e=new InternMap;if(void 0===n)for(let n of t)null!=n&&n>=n&&e.set(n,(e.get(n)||0)+1);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&i>=i&&e.set(i,(e.get(i)||0)+1)}let r,i=0;for(const[t,n]of e)n>i&&(i=n,r=t);return r},t.namespace=It,t.namespaces=Ut,t.nice=Z,t.now=Ai,t.pack=function(){var t=null,n=1,e=1,r=np;function i(i){const o=ap();return i.x=n/2,i.y=e/2,t?i.eachBefore(xp(t)).eachAfter(wp(r,.5,o)).eachBefore(Mp(1)):i.eachBefore(xp(mp)).eachAfter(wp(np,1,o)).eachAfter(wp(r,i.r/Math.min(n,e),o)).eachBefore(Mp(Math.min(n,e)/(2*i.r))),i}return i.radius=function(n){return arguments.length?(t=Jd(n),i):t},i.size=function(t){return arguments.length?(n=+t[0],e=+t[1],i):[n,e]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:ep(+t),i):r},i},t.packEnclose=function(t){return up(t,ap())},t.packSiblings=function(t){return bp(t,ap()),t},t.pairs=function(t,n=st){const e=[];let r,i=!1;for(const o of t)i&&e.push(n(r,o)),r=o,i=!0;return e},t.partition=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&Ap(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a0&&(d+=l);for(null!=n?p.sort((function(t,e){return n(g[t],g[e])})):null!=e&&p.sort((function(t,n){return e(a[t],a[n])})),u=0,f=d?(v-h*b)/d:0;u0?l*f:0)+b,g[c]={data:a[c],index:u,value:l,startAngle:y,endAngle:s,padAngle:_};return g}return a.value=function(n){return arguments.length?(t="function"==typeof n?n:ym(+n),a):t},a.sortValues=function(t){return arguments.length?(n=t,e=null,a):n},a.sort=function(t){return arguments.length?(e=t,n=null,a):e},a.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:ym(+t),a):r},a.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:ym(+t),a):i},a.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:ym(+t),a):o},a},t.piecewise=di,t.pointRadial=Qm,t.pointer=ne,t.pointers=function(t,n){return t.target&&(t=te(t),void 0===n&&(n=t.currentTarget),t=t.touches||[t]),Array.from(t,(t=>ne(t,n)))},t.polygonArea=function(t){for(var n,e=-1,r=t.length,i=t[r-1],o=0;++eu!=f>u&&a<(c-e)*(u-r)/(f-r)+e&&(s=!s),c=e,f=r;return s},t.polygonHull=function(t){if((e=t.length)<3)return null;var n,e,r=new Array(e),i=new Array(e);for(n=0;n=0;--n)f.push(t[r[o[n]][2]]);for(n=+u;n(n=1664525*n+1013904223|0,lg*(n>>>0))},t.randomLogNormal=Kp,t.randomLogistic=fg,t.randomNormal=Zp,t.randomPareto=ng,t.randomPoisson=sg,t.randomUniform=Vp,t.randomWeibull=ug,t.range=lt,t.rank=function(t,e=n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");let r=Array.from(t);const i=new Float64Array(r.length);2!==e.length&&(r=r.map(e),e=n);const o=(t,n)=>e(r[t],r[n]);let a,u;return(t=Uint32Array.from(r,((t,n)=>n))).sort(e===n?(t,n)=>O(r[t],r[n]):I(o)),t.forEach(((t,n)=>{const e=o(t,void 0===a?t:a);e>=0?((void 0===a||e>0)&&(a=t,u=n),i[t]=u):i[t]=NaN})),i},t.reduce=function(t,n,e){if("function"!=typeof n)throw new TypeError("reducer is not a function");const r=t[Symbol.iterator]();let i,o,a=-1;if(arguments.length<3){if(({done:i,value:e}=r.next()),i)return;++a}for(;({done:i,value:o}=r.next()),!i;)e=n(e,o,++a,t);return e},t.reverse=function(t){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");return Array.from(t).reverse()},t.rgb=Fe,t.ribbon=function(){return Wa()},t.ribbonArrow=function(){return Wa(Va)},t.rollup=$,t.rollups=D,t.scaleBand=yg,t.scaleDiverging=function t(){var n=Ng(L_()(mg));return n.copy=function(){return B_(n,t())},dg.apply(n,arguments)},t.scaleDivergingLog=function t(){var n=Fg(L_()).domain([.1,1,10]);return n.copy=function(){return B_(n,t()).base(n.base())},dg.apply(n,arguments)},t.scaleDivergingPow=j_,t.scaleDivergingSqrt=function(){return j_.apply(null,arguments).exponent(.5)},t.scaleDivergingSymlog=function t(){var n=Ig(L_());return n.copy=function(){return B_(n,t()).constant(n.constant())},dg.apply(n,arguments)},t.scaleIdentity=function t(n){var e;function r(t){return null==t||isNaN(t=+t)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,_g),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,_g):[0,1],Ng(r)},t.scaleImplicit=pg,t.scaleLinear=function t(){var n=Sg();return n.copy=function(){return Tg(n,t())},hg.apply(n,arguments),Ng(n)},t.scaleLog=function t(){const n=Fg(Ag()).domain([1,10]);return n.copy=()=>Tg(n,t()).base(n.base()),hg.apply(n,arguments),n},t.scaleOrdinal=gg,t.scalePoint=function(){return vg(yg.apply(null,arguments).paddingInner(1))},t.scalePow=jg,t.scaleQuantile=function t(){var e,r=[],i=[],o=[];function a(){var t=0,n=Math.max(1,i.length);for(o=new Array(n-1);++t0?o[n-1]:r[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},u.unknown=function(t){return arguments.length?(n=t,u):u},u.thresholds=function(){return o.slice()},u.copy=function(){return t().domain([e,r]).range(a).unknown(n)},hg.apply(Ng(u),arguments)},t.scaleRadial=function t(){var n,e=Sg(),r=[0,1],i=!1;function o(t){var r=function(t){return Math.sign(t)*Math.sqrt(Math.abs(t))}(e(t));return isNaN(r)?n:i?Math.round(r):r}return o.invert=function(t){return e.invert(Hg(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,_g)).map(Hg)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},hg.apply(o,arguments),Ng(o)},t.scaleSequential=function t(){var n=Ng(O_()(mg));return n.copy=function(){return B_(n,t())},dg.apply(n,arguments)},t.scaleSequentialLog=function t(){var n=Fg(O_()).domain([1,10]);return n.copy=function(){return B_(n,t()).base(n.base())},dg.apply(n,arguments)},t.scaleSequentialPow=Y_,t.scaleSequentialQuantile=function t(){var e=[],r=mg;function i(t){if(null!=t&&!isNaN(t=+t))return r((s(e,t,1)-1)/(e.length-1))}return i.domain=function(t){if(!arguments.length)return e.slice();e=[];for(let n of t)null==n||isNaN(n=+n)||e.push(n);return e.sort(n),i},i.interpolator=function(t){return arguments.length?(r=t,i):r},i.range=function(){return e.map(((t,n)=>r(n/(e.length-1))))},i.quantiles=function(t){return Array.from({length:t+1},((n,r)=>at(e,r/t)))},i.copy=function(){return t(r).domain(e)},dg.apply(i,arguments)},t.scaleSequentialSqrt=function(){return Y_.apply(null,arguments).exponent(.5)},t.scaleSequentialSymlog=function t(){var n=Ig(O_());return n.copy=function(){return B_(n,t()).constant(n.constant())},dg.apply(n,arguments)},t.scaleSqrt=function(){return jg.apply(null,arguments).exponent(.5)},t.scaleSymlog=function t(){var n=Ig(Ag());return n.copy=function(){return Tg(n,t()).constant(n.constant())},hg.apply(n,arguments)},t.scaleThreshold=function t(){var n,e=[.5],r=[0,1],i=1;function o(t){return null!=t&&t<=t?r[s(e,t,0,i)]:n}return o.domain=function(t){return arguments.length?(e=Array.from(t),i=Math.min(e.length,r.length-1),o):e.slice()},o.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),o):r.slice()},o.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t().domain(e).range(r).unknown(n)},hg.apply(o,arguments)},t.scaleTime=function(){return hg.apply(I_(uv,cv,tv,Zy,xy,py,sy,ay,iy,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},t.scaleUtc=function(){return hg.apply(I_(ov,av,ev,Qy,Fy,yy,hy,cy,iy,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},t.scan=function(t,n){const e=ht(t,n);return e<0?void 0:e},t.schemeAccent=G_,t.schemeBlues=Xb,t.schemeBrBG=ib,t.schemeBuGn=wb,t.schemeBuPu=Tb,t.schemeCategory10=X_,t.schemeDark2=V_,t.schemeGnBu=Sb,t.schemeGreens=Vb,t.schemeGreys=Zb,t.schemeObservable10=W_,t.schemeOrRd=Nb,t.schemeOranges=em,t.schemePRGn=ab,t.schemePaired=Z_,t.schemePastel1=K_,t.schemePastel2=Q_,t.schemePiYG=cb,t.schemePuBu=zb,t.schemePuBuGn=Cb,t.schemePuOr=sb,t.schemePuRd=Db,t.schemePurples=Qb,t.schemeRdBu=hb,t.schemeRdGy=pb,t.schemeRdPu=Fb,t.schemeRdYlBu=yb,t.schemeRdYlGn=_b,t.schemeReds=tm,t.schemeSet1=J_,t.schemeSet2=tb,t.schemeSet3=nb,t.schemeSpectral=mb,t.schemeTableau10=eb,t.schemeYlGn=Ob,t.schemeYlGnBu=Ub,t.schemeYlOrBr=Yb,t.schemeYlOrRd=jb,t.select=Zn,t.selectAll=function(t){return"string"==typeof t?new Vn([document.querySelectorAll(t)],[document.documentElement]):new Vn([Ht(t)],Gn)},t.selection=Wn,t.selector=jt,t.selectorAll=Gt,t.shuffle=dt,t.shuffler=pt,t.some=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(n(r,++e,t))return!0;return!1},t.sort=U,t.stack=function(){var t=ym([]),n=dw,e=hw,r=pw;function i(i){var o,a,u=Array.from(t.apply(this,arguments),gw),c=u.length,f=-1;for(const t of i)for(o=0,++f;o0)for(var e,r,i,o,a,u,c=0,f=t[n[0]].length;c0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):(r[0]=0,r[1]=i)},t.stackOffsetExpand=function(t,n){if((r=t.length)>0){for(var e,r,i,o=0,a=t[0].length;o0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,a=1;afunction(t){t=`${t}`;let n=t.length;zp(t,n-1)&&!zp(t,n-2)&&(t=t.slice(0,-1));return"/"===t[0]?t:`/${t}`}(t(n,e,r)))),e=n.map(Pp),i=new Set(n).add("");for(const t of e)i.has(t)||(i.add(t),n.push(t),e.push(Pp(t)),h.push(Np));d=(t,e)=>n[e],p=(t,n)=>e[n]}for(a=0,i=h.length;a=0&&(f=h[t]).data===Np;--t)f.data=null}if(u.parent=Sp,u.eachBefore((function(t){t.depth=t.parent.depth+1,--i})).eachBefore(Kd),u.parent=null,i>0)throw new Error("cycle");return u}return r.id=function(t){return arguments.length?(n=Jd(t),r):n},r.parentId=function(t){return arguments.length?(e=Jd(t),r):e},r.path=function(n){return arguments.length?(t=Jd(n),r):t},r},t.style=_n,t.subset=function(t,n){return _t(n,t)},t.sum=function(t,n){let e=0;if(void 0===n)for(let n of t)(n=+n)&&(e+=n);else{let r=-1;for(let i of t)(i=+n(i,++r,t))&&(e+=i)}return e},t.superset=_t,t.svg=Nc,t.symbol=function(t,n){let e=null,r=km(i);function i(){let i;if(e||(e=i=r()),t.apply(this,arguments).draw(e,+n.apply(this,arguments)),i)return e=null,i+""||null}return t="function"==typeof t?t:ym(t||fx),n="function"==typeof n?n:ym(void 0===n?64:+n),i.type=function(n){return arguments.length?(t="function"==typeof n?n:ym(n),i):t},i.size=function(t){return arguments.length?(n="function"==typeof t?t:ym(+t),i):n},i.context=function(t){return arguments.length?(e=null==t?null:t,i):e},i},t.symbolAsterisk=cx,t.symbolCircle=fx,t.symbolCross=sx,t.symbolDiamond=dx,t.symbolDiamond2=px,t.symbolPlus=gx,t.symbolSquare=yx,t.symbolSquare2=vx,t.symbolStar=xx,t.symbolTimes=Px,t.symbolTriangle=Mx,t.symbolTriangle2=Ax,t.symbolWye=Cx,t.symbolX=Px,t.symbols=zx,t.symbolsFill=zx,t.symbolsStroke=$x,t.text=mc,t.thresholdFreedmanDiaconis=function(t,n,e){const r=v(t),i=at(t,.75)-at(t,.25);return r&&i?Math.ceil((e-n)/(2*i*Math.pow(r,-1/3))):1},t.thresholdScott=function(t,n,e){const r=v(t),i=w(t);return r&&i?Math.ceil((e-n)*Math.cbrt(r)/(3.49*i)):1},t.thresholdSturges=K,t.tickFormat=Eg,t.tickIncrement=V,t.tickStep=W,t.ticks=G,t.timeDay=py,t.timeDays=gy,t.timeFormatDefaultLocale=P_,t.timeFormatLocale=hv,t.timeFriday=Sy,t.timeFridays=$y,t.timeHour=sy,t.timeHours=ly,t.timeInterval=Vg,t.timeMillisecond=Wg,t.timeMilliseconds=Zg,t.timeMinute=ay,t.timeMinutes=uy,t.timeMonday=wy,t.timeMondays=ky,t.timeMonth=Zy,t.timeMonths=Ky,t.timeSaturday=Ey,t.timeSaturdays=Dy,t.timeSecond=iy,t.timeSeconds=oy,t.timeSunday=xy,t.timeSundays=Ny,t.timeThursday=Ay,t.timeThursdays=zy,t.timeTickInterval=cv,t.timeTicks=uv,t.timeTuesday=My,t.timeTuesdays=Cy,t.timeWednesday=Ty,t.timeWednesdays=Py,t.timeWeek=xy,t.timeWeeks=Ny,t.timeYear=tv,t.timeYears=nv,t.timeout=$i,t.timer=Ni,t.timerFlush=ki,t.transition=go,t.transpose=gt,t.tree=function(){var t=$p,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,a=new Up(t,0),u=[a];n=u.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)u.push(e=n.children[i]=new Up(r[i],i)),e.parent=n;return(a.parent=new Up(null,0)).children=[a],a}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(a),r)i.eachBefore(u);else{var f=i,s=i,l=i;i.eachBefore((function(t){t.xs.x&&(s=t),t.depth>l.depth&&(l=t)}));var h=f===s?1:t(f,s)/2,d=h-f.x,p=n/(s.x+h+d),g=e/(l.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,a=n,u=e,c=o.parent.children[0],f=o.m,s=a.m,l=u.m,h=c.m;u=Rp(u),o=Dp(o),u&&o;)c=Dp(c),(a=Rp(a)).a=n,(i=u.z+l-o.z-f+t(u._,o._))>0&&(Fp(qp(u,n,r),n,i),f+=i,s+=i),l+=u.m,f+=o.m,h+=c.m,s+=a.m;u&&!Rp(a)&&(a.t=u,a.m+=l-s),o&&!Dp(c)&&(c.t=o,c.m+=f-h,r=n)}return r}(n,i,n.parent.A||r[0])}function a(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function u(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.treemap=function(){var t=Yp,n=!1,e=1,r=1,i=[0],o=np,a=np,u=np,c=np,f=np;function s(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(l),i=[0],n&&t.eachBefore(Tp),t}function l(n){var e=i[n.depth],r=n.x0+e,s=n.y0+e,l=n.x1-e,h=n.y1-e;l=e-1){var s=u[n];return s.x0=i,s.y0=o,s.x1=a,void(s.y1=c)}var l=f[n],h=r/2+l,d=n+1,p=e-1;for(;d>>1;f[g]c-o){var _=r?(i*v+a*y)/r:a;t(n,d,y,i,o,_,c),t(d,e,v,_,o,a,c)}else{var b=r?(o*v+c*y)/r:c;t(n,d,y,i,o,a,b),t(d,e,v,i,b,a,c)}}(0,c,t.value,n,e,r,i)},t.treemapDice=Ap,t.treemapResquarify=Lp,t.treemapSlice=Ip,t.treemapSliceDice=function(t,n,e,r,i){(1&t.depth?Ip:Ap)(t,n,e,r,i)},t.treemapSquarify=Yp,t.tsv=Mc,t.tsvFormat=lc,t.tsvFormatBody=hc,t.tsvFormatRow=pc,t.tsvFormatRows=dc,t.tsvFormatValue=gc,t.tsvParse=fc,t.tsvParseRows=sc,t.union=function(...t){const n=new InternSet;for(const e of t)for(const t of e)n.add(t);return n},t.unixDay=_y,t.unixDays=by,t.utcDay=yy,t.utcDays=vy,t.utcFriday=By,t.utcFridays=Vy,t.utcHour=hy,t.utcHours=dy,t.utcMillisecond=Wg,t.utcMilliseconds=Zg,t.utcMinute=cy,t.utcMinutes=fy,t.utcMonday=qy,t.utcMondays=jy,t.utcMonth=Qy,t.utcMonths=Jy,t.utcSaturday=Yy,t.utcSaturdays=Wy,t.utcSecond=iy,t.utcSeconds=oy,t.utcSunday=Fy,t.utcSundays=Ly,t.utcThursday=Oy,t.utcThursdays=Gy,t.utcTickInterval=av,t.utcTicks=ov,t.utcTuesday=Uy,t.utcTuesdays=Hy,t.utcWednesday=Iy,t.utcWednesdays=Xy,t.utcWeek=Fy,t.utcWeeks=Ly,t.utcYear=ev,t.utcYears=rv,t.variance=x,t.version="7.9.0",t.window=pn,t.xml=Sc,t.zip=function(){return gt(arguments)},t.zoom=function(){var t,n,e,r=Ew,i=Nw,o=zw,a=Cw,u=Pw,c=[0,1/0],f=[[-1/0,-1/0],[1/0,1/0]],s=250,l=ri,h=$t("start","zoom","end"),d=500,p=150,g=0,y=10;function v(t){t.property("__zoom",kw).on("wheel.zoom",T,{passive:!1}).on("mousedown.zoom",A).on("dblclick.zoom",S).filter(u).on("touchstart.zoom",E).on("touchmove.zoom",N).on("touchend.zoom touchcancel.zoom",k).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function _(t,n){return(n=Math.max(c[0],Math.min(c[1],n)))===t.k?t:new ww(n,t.x,t.y)}function b(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new ww(t.k,r,i)}function m(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function x(t,n,e,r){t.on("start.zoom",(function(){w(this,arguments).event(r).start()})).on("interrupt.zoom end.zoom",(function(){w(this,arguments).event(r).end()})).tween("zoom",(function(){var t=this,o=arguments,a=w(t,o).event(r),u=i.apply(t,o),c=null==e?m(u):"function"==typeof e?e.apply(t,o):e,f=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),s=t.__zoom,h="function"==typeof n?n.apply(t,o):n,d=l(s.invert(c).concat(f/s.k),h.invert(c).concat(f/h.k));return function(t){if(1===t)t=h;else{var n=d(t),e=f/n[2];t=new ww(e,c[0]-n[0]*e,c[1]-n[1]*e)}a.zoom(null,t)}}))}function w(t,n,e){return!e&&t.__zooming||new M(t,n)}function M(t,n){this.that=t,this.args=n,this.active=0,this.sourceEvent=null,this.extent=i.apply(t,n),this.taps=0}function T(t,...n){if(r.apply(this,arguments)){var e=w(this,n).event(t),i=this.__zoom,u=Math.max(c[0],Math.min(c[1],i.k*Math.pow(2,a.apply(this,arguments)))),s=ne(t);if(e.wheel)e.mouse[0][0]===s[0]&&e.mouse[0][1]===s[1]||(e.mouse[1]=i.invert(e.mouse[0]=s)),clearTimeout(e.wheel);else{if(i.k===u)return;e.mouse=[s,i.invert(s)],Gi(this),e.start()}Sw(t),e.wheel=setTimeout((function(){e.wheel=null,e.end()}),p),e.zoom("mouse",o(b(_(i,u),e.mouse[0],e.mouse[1]),e.extent,f))}}function A(t,...n){if(!e&&r.apply(this,arguments)){var i=t.currentTarget,a=w(this,n,!0).event(t),u=Zn(t.view).on("mousemove.zoom",(function(t){if(Sw(t),!a.moved){var n=t.clientX-s,e=t.clientY-l;a.moved=n*n+e*e>g}a.event(t).zoom("mouse",o(b(a.that.__zoom,a.mouse[0]=ne(t,i),a.mouse[1]),a.extent,f))}),!0).on("mouseup.zoom",(function(t){u.on("mousemove.zoom mouseup.zoom",null),ue(t.view,a.moved),Sw(t),a.event(t).end()}),!0),c=ne(t,i),s=t.clientX,l=t.clientY;ae(t.view),Aw(t),a.mouse=[c,this.__zoom.invert(c)],Gi(this),a.start()}}function S(t,...n){if(r.apply(this,arguments)){var e=this.__zoom,a=ne(t.changedTouches?t.changedTouches[0]:t,this),u=e.invert(a),c=e.k*(t.shiftKey?.5:2),l=o(b(_(e,c),a,u),i.apply(this,n),f);Sw(t),s>0?Zn(this).transition().duration(s).call(x,l,a,t):Zn(this).call(v.transform,l,a,t)}}function E(e,...i){if(r.apply(this,arguments)){var o,a,u,c,f=e.touches,s=f.length,l=w(this,i,e.changedTouches.length===s).event(e);for(Aw(e),a=0;a + + + + D3.js Interactive Horizontal Bar Graph + + + + + +

Life Cycle Cost for 50 Years

+
+ + + + + +""" + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Life Cycle Cost for 50 Years") + + view = QWebEngineView() + view.setHtml(html_content) + self.setCentralWidget(view) + self.resize(700, 600) + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) diff --git a/src/osbridgelcca/desktop_app/graphs/bubble_graph.py b/src/osbridgelcca/desktop_app/graphs/bubble_graph.py new file mode 100644 index 0000000..74c502e --- /dev/null +++ b/src/osbridgelcca/desktop_app/graphs/bubble_graph.py @@ -0,0 +1,231 @@ +import sys +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtWebEngineWidgets import QWebEngineView +import pandas as pd + +FILE_PATH = r"data.csv" +df = pd.read_csv(FILE_PATH) + +# using the d3js graphing library to plot the graph, it is downloaded locally and saved in the same directory as that of this graph script +with open(r"..\dependencies\d3js.js", "r", encoding="utf-8") as f: + d3_js = f.read() + +name = ['A - Initial Carbon Emission Cost\n', 'B - Carbon Emission due to Re-Routing', 'C - Maintenance Emission Costs', + "Embodied carbon emissions", "Additional CO2 e costs due to rerouting", "Periodic maintenance carbon emissions"] +percentage_list = [] +cost_list = [] + +for i, item in enumerate(name): + count = 0 + if i < 3: + for _, row in df.iterrows(): + if item in list(row): + if count == 0: + temp_row = list(row) + percentage_list.append(float(temp_row[temp_row.index(item) + 1])) + count += 1 + else: + for _, row in df.iterrows(): + if item in list(row): + if count == 0: + temp_row = list(row) + cost_list.append(float(temp_row[temp_row.index(item) + 1])) + count += 1 + +percentage_list = [round(item, 2) for item in percentage_list] +print(percentage_list) +val_a, val_b, val_c = percentage_list + +cost_list = [round(float(items) / 100000.0, 2) for items in cost_list] +cost_a, cost_b, cost_c = cost_list + + +html_content = f""" + + + + + Bubble Chart Layout + + + + +
+
+
+ A - Initial Carbon Emission Cost + B - Carbon Emission due to Re-Routing + C - Maintenance Emission Costs +
+
+
+ + + + +""" + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Bubble chart") + self.setGeometry(100, 100, 900, 550) + + view = QWebEngineView() + view.setHtml(html_content) + self.setCentralWidget(view) + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) diff --git a/src/osbridgelcca/desktop_app/graphs/data.csv b/src/osbridgelcca/desktop_app/graphs/data.csv new file mode 100644 index 0000000..0b4a8bb --- /dev/null +++ b/src/osbridgelcca/desktop_app/graphs/data.csv @@ -0,0 +1,29 @@ +These are the cordinates for plotting the life cycle cost analysis for 50 years the same format of inputs will be used for 100 years analysis as well. On X cordinate comes the magnitude of the amount and on the Y aixis come the different cost component of analysis,,,,,,,,,,,,, +,,Cost Compoents,Amount,%,Stage distribution,LCC Type,,Stage,Distribution,,,Carbon Emission Throughout the life cycle, +,,Initial construction cost,6182839.35,23.85836602,Initial Stage,Economic Cost,,Initial Stage,28.42910132,,,"A - Initial Carbon Emission Cost +",15.63380933 +,,Embodied carbon emissions,852863.518,3.291033266,Initial Stage,Environemntal Cost,,,54.92257883,,,B - Carbon Emission due to Re-Routing,53.2264347 +,,Time cost estimate,231856.4756,0.894688726,Initial Stage,Economic Cost,,,16.64831832,,,C - Maintenance Emission Costs,31.13975597 +,,Road user cost,12392640,47.82077042,Initial Stage,Social Cost,,Use Stage,48.11024121,,,, +,,Additional CO2 e costs due to rerouting,2903635.538,11.20456081,Initial Stage,Environemntal Cost,,,0,,,, +,,Periodic Maintenance costs,124402.59,0.480045228,Use Stage,Economic Cost,,,51.88975879,,,, +,,Periodic maintenance carbon emissions,1698751.806,6.555150485,Use Stage,Environemntal Cost,,End of Life Stage,100,,,, +,,Annual routine inspection costs,1273235.898,4.913167942,Use Stage,Economic Cost,,,0,,,, +,,Repair and rehabilitation costs,177380.5728,0.684476887,Use Stage,Economic Cost,,,0,,,, +,,Demolition and deconstruction costs,77158.678,0.297740225,End of Life Stage,Economic Cost,,Beyond Life Stage,0,,,, +,,Recycling costs,0,0,Beyond Life Stage,Economic Cost,,,0,,,, +,,Total Life Cycle Cost,25914764.43,100,,,,,0,,,, +,,,,,,,,,,,,, +,,Cost Compoents,Amount,%,Stage distribution,LCC Type,,Stage,Distribution,,,, +,,Initial construction cost,6701398.36,31.58858558,Initial Stage,Economic Cost,,Initial Stage,37.80673377,,,, +,,Embodied carbon emissions,1102094.341,5.194975666,Initial Stage,Environemntal Cost,,,,,,, +,,Time cost estimate,167534.959,0.78971464,Initial Stage,Economic Cost,,,,,,, +,,Road user cost,8261760,38.94370977,Initial Stage,Social Cost,,Use Stage,,,,, +,,Additional CO2e costs due to rerouting,1935757.025,9.124636854,Initial Stage,Environemntal Cost,,,,,,, +,,Periodic Maintenance costs,149545.738,0.704918301,Use Stage,Economic Cost,,,,,,, +,,Periodic maintenance carbon emissions,1767651.034,8.332230523,Use Stage,Environemntal Cost,,End of Life Stage,,,,, +,,Annual routine inspection costs,1380023.073,6.505056796,Use Stage,Economic Cost,,,,,,, +,,Repair and rehabilitation costs,0,0,Use Stage,Economic Cost,,,,,,, +,,Demolition and deconstruction costs,83630.03,0.39420942,End of Life Stage,Economic Cost,,Beyond Life Stage,,,,, +,,Recycling costs,-334774.6688,-1.578037554,Beyond Life Stage,Economic Cost,,,,,,, +,,Total Life Cycle Cost,21214619.89,100,,,,,,,,, diff --git a/src/osbridgelcca/desktop_app/graphs/data1.csv b/src/osbridgelcca/desktop_app/graphs/data1.csv new file mode 100644 index 0000000..d3235a9 --- /dev/null +++ b/src/osbridgelcca/desktop_app/graphs/data1.csv @@ -0,0 +1,29 @@ +These are the cordinates for plotting the life cycle cost analysis for 50 years the same format of inputs will be used for 100 years analysis as well. On X cordinate comes the magnitude of the amount and on the Y aixis come the different cost component of analysis,,,,,,,,,,,,, +,,Cost Compoents,Amount,%,Stage distribution,LCC Type,,Stage,Distribution,,,Carbon Emission Throughout the life cycle, +,,Initial construction cost,6182839.35,23.85836602,Initial Stage,Economic Cost,,Initial Stage,28.42910132,,,"A - Initial Carbon Emission Cost +",15.63380933 +,,Embodied carbon emissions,852863.518,3.291033266,Initial Stage,Environemntal Cost,,,54.92257883,,,B - Carbon Emission due to Re-Routing,53.2264347 +,,Time cost estimate,231856.4756,0.894688726,Initial Stage,Economic Cost,,,16.64831832,,,C - Maintenance Emission Costs,31.13975597 +,,Road user cost,12392640,47.82077042,Initial Stage,Social Cost,,Use Stage,48.11024121,,,, +,,Additional CO2 e costs due to rerouting,2903635.538,11.20456081,Initial Stage,Environemntal Cost,,,0,,,, +,,Periodic Maintenance costs,124402.59,0.480045228,Use Stage,Economic Cost,,,51.88975879,,,, +,,Periodic maintenance carbon emissions,1698751.806,6.555150485,Use Stage,Environemntal Cost,,End of Life Stage,100,,,, +,,Annual routine inspection costs,1273235.898,4.913167942,Use Stage,Economic Cost,,,0,,,, +,,Repair and rehabilitation costs,177380.5728,0.684476887,Use Stage,Economic Cost,,,0,,,, +,,Demolition and deconstruction costs,77158.678,0.297740225,End of Life Stage,Economic Cost,,Beyond Life Stage,0,,,, +,,Recycling costs,0,0,Beyond Life Stage,Economic Cost,,,0,,,, +,,Total Life Cycle Cost,25914764.43,100,,,,,0,,,, +,,,,,,,,,,,,, +,,Cost Compoents,Amount,%,Stage distribution,LCC Type,,Stage,Distribution,,,, +,,Initial construction cost,6701398.36,31.58858558,Initial Stage,Economic Cost,,Initial Stage,37.80673377,,,, +,,Embodied carbon emissions,1102094.341,5.194975666,Initial Stage,Environemntal Cost,,,,,,, +,,Time cost estimate,167534.959,0.78971464,Initial Stage,Economic Cost,,,,,,, +,,Road user cost,8261760,38.94370977,Initial Stage,Social Cost,,Use Stage,,,,, +,,Additional CO2 e costs due to rerouting,1935757.025,9.124636854,Initial Stage,Environemntal Cost,,,,,,, +,,Periodic Maintenance costs,149545.738,0.704918301,Use Stage,Economic Cost,,,,,,, +,,Periodic maintenance carbon emissions,1767651.034,8.332230523,Use Stage,Environemntal Cost,,End of Life Stage,,,,, +,,Annual routine inspection costs,1380023.073,6.505056796,Use Stage,Economic Cost,,,,,,, +,,Repair and rehabilitation costs,0,0,Use Stage,Economic Cost,,,,,,, +,,Demolition and deconstruction costs,83630.03,0.39420942,End of Life Stage,Economic Cost,,Beyond Life Stage,,,,, +,,Recycling costs,-334774.6688,-1.578037554,Beyond Life Stage,Economic Cost,,,,,,, +,,Total Life Cycle Cost,21214619.89,100,,,,,,,,, diff --git a/src/osbridgelcca/desktop_app/graphs/horizontal_bar_graph.py b/src/osbridgelcca/desktop_app/graphs/horizontal_bar_graph.py new file mode 100644 index 0000000..b10bffe --- /dev/null +++ b/src/osbridgelcca/desktop_app/graphs/horizontal_bar_graph.py @@ -0,0 +1,345 @@ +import sys +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtWebEngineWidgets import QWebEngineView +import pandas as pd + +FILE_PATH = r"data1.csv" +df = pd.read_csv(FILE_PATH) + +# using the d3js graphing library to plot the graph, it is downloaded locally and saved in the same directory as that of this graph script +with open(r"..\dependencies\d3js.js", "r", encoding="utf-8") as f: + d3_js = f.read() + +list_final = [ + "Initial construction cost", + "Embodied carbon emissions", + "Time cost estimate", + "Road user cost", + "Additional CO2 e costs due to rerouting", + "Periodic Maintenance costs", + "Periodic maintenance carbon emissions", + "Annual routine inspection costs", + "Repair and rehabilitation costs", + "Demolition and deconstruction costs", + "Recycling costs" +] + +# Extract values from DataFrame +psc_cost = [] +steel_cost = [] +for item in list_final: + count = 0 + for _, row in df.iterrows(): + if item in list(row): + temp_row = list(row) + if count == 0: + psc_cost.append(float(temp_row[temp_row.index(item) + 1])) + else: + steel_cost.append(float(temp_row[temp_row.index(item) + 1])) + count += 1 + +# Calculate Total Life-Cycle Cost +total_psc_cost = sum(psc_cost) +total_steel_cost = sum(steel_cost) + +# Prepare data for D3.js +js_data = [] +for i, label in enumerate(list_final): + # Adjust labels for better display in the chart + display_label = label.replace("Initial construction cost", "Initial Construction Cost") \ + .replace("Embodied carbon emissions", "Initial Carbon Emission Cost") \ + .replace("Time cost estimate", "Time Cost") \ + .replace("Road user cost", "Road User Cost") \ + .replace("Additional CO2 e costs due to rerouting", "Carbon Emission due to Re-Routing") \ + .replace("Periodic Maintenance costs", "Periodic Maintenance Costs") \ + .replace("Periodic maintenance carbon emissions", "Maintenance Emission Cost") \ + .replace("Annual routine inspection costs", "Routine Inspection Cost") \ + .replace("Repair and rehabilitation costs", "Repair & Rehabilitation Cost") \ + .replace("Demolition and deconstruction costs", "Demolition & Disposal Cost") \ + .replace("Recycling costs", "Recycling Cost") + + + js_data.append({ + "label": display_label, + "psc": psc_cost[i], + "steel": steel_cost[i] + }) + +# Add the "Total Life-Cycle Cost" entry +js_data.append({ + "label": "Total Life-Cycle Cost", + "psc": total_psc_cost, + "steel": total_steel_cost +}) + +# Convert Python list of dictionaries to a JavaScript array string +js_data_string = str(js_data).replace("'", '"') # Replace single quotes with double quotes for JSON compatibility + +html_content = f""" + + + + + Bridge Cost Comparison + + + + +
+
+ + + +""" + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Bridge Cost Comparison") + self.setGeometry(100, 100, 1200, 900) + + view = QWebEngineView() + view.setHtml(html_content) + self.setCentralWidget(view) + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/graphs/pie_chart.py b/src/osbridgelcca/desktop_app/graphs/pie_chart.py new file mode 100644 index 0000000..9e01ce4 --- /dev/null +++ b/src/osbridgelcca/desktop_app/graphs/pie_chart.py @@ -0,0 +1,569 @@ +import pandas as pd +import json +from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout +from PySide6.QtWebEngineWidgets import QWebEngineView +from PySide6.QtCore import QUrl, QSize + +# using the d3js graphing library to plot the graph, it is downloaded locally and saved in the same directory as that of this graph script +with open(r"..\dependencies\d3js.js", "r", encoding="utf-8") as f: + d3_js = f.read() + +class D3PieChartViewer(QMainWindow): + def __init__(self, data_js): + super().__init__() + self.setWindowTitle("Cost Breakdown Pie Chart") + self.setMinimumSize(QSize(800, 600)) + + self.central_widget = QWidget() + self.setCentralWidget(self.central_widget) + + self.layout = QVBoxLayout(self.central_widget) + + self.web_view = QWebEngineView() + self.layout.addWidget(self.web_view) + + # Generate HTML content + html_content = self.generate_html(data_js) + + # Load the HTML content + self.web_view.setHtml(html_content, QUrl.fromLocalFile("")) + + def generate_html(self, data_js): + html = f""" + + + + Cost Breakdown Pie Chart + + + + +
+

Cost Breakdown (in Lakhs)

+
+
+
+ + + + + """ + return html + +def main(): + print("generating graph") + FILE_PATH = r"data1.csv" + + # --- Read CSV and Extract Data --- + try: + df = pd.read_csv(FILE_PATH) + if df.empty: + print(f"Warning: CSV file '{FILE_PATH}' is empty.") + except FileNotFoundError: + print(f"Error: CSV file not found at '{FILE_PATH}'.") + df = pd.DataFrame() + except Exception as e: + print(f"Error reading CSV: {e}") + df = pd.DataFrame() + + # Define labels and their specific colors + label_colors = { + "Road user cost": "#FF8C00", + "Time cost estimate": "#483D8B", + "Embodied carbon emissions": "#B22222", + "Initial construction cost": "#996633", + "Additional CO2 e costs due to rerouting": "#8B0000", + "Periodic Maintenance costs": "#F6FB05", + "Periodic maintenance carbon emissions": "#A52A2A", + "Annual routine inspection costs": "#4682B4", + "Repair and rehabilitation costs": "#008000", + "Demolition and deconstruction costs": "#800080" + } + + # Extract data + values_dict = {} + for item in label_colors.keys(): + found_value = False + if not df.empty: + for _, row in df.iterrows(): + row_list = [str(x) for x in row.values] + if item in row_list: + idx = row_list.index(item) + if idx + 1 < len(row_list): + values_dict[item] = row_list[idx + 1] + found_value = True + break + if not found_value: + values_dict[item] = "0.0" + + cost_list = [] + for key in label_colors.keys(): + try: + cost_list.append(float(values_dict.get(key, "0.0"))) + except: + cost_list.append(0.0) + + total_cost = sum(cost_list) + percentage_list = [(v / total_cost) * 100 if total_cost else 0 for v in cost_list] + cost_list_lakhs = [v / 100000 for v in cost_list] + + # Create data with specific color mapping + data_with_colors = [ + { + "label": name, + "cost": cost, + "percent": percent, + "color": label_colors[name], + "disabled": False + } + for name, cost, percent in zip(label_colors.keys(), cost_list_lakhs, percentage_list) + ] + + data_js = json.dumps(data_with_colors) + + # Create and show the application window + app = QApplication([]) + viewer = D3PieChartViewer(data_js) + viewer.show() + app.exec() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/graphs/radial_bar_graph.py b/src/osbridgelcca/desktop_app/graphs/radial_bar_graph.py new file mode 100644 index 0000000..eb26c46 --- /dev/null +++ b/src/osbridgelcca/desktop_app/graphs/radial_bar_graph.py @@ -0,0 +1,205 @@ +import sys +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtWebEngineWidgets import QWebEngineView +import pandas as pd +import copy + +FILE_PATH = r"data.csv" +df = pd.read_csv(FILE_PATH) + +# using the d3js graphing library to plot the graph, it is downloaded locally and saved in the same directory as that of this graph script +with open(r"..\dependencies\d3js.js", "r", encoding="utf-8") as f: + d3_js = f.read() + +# hexal color code for various concentric circles, used inside the HTML js script text(its inside the string html_content) +colors = ['#273B5C', '#2E5743', '#996515', '#36454F'] + +# labels used displaying purpose, used inside the HTML js script text(its inside the string html_content) +stage_label = ['Initial Stage', 'Use Stage', 'End of Life Stage', 'Beyond Life Stage'] +stage_label_condition = ['initialstage', 'usestage', 'endoflifestage', 'beyondlifestage'] +percentage = [] + +# for loop to extract the values, modify according to the style +# Fixed: Using .iloc for positional access instead of deprecated row[index] syntax +for ___, row in df.iterrows(): + if isinstance(row.iloc[8], str): + if row.iloc[8].lower().replace(" ", "") in stage_label_condition: + percentage.append(float(row.iloc[9])) +percentage = percentage[:4] + +# html + js + css script to generate the radial bar graph, we used python's format to plug the values which we have extracted from the csv file +html_content = f""" + + + + + + + + + +

Economic cost distribution across various stages for bridges for 50 years

+ +
+ + + + + + +""" + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Economic Cost Distribution") + + view = QWebEngineView() + view.setHtml(html_content) + self.setCentralWidget(view) + self.resize(700, 600) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/main_template.py b/src/osbridgelcca/desktop_app/main_template.py index af16b07..227ad4b 100644 --- a/src/osbridgelcca/desktop_app/main_template.py +++ b/src/osbridgelcca/desktop_app/main_template.py @@ -12,6 +12,8 @@ from widgets.title_bar import CustomTitleBar from widgets.project_details_right_widget import ProjectDetailsWidget from widgets.tutorial_widget_left import TutorialWidget +from widgets.results_widget import ResultsWidget +from widgets.comparison_widget import ComparisonWidget from widgets.structure_works_data.foundation_widget import Foundation from widgets.structure_works_data.super_structure_widget import SuperStructure from widgets.structure_works_data.sub_structure_widget import SubStructure @@ -375,7 +377,7 @@ def setupUi(self, MainWindow): """) # ------------------------------------------------------------ body_layout = QHBoxLayout(body_widget) - body_layout.setSpacing(40) + body_layout.setSpacing(20) # Placeholders for dynamic widgets self.left_panel_placeholder = QWidget() @@ -399,6 +401,26 @@ def show_tutorial_widget(): self.left_panel_placeholder.layout().addWidget(self.current_left_widget) self.current_left_widget.closed.connect(lambda: self.remove_left_widget()) + def show_results_widget(): + if self.current_right_widget: + self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) + self.current_right_widget.setParent(None) + self.current_right_widget = ResultsWidget() + self.right_panel_placeholder.layout().addWidget(self.current_right_widget) + self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) + print("results widget") + self.results_tab.clicked.connect(show_results_widget) + + def show_comparison_widget(): + if self.current_right_widget: + self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) + self.current_right_widget.setParent(None) + self.current_right_widget = ComparisonWidget() + self.right_panel_placeholder.layout().addWidget(self.current_right_widget) + self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) + print("comparison widget") + self.compare.clicked.connect(show_comparison_widget) + def show_project_details_widget(widget_name=None): if widget_name and widget_name in self.widget_map: # Tabs are already visible diff --git a/src/osbridgelcca/desktop_app/widgets/comparison_widget.py b/src/osbridgelcca/desktop_app/widgets/comparison_widget.py new file mode 100644 index 0000000..a64b680 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/comparison_widget.py @@ -0,0 +1,873 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, QSize, Qt, QPropertyAnimation, QEasingCurve, Signal +from PySide6.QtGui import (QIcon) +from PySide6.QtWebEngineWidgets import QWebEngineView +from PySide6.QtWidgets import (QHBoxLayout, QTextEdit, QScrollArea, QSpacerItem, QSizePolicy, + QPushButton, QWidget, QLabel, QVBoxLayout, QGridLayout, QLineEdit, QComboBox, QCheckBox) +import sys + +class ComparisonWidget(QWidget): + closed = Signal() + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName("comparison_panel_widget") + self.setStyleSheet(""" + #comparison_panel_widget { + background-color: #FDEFEF; + border-radius: 8px; + } + #comparison_panel_widget QLabel { + color: #FDEFEF; + font-size: 12px; + } + #comparison_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #FDEFEF; + } + QScrollArea { + border: 1px solid #000000; + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + } + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + QPushButton#top_button_comparison_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_comparison_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_comparison_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + QPushButton#top_button_comparison_panel:hover QIcon { + color: red; + } + + /* Comparison cards */ + QWidget#comparison_card { + background-color: #F0F8FF; + border: 2px solid #4682B4; + border-radius: 10px; + margin: 10px; + } + QPushButton#close_comparison_card_button { + border: none; + background: transparent; + padding: 2px; + } + QPushButton#close_comparison_card_button:hover { + background: rgba(0,0,0,0.05); + border-radius: 4px; + } + + /* Comparison section headers */ + QWidget#comparison_section { + background-color: #E6F3FF; + border: 1px solid #87CEEB; + border-radius: 8px; + padding: 15px; + margin: 5px; + } + + /* Comparison buttons */ + QPushButton#comparison_button { + background-color: #4682B4; + color: white; + border: none; + border-radius: 6px; + padding: 8px 16px; + font-size: 14px; + font-weight: 500; + } + QPushButton#comparison_button:hover { + background-color: #5A9BD4; + } + QPushButton#comparison_button:pressed { + background-color: #36648B; + } + + """) + + main_layout = QVBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + + # --- Top Section --- + top_h_layout = QHBoxLayout() + self.top_button_comparison_panel = QPushButton("Comparison Window ") + self.top_button_comparison_panel.setObjectName("top_button_comparison_panel") + self.top_button_comparison_panel.setIcon(QIcon("resources/close.png")) + self.top_button_comparison_panel.setIconSize(QSize(13, 13)) + self.top_button_comparison_panel.setLayoutDirection(Qt.LayoutDirection.RightToLeft) + self.top_button_comparison_panel.clicked.connect(self.close_widget) + + top_h_layout.addWidget(self.top_button_comparison_panel) + top_h_layout.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)) + main_layout.addLayout(top_h_layout) + + # --- Main Content Area --- + self.main_content_widget = QWidget() + self.main_content_widget.setObjectName("main_content_widget") + self.main_content_widget.setStyleSheet(""" + #main_content_widget { + background-color: #FDEFEF; + border: 1px solid #4682B4; + border-top: none; + } + """) + + # Create scroll area for the main content + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.scroll_area.setStyleSheet(""" + QScrollArea { + border: none; + background-color: transparent; + } + QScrollBar:vertical { + background: #F0F0F0; + width: 12px; + border-radius: 6px; + } + QScrollBar::handle:vertical { + background: #C0C0C0; + border-radius: 6px; + min-height: 20px; + } + QScrollBar::handle:vertical:hover { + background: #A0A0A0; + } + QScrollBar:horizontal { + background: #F0F0F0; + height: 12px; + border-radius: 6px; + } + QScrollBar::handle:horizontal { + background: #C0C0C0; + border-radius: 6px; + min-width: 20px; + } + QScrollBar::handle:horizontal:hover { + background: #A0A0A0; + } + """) + + # Create scrollable content widget + self.scroll_content_widget = QWidget() + self.scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_content_widget.setStyleSheet(""" + #scroll_content_widget { + background-color: #F0F8FF; + } + """) + + # Main content layout + content_layout = QVBoxLayout(self.scroll_content_widget) + content_layout.setContentsMargins(20, 20, 20, 20) + content_layout.setSpacing(20) + + # File selection section + self.file_selection_widget = self._create_file_selection() + content_layout.addWidget(self.file_selection_widget) + + # Comparison chart area + self.comparison_chart_widget = self._create_comparison_chart() + content_layout.addWidget(self.comparison_chart_widget) + + # Set the scroll content widget to the scroll area + self.scroll_area.setWidget(self.scroll_content_widget) + + # Add scroll area to main layout + main_layout.addWidget(self.scroll_area) + + def close_widget(self): + self.closed.emit() + self.setParent(None) + + def _create_file_selection(self) -> QWidget: + """Create the file selection section matching the image""" + selection_widget = QWidget() + selection_widget.setStyleSheet(""" + QWidget { + background-color: #FFFFFF; + border: 1px solid #CCCCCC; + border-radius: 8px; + margin: 5px; + } + """) + + selection_layout = QVBoxLayout(selection_widget) + selection_layout.setContentsMargins(20, 15, 20, 15) + selection_layout.setSpacing(15) + + # Top section with description and browse button + top_layout = QHBoxLayout() + + # Description text + desc_label = QLabel("Select files to compare various\ncomponents of life-cycle cost analysis") + desc_label.setStyleSheet(""" + QLabel { + font-size: 14px; + color: #333333; + line-height: 1.4; + } + """) + top_layout.addWidget(desc_label) + + top_layout.addStretch(1) + + # Browse button + browse_button = QPushButton("Browse...") + browse_button.setStyleSheet(""" + QPushButton { + background-color: #E0E0E0; + border: 1px solid #CCCCCC; + border-radius: 4px; + padding: 8px 16px; + font-size: 14px; + color: #333333; + } + QPushButton:hover { + background-color: #D0D0D0; + } + QPushButton:pressed { + background-color: #C0C0C0; + } + """) + browse_button.setCursor(Qt.CursorShape.PointingHandCursor) + top_layout.addWidget(browse_button) + + selection_layout.addLayout(top_layout) + + # File checkboxes section + files_layout = QVBoxLayout() + files_layout.setSpacing(8) + + # PSC Bridge checkbox + psc_layout = QHBoxLayout() + psc_checkbox = QCheckBox() + psc_checkbox.setChecked(True) + psc_checkbox.setStyleSheet(""" + QCheckBox::indicator { + width: 16px; + height: 16px; + border: 2px solid #4CAF50; + border-radius: 3px; + background-color: #FFFFFF; + } + QCheckBox::indicator:checked { + background-color: #4CAF50; + border-color: #4CAF50; + } + QCheckBox::indicator:checked::before { + content: "✓"; + color: white; + font-size: 12px; + font-weight: bold; + text-align: center; + line-height: 16px; + } + """) + + psc_label = QLabel("PSC Bridge.os") + psc_label.setStyleSheet(""" + QLabel { + font-size: 14px; + color: #333333; + margin-left: 8px; + } + """) + + psc_import_label = QLabel("Imported") + psc_import_label.setStyleSheet(""" + QLabel { + font-size: 12px; + color: #888888; + margin-left: 10px; + } + """) + + psc_layout.addWidget(psc_checkbox) + psc_layout.addWidget(psc_label) + psc_layout.addWidget(psc_import_label) + psc_layout.addStretch(1) + + # Steel Bridge checkbox + steel_layout = QHBoxLayout() + steel_checkbox = QCheckBox() + steel_checkbox.setChecked(True) + steel_checkbox.setStyleSheet(""" + QCheckBox::indicator { + width: 16px; + height: 16px; + border: 2px solid #4CAF50; + border-radius: 3px; + background-color: #FFFFFF; + } + QCheckBox::indicator:checked { + background-color: #4CAF50; + border-color: #4CAF50; + } + QCheckBox::indicator:checked::before { + content: "✓"; + color: white; + font-size: 12px; + font-weight: bold; + text-align: center; + line-height: 16px; + } + """) + + steel_label = QLabel("Steel Bridge.os") + steel_label.setStyleSheet(""" + QLabel { + font-size: 14px; + color: #333333; + margin-left: 8px; + } + """) + + steel_import_label = QLabel("Imported") + steel_import_label.setStyleSheet(""" + QLabel { + font-size: 12px; + color: #888888; + margin-left: 10px; + } + """) + + steel_layout.addWidget(steel_checkbox) + steel_layout.addWidget(steel_label) + steel_layout.addWidget(steel_import_label) + steel_layout.addStretch(1) + + files_layout.addLayout(psc_layout) + files_layout.addLayout(steel_layout) + + selection_layout.addLayout(files_layout) + + return selection_widget + + + + def _create_comparison_chart(self) -> QWidget: + """Create the comparison chart area matching the image""" + chart_widget = QWidget() + chart_widget.setStyleSheet(""" + QWidget { + background-color: #FFFFFF; + border: 1px solid #CCCCCC; + border-radius: 8px; + margin: 5px; + } + """) + + chart_layout = QVBoxLayout(chart_widget) + chart_layout.setContentsMargins(20, 15, 20, 15) + chart_layout.setSpacing(15) + + # Chart area - full width + # Horizontal bar chart using QWebEngineView + chart_view = QWebEngineView() + chart_view.setHtml(self._build_comparison_chart_html()) + chart_view.setZoomFactor(0.9) + chart_view.setMinimumHeight(400) + chart_view.setMaximumHeight(600) + chart_view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + + chart_layout.addWidget(chart_view) + + return chart_widget + + def _create_legend(self) -> QWidget: + """Create the legend widget matching the image""" + legend_widget = QWidget() + legend_widget.setStyleSheet(""" + QWidget { + background-color: #FDEFEF; + border: 1px solid #DDDDDD; + border-radius: 6px; + padding: 10px; + } + """) + + legend_layout = QVBoxLayout(legend_widget) + legend_layout.setContentsMargins(10, 10, 10, 10) + legend_layout.setSpacing(8) + + # PSC Bridge legend item + psc_layout = QHBoxLayout() + psc_color = QWidget() + psc_color.setFixedSize(16, 16) + psc_color.setStyleSheet("background-color: #87CEEB; border: 1px solid #5F9EA0;") + + psc_label = QLabel("PSC Bridge") + psc_label.setStyleSheet(""" + QLabel { + font-size: 12px; + color: #333333; + } + """) + + psc_layout.addWidget(psc_color) + psc_layout.addWidget(psc_label) + psc_layout.addStretch(1) + + # Steel Bridge legend item + steel_layout = QHBoxLayout() + steel_color = QWidget() + steel_color.setFixedSize(16, 16) + steel_color.setStyleSheet("background-color: #8B0000; border: 1px solid #654321;") + + steel_label = QLabel("Steel Bridge") + steel_label.setStyleSheet(""" + QLabel { + font-size: 12px; + color: #333333; + } + """) + + steel_layout.addWidget(steel_color) + steel_layout.addWidget(steel_label) + steel_layout.addStretch(1) + + legend_layout.addLayout(psc_layout) + legend_layout.addLayout(steel_layout) + + return legend_widget + + def _create_download_options(self) -> QWidget: + """Create the download options widget matching the image""" + download_widget = QWidget() + download_layout = QVBoxLayout(download_widget) + download_layout.setContentsMargins(0, 0, 0, 0) + download_layout.setSpacing(5) + + # Download options + options = ["Download as PNG", "Download as JPG", "Download as PDF", "View as Table"] + + for option in options: + option_label = QLabel(option) + option_label.setStyleSheet(""" + QLabel { + font-size: 12px; + color: #0066CC; + padding: 4px; + text-decoration: underline; + } + QLabel:hover { + background-color: #FDEFEF; + color: #0052A3; + } + """) + option_label.setCursor(Qt.CursorShape.PointingHandCursor) + download_layout.addWidget(option_label) + + return download_widget + + def _build_comparison_chart_html(self) -> str: + """Generate HTML for horizontal bar chart using exact code from Horizontal Bar Graph 2.py""" + # Sample data matching the original structure - you can replace this with actual CSV data later + list_final = [ + "Initial construction cost", + "Embodied carbon emissions", + "Time cost estimate", + "Road user cost", + "Additional CO2 e costs due to rerouting", + "Periodic Maintenance costs", + "Periodic maintenance carbon emissions", + "Annual routine inspection costs", + "Repair and rehabilitation costs", + "Demolition and deconstruction costs", + "Recycling costs" + ] + + # Sample data (you can replace these with actual values from CSV) + psc_cost = [61.83, 8.53, 2.32, 123.93, 29.03, 1.44, 16.99, 14.32, 1.99, 0.77, 0.0] + steel_cost = [61.83, 11.02, 1.68, 123.93, 29.03, 1.44, 16.99, 14.32, 1.99, 0.77, 0.0] + + # Calculate Total Life-Cycle Cost + total_psc_cost = sum(psc_cost) + total_steel_cost = sum(steel_cost) + + # Prepare data for D3.js + js_data = [] + for i, label in enumerate(list_final): + # Adjust labels for better display in the chart + display_label = label.replace("Initial construction cost", "Initial Construction Cost") \ + .replace("Embodied carbon emissions", "Initial Carbon Emission Cost") \ + .replace("Time cost estimate", "Time Cost") \ + .replace("Road user cost", "Road User Cost") \ + .replace("Additional CO2 e costs due to rerouting", "Carbon Emission due to Re-Routing") \ + .replace("Periodic Maintenance costs", "Periodic Maintenance Costs") \ + .replace("Periodic maintenance carbon emissions", "Maintenance Emission Cost") \ + .replace("Annual routine inspection costs", "Routine Inspection Cost") \ + .replace("Repair and rehabilitation costs", "Repair & Rehabilitation Cost") \ + .replace("Demolition and deconstruction costs", "Demolition & Disposal Cost") \ + .replace("Recycling costs", "Recycling Cost") + + js_data.append({ + "label": display_label, + "psc": psc_cost[i], + "steel": steel_cost[i] + }) + + # Add the "Total Life-Cycle Cost" entry + js_data.append({ + "label": "Total Life-Cycle Cost", + "psc": total_psc_cost, + "steel": total_steel_cost + }) + + # Convert Python list of dictionaries to a JavaScript array string + import json + js_data_string = json.dumps(js_data) + + html = f""" + + + + + Bridge Cost Comparison + + + + +
+
+ + + + """ + return html + + + +#----------------Standalone-Test-Code-------------------------------- + +class MyMainWindow(QMainWindow): + def __init__(self): + super().__init__() + + self.setStyleSheet("border: none") + + self.central_widget = QWidget() + self.central_widget.setObjectName("central_widget") + self.setCentralWidget(self.central_widget) + + self.main_h_layout = QHBoxLayout(self.central_widget) + self.main_h_layout.addStretch(1) + + self.main_h_layout.addWidget(ComparisonWidget(), 2) + + self.setWindowState(Qt.WindowState.WindowMaximized) + + +if __name__ == "__main__": + QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_DontShowIconsInMenus, False) + app = QApplication(sys.argv) + window = MyMainWindow() + window.show() + sys.exit(app.exec()) diff --git a/src/osbridgelcca/desktop_app/widgets/results_widget.py b/src/osbridgelcca/desktop_app/widgets/results_widget.py new file mode 100644 index 0000000..cd29897 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/results_widget.py @@ -0,0 +1,2117 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, QSize, Qt, QPropertyAnimation, QEasingCurve, Signal +from PySide6.QtGui import (QIcon) +from PySide6.QtWebEngineWidgets import QWebEngineView + +from PySide6.QtWidgets import (QHBoxLayout, QTextEdit, QScrollArea, QSpacerItem, QSizePolicy, + QPushButton, QWidget, QLabel, QVBoxLayout, QGridLayout, QLineEdit, QComboBox) +import sys + +# using the d3js graphing library to plot the graph, it is downloaded locally and saved in the same directory as that of this graph script +with open(r"dependencies/d3js.js", "r", encoding="utf-8") as f: + d3_js = f.read() + +class ResultsWidget(QWidget): + closed = Signal() + def __init__(self, parent=None): + super().__init__(parent) + self.setObjectName("central_panel_widget") + self.setStyleSheet(""" + #central_panel_widget { + background-color: #F8F8F8; + border-radius: 8px; + } + #central_panel_widget QLabel { + color: #333333; + font-size: 12px; + } + #central_panel_widget QLabel#page_number_label { + font-size: 14px; + font-weight: bold; + color: #555555; + } + QScrollArea { + border: 1px solid #000000; + background-color: transparent; + outline: none; + } + #scroll_content_widget { + background-color: #FFF9F9; + } + QScrollBar:vertical { + border: 1px solid #E0E0E0; + background: #F0F0F0; + width: 12px; + margin: 18px 0px 18px 0px; + border-radius: 6px; + } + QScrollBar::handle:vertical { + background: #C0C0C0; + border: 1px solid #A0A0A0; + min-height: 20px; + border-radius: 5px; + } + QScrollBar::add-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: bottom; + subcontrol-position: bottom; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + } + QScrollBar::sub-line:vertical { + border: 1px solid #E0E0E0; + background: #E8E8E8; + height: 18px; + subcontrol-origin: top; + subcontrol-position: top; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + } + QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { + width: 10px; + height: 10px; + } + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + } + QScrollBar::up-arrow:vertical { + image: url(resources/arrow_up.png); + } + QScrollBar::down-arrow:vertical { + image: url(resources/arrow_down.png); + } + QScrollBar::add-line:vertical:hover, QScrollBar::sub-line:vertical:hover { + background: #D0D0D0; + } + QPushButton#top_button_right_panel { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_right_panel:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_right_panel:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + QPushButton#top_button_right_panel:hover QIcon { + color: red; + } + + + QPushButton#top_button_right_panel_pressed { + background-color: #FDEFEF; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + border-bottom: 1px solid #000000; + text-align: left; + padding: 4px 10px; + color: #000000; + } + QPushButton#top_button_right_panel_pressed:hover { + background-color: #F0E6E6; + border-color: #808080; + } + QPushButton#top_button_right_panel_pressed:pressed { + background-color: #FFF3F3; + border-color: #606060; + } + QPushButton#top_button_right_panel_pressed:hover QIcon { + color: red; + } + + /* Results cards */ + QWidget#result_card { + background-color: #FDEFEF; + border: 1px solid #000000; + border-radius: 8px; + height: 150px; + } + QPushButton#close_card_button { + border: none; + background: transparent; + padding: 2px; + } + QPushButton#close_card_button:hover { + background: rgba(0,0,0,0.05); + border-radius: 4px; + } + + /* Bottom navigation buttons */ + QPushButton#bottom_nav_button { + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 6px; + color: #333333; + font-size: 14px; + font-weight: 500; + } + QPushButton#bottom_nav_button:hover { + background-color: #F8F8F8; + border-color: #C0C0C0; + } + QPushButton#bottom_nav_button:pressed { + background-color: #F0F0F0; + border-color: #A0A0A0; + } + + """) + + left_panel_vlayout = QVBoxLayout(self) + left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setSpacing(0) + + self.pressed=0 + self.current_page = 0 # 0: initial view, 1: pie chart, 2: 100-year graphs, 3: bubble graphs + self.max_page = 3 + + # --- Top Section --- + top_h_layout_left_panel = QHBoxLayout() + self.top_button_right_panel = QPushButton("Data Window ") + self.top_button_right_panel.setObjectName("top_button_right_panel") + self.top_button_right_panel.setIcon(QIcon("resources/close.png")) + self.top_button_right_panel.setIconSize(QSize(13, 13)) + self.top_button_right_panel.setLayoutDirection(Qt.LayoutDirection.RightToLeft) + self.top_button_right_panel.clicked.connect(self.close_widget) + + self.top_button_left_panel = QPushButton("Results Window ") + self.top_button_left_panel.setObjectName("top_button_right_panel") + self.top_button_left_panel.setIcon(QIcon("resources/close.png")) + self.top_button_left_panel.setIconSize(QSize(13, 13)) + self.top_button_left_panel.setLayoutDirection(Qt.LayoutDirection.RightToLeft) + self.top_button_left_panel.clicked.connect(self.switch_to_results_widget) + + top_h_layout_left_panel.addWidget(self.top_button_right_panel) + top_h_layout_left_panel.addWidget(self.top_button_left_panel) + + top_h_layout_left_panel.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)) + left_panel_vlayout.addLayout(top_h_layout_left_panel) + + # --- Main Content Area --- + self.main_content_widget = QWidget() + self.main_content_widget.setObjectName("main_content_widget") + self.main_content_widget.setStyleSheet(""" + #main_content_widget { + background-color: #FDEFEF; + border: 1px solid #000000; + border-top: none; + } + """) + + # Create scroll area for the main content + self.scroll_area = QScrollArea() + self.scroll_area.setWidgetResizable(True) + self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.scroll_area.setStyleSheet(""" + QScrollArea { + border: none; + background-color: transparent; + } + QScrollBar:vertical { + background: #F0F0F0; + width: 12px; + border-radius: 6px; + } + QScrollBar::handle:vertical { + background: #C0C0C0; + border-radius: 6px; + min-height: 20px; + } + QScrollBar::handle:vertical:hover { + background: #A0A0A0; + } + QScrollBar:horizontal { + background: #F0F0F0; + height: 12px; + border-radius: 6px; + } + QScrollBar::handle:horizontal { + background: #C0C0C0; + border-radius: 6px; + min-width: 20px; + } + QScrollBar::handle:horizontal:hover { + background: #A0A0A0; + } + """) + + # Create scrollable content widget + self.scroll_content_widget = QWidget() + self.scroll_content_widget.setObjectName("scroll_content_widget") + self.scroll_content_widget.setStyleSheet(""" + #scroll_content_widget { + background-color: #FDEFEF; + } + """) + + # Add a simple label to show this is the results area + content_layout = QVBoxLayout(self.scroll_content_widget) + content_layout.setContentsMargins(20, 20, 20, 20) + content_layout.setSpacing(20) + + self.welcome_label = QLabel("Results Area") + self.welcome_label.setStyleSheet(""" + QLabel { + font-size: 18px; + font-weight: bold; + color: #333333; + padding: 20px; + } + """) + self.welcome_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + content_layout.addWidget(self.welcome_label) + + # Container with three closable cards (initially hidden) + self.cards_container = QWidget() + self.cards_container.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + + # Create a flow layout for responsive card arrangement + self.cards_layout = QHBoxLayout(self.cards_container) + self.cards_layout.setContentsMargins(0, 0, 0, 0) + self.cards_layout.setSpacing(20) + self.cards_layout.setSizeConstraint(QHBoxLayout.SizeConstraint.SetNoConstraint) + + titles = [ + "Economic cost distribution across various stages for bridges for 50 years", + "Social cost distribution across stages for PSC bridges for 50 years", + "Environmental cost distribution across stages for PSC bridges for 50 years", + ] + + # Create and add cards with debug information + self.cards_list = [] # Store references to cards + for i, title in enumerate(titles): + card = self._create_result_card(title, [30, 45, 15, 10]) + self.cards_list.append(card) + self.cards_layout.addWidget(card) + print(f"Created card {i}: {title[:30]}...") + print(f"Card {i} visible: {card.isVisible()}") + print(f"Card {i} parent: {card.parent()}") + + self.cards_container.setVisible(False) + content_layout.addWidget(self.cards_container) + + print(f"Cards container created with {len(self.cards_list)} cards") + print(f"Cards container visible: {self.cards_container.isVisible()}") + print(f"Cards container parent: {self.cards_container.parent()}") + + # Shared legend below all cards + self.legend_widget = self._build_legend_widget() + self.legend_widget.setVisible(False) + content_layout.addWidget(self.legend_widget, 0, Qt.AlignmentFlag.AlignHCenter) + + # Bar graph below legend + self.bar_graph_widget = self._build_bar_graph() + self.bar_graph_widget.setVisible(False) + content_layout.addWidget(self.bar_graph_widget, 0, Qt.AlignmentFlag.AlignHCenter) + + # Pie chart widget (for page 1) + self.pie_chart_widget = self._build_pie_chart() + self.pie_chart_widget.setVisible(False) + content_layout.addWidget(self.pie_chart_widget, 0, Qt.AlignmentFlag.AlignHCenter) + + # 100-year widgets (for page 2) + self.cards_container_100 = QWidget() + self.cards_container_100.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + self.cards_layout_100 = QHBoxLayout(self.cards_container_100) + self.cards_layout_100.setContentsMargins(0, 0, 0, 0) + self.cards_layout_100.setSpacing(20) + self.cards_layout_100.setSizeConstraint(QHBoxLayout.SizeConstraint.SetNoConstraint) + + titles_100 = [ + "Economic cost distribution across various stages for bridges for 100 years", + "Social cost distribution across stages for PSC bridges for 100 years", + "Environmental cost distribution across stages for PSC bridges for 100 years", + ] + + self.cards_list_100 = [] + for i, title in enumerate(titles_100): + card = self._create_result_card(title, [30, 45, 15, 10]) + self.cards_list_100.append(card) + self.cards_layout_100.addWidget(card) + + self.cards_container_100.setVisible(False) + content_layout.addWidget(self.cards_container_100) + + self.legend_widget_100 = self._build_legend_widget() + self.legend_widget_100.setVisible(False) + content_layout.addWidget(self.legend_widget_100, 0, Qt.AlignmentFlag.AlignHCenter) + + self.bar_graph_widget_100 = self._build_bar_graph_100() + self.bar_graph_widget_100.setVisible(False) + content_layout.addWidget(self.bar_graph_widget_100, 0, Qt.AlignmentFlag.AlignHCenter) + + # Bubble graph widgets (for page 3) + self.bubble_cards_container = QWidget() + self.bubble_cards_container.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + self.bubble_cards_layout = QHBoxLayout(self.bubble_cards_container) + self.bubble_cards_layout.setContentsMargins(0, 0, 0, 0) + self.bubble_cards_layout.setSpacing(20) + + bubble_titles = [ + "Carbon Emission Cost Through-out Life-Cycle Stages for 50 years", + "Carbon Emission Cost Through-out Life-Cycle Stages for 100 years", + ] + + self.bubble_cards_list = [] + for i, title in enumerate(bubble_titles): + card = self._create_bubble_card(title) + self.bubble_cards_list.append(card) + self.bubble_cards_layout.addWidget(card) + + self.bubble_cards_container.setVisible(False) + content_layout.addWidget(self.bubble_cards_container) + + # Bottom navigation buttons + self.bottom_buttons_widget = self._build_bottom_buttons() + self.bottom_buttons_widget.setVisible(False) + content_layout.addWidget(self.bottom_buttons_widget, 0, Qt.AlignmentFlag.AlignHCenter) + + # Set the scroll content widget to the scroll area + self.scroll_area.setWidget(self.scroll_content_widget) + + # Add scroll area to main layout + left_panel_vlayout.addWidget(self.scroll_area) + + # Connect resize event to handle dynamic layout + self.resizeEvent = self.handle_resize + + + + + def close_widget(self): + self.closed.emit() + self.setParent(None) + + def _build_radial_bar_html(self, percentages): + # Stage labels and color palette copied from the original script + stage_label = ['Initial Stage', 'Use Stage', 'End of Life Stage', 'Beyond Life Stage'] + colors = ['#273B5C', '#2E5743', '#996515', '#36454F'] + + # Ensure exactly four entries + values = (percentages + [0, 0, 0, 0])[:4] + + # Size tuned to fit the card nicely with proper height for full visibility + width = 360 + height = 220 + body_bg = "#FDEFEF" + + html = f""" + + + + + + + + +
+ + + + +""" + return html + + def _build_legend_widget(self) -> QWidget: + stage_label = ['Initial Stage', 'Use Stage', 'End of Life Stage', 'Beyond Life Stage'] + colors = ['#273B5C', '#2E5743', '#996515', '#36454F'] + legend = QWidget() + layout = QHBoxLayout(legend) + layout.setContentsMargins(8, 0, 8, 6) + layout.setSpacing(16) + for name, color in zip(stage_label, colors): + item = QWidget() + item_layout = QHBoxLayout(item) + item_layout.setContentsMargins(0, 0, 0, 0) + item_layout.setSpacing(6) + swatch = QWidget() + swatch.setFixedSize(14, 14) + swatch.setStyleSheet(f"background:{color}; border-radius:3px;") + label = QLabel(name) + item_layout.addWidget(swatch) + item_layout.addWidget(label) + layout.addWidget(item) + layout.addStretch(1) + return legend + + def _build_bottom_buttons(self) -> QWidget: + """Create bottom navigation buttons widget""" + buttons_widget = QWidget() + buttons_layout = QHBoxLayout(buttons_widget) + buttons_layout.setContentsMargins(20, 15, 20, 15) + buttons_layout.setSpacing(15) + + # Back button + self.back_button = QPushButton("Back") + self.back_button.setObjectName("bottom_nav_button") + self.back_button.setFixedSize(80, 35) + self.back_button.setCursor(Qt.CursorShape.PointingHandCursor) + self.back_button.clicked.connect(self.go_back) + + # Next button + self.next_button = QPushButton("Next") + self.next_button.setObjectName("bottom_nav_button") + self.next_button.setFixedSize(80, 35) + self.next_button.setCursor(Qt.CursorShape.PointingHandCursor) + self.next_button.clicked.connect(self.go_next) + + # Add buttons to layout + buttons_layout.addStretch(1) + buttons_layout.addWidget(self.back_button) + buttons_layout.addWidget(self.next_button) + buttons_layout.addStretch(1) + + return buttons_widget + + def _build_bar_graph(self) -> QWidget: + """Create bar graph widget showing life-cycle costs""" + bar_graph_widget = QWidget() + bar_graph_layout = QVBoxLayout(bar_graph_widget) + bar_graph_layout.setContentsMargins(20, 15, 20, 15) + bar_graph_layout.setSpacing(10) + + # Title for the bar graph + title_label = QLabel("Life-Cycle Costs for 50 years") + title_label.setStyleSheet(""" + QLabel { + font-size: 16px; + font-weight: bold; + color: #333333; + text-align: center; + } + """) + title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + bar_graph_layout.addWidget(title_label) + + # Create the bar graph using QWebEngineView + graph_view = QWebEngineView() + graph_view.setHtml(self._build_bar_graph_html()) + graph_view.setZoomFactor(0.8) + graph_view.setMinimumHeight(300) + graph_view.setMaximumHeight(400) + graph_view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + graph_view.setMinimumWidth(500) # Ensure minimum width for readability + bar_graph_layout.addWidget(graph_view) + + # Store reference for resize handling + self.bar_graph_view = graph_view + + return bar_graph_widget + + def _build_bar_graph_html(self) -> str: + """Generate HTML for the horizontal bar graph using exact data from bargraph.py""" + # Extract names and costs from the original bargraph.py data + name = [ + "Initial Construction Cost", "Initial Carbon Emission Cost", "Time Cost", + "Road User Cost", "Periodic Maintenance Costs", "Maintenance Emission Costs", + "Routine Inspection Costs", "Repair & Rehabilitation Costs", "Reconstruction Costs", + "Demolition & Disposal Cost", "Recycling Cost", "Total Life-Cycle Cost" + ] + cost = [ + 61.83, 29.03, 0.0, 123.93, 1.44, 0.0, 0.0, 0.0, 0.0, 18.69, 0.0, 0.0 + ] + + # Clean data (include all values except total, but handle zeros specially) + temp_name = [] + temp_cost = [] + for i in range(len(cost)): + if i < 11: # Exclude only the total, include all other values including zeros + temp_name.append(name[i]) + temp_cost.append(cost[i]) + + # Display adjustment for zero values (from original code) + temp_cost_display = [] + cost_list = temp_cost.copy() + if cost_list: + # Find minimum non-zero value for display adjustment + non_zero_costs = [c for c in cost_list if c > 0] + if non_zero_costs: + min_non_zero = min(non_zero_costs) + for j in temp_cost: + if j == 0.0: + temp_cost_display.append(min_non_zero / 2) + else: + temp_cost_display.append(j) + else: + temp_cost_display = temp_cost + else: + temp_cost_display = temp_cost + + # Color list from original bargraph.py + color_list = ['#638B48', '#6F6F6F', '#638B48', '#E09365', '#6F6F6F', '#638B48', + '#6F6F6F', '#638B48', '#638B48', '#638B48', '#638B48', '#ffffff'] + + # Calculate total cost + total_cost = sum(temp_cost) + + # Create data array for D3 + data = [] + for i, (n, c, col) in enumerate(zip(temp_name, temp_cost_display, color_list[:len(temp_name)])): + data.append({"name": n, "value": c, "color": col}) + + # Debug: Print the data being processed + print(f"Original cost data: {cost}") + print(f"Filtered names: {temp_name}") + print(f"Filtered costs: {temp_cost}") + print(f"Display costs: {temp_cost_display}") + print(f"Number of bars to display: {len(temp_name)}") + print(f"Data array for D3: {data}") + + html = f""" + + + + + D3.js Interactive Horizontal Bar Graph + + + + +

Life Cycle Cost for 50 Years

+
+ + + + + + +""" + return html + + def handle_resize(self, event): + """Handle window resize events to make layout more responsive""" + # Get the current width of the widget + current_width = self.width() + + # Only adjust card layout if cards are visible and we have the necessary attributes + if (hasattr(self, 'cards_container') and + hasattr(self, 'cards_layout') and + self.cards_container.isVisible() and + self.cards_container.layout() is not None): + + current_layout = self.cards_container.layout() + + if current_width < 1000: # If window is narrow + # Stack cards vertically + if isinstance(current_layout, QHBoxLayout): + # Convert to vertical layout + new_layout = QVBoxLayout() + new_layout.setContentsMargins(0, 0, 0, 0) + new_layout.setSpacing(20) + + # Move all cards to new layout + while current_layout.count(): + item = current_layout.takeAt(0) + if item.widget(): + new_layout.addWidget(item.widget()) + + # Replace the layout + self.cards_container.setLayout(new_layout) + self.cards_layout = new_layout + + # Ensure all cards remain visible after layout change + for i in range(new_layout.count()): + item = new_layout.itemAt(i) + if item and item.widget(): + item.widget().setVisible(True) + + else: # If window is wide + # Arrange cards horizontally + if isinstance(current_layout, QVBoxLayout): + # Convert to horizontal layout + new_layout = QHBoxLayout() + new_layout.setContentsMargins(0, 0, 0, 0) + new_layout.setSpacing(20) + new_layout.setSizeConstraint(QHBoxLayout.SizeConstraint.SetNoConstraint) + + # Move all cards to new layout + while current_layout.count(): + item = current_layout.takeAt(0) + if item.widget(): + new_layout.addWidget(item.widget()) + + # Replace the layout + self.cards_container.setLayout(new_layout) + self.cards_layout = new_layout + + # Ensure all cards remain visible after layout change + for i in range(new_layout.count()): + item = new_layout.itemAt(i) + if item and item.widget(): + item.widget().setVisible(True) + + # Call the parent's resize event handler + super().resizeEvent(event) + + # After resize, ensure cards remain visible + if hasattr(self, 'cards_container') and self.cards_container.isVisible(): + self.ensure_cards_visible() + + def ensure_cards_visible(self): + """Ensure all cards are visible and properly displayed""" + print("=== ensure_cards_visible called ===") + + if hasattr(self, 'cards_container'): + print(f"Cards container exists: {self.cards_container}") + print(f"Cards container visible: {self.cards_container.isVisible()}") + print(f"Cards container parent: {self.cards_container.parent()}") + + # Make sure the cards container itself is visible + self.cards_container.setVisible(True) + print(f"Cards container set visible: {self.cards_container.isVisible()}") + + # Check cards list if available + if hasattr(self, 'cards_list'): + print(f"Cards list has {len(self.cards_list)} cards") + for i, card in enumerate(self.cards_list): + print(f"Card {i} in list - visible: {card.isVisible()}, parent: {card.parent()}") + card.setVisible(True) + print(f"Card {i} set visible: {card.isVisible()}") + + # Ensure all child cards in layout are visible + if hasattr(self, 'cards_layout'): + layout = self.cards_layout + print(f"Layout has {layout.count()} items") + for i in range(layout.count()): + item = layout.itemAt(i) + if item and item.widget(): + widget = item.widget() + print(f"Layout item {i}: {widget}, visible: {widget.isVisible()}") + widget.setVisible(True) + print(f"Layout item {i} set visible: {widget.isVisible()}") + else: + print("ERROR: cards_container does not exist!") + + print("=== ensure_cards_visible finished ===") + + def _create_result_card(self, title: str, percentages=None) -> QWidget: + if percentages is None: + percentages = [30, 45, 15, 10] + card = QWidget() + card.setObjectName("result_card") + card.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + card.setMinimumWidth(300) + card.setMaximumHeight(280) + + print(f"Creating card with title: {title[:30]}...") + print(f"Card object: {card}") + print(f"Card visible by default: {card.isVisible()}") + + card_layout = QVBoxLayout(card) + card_layout.setContentsMargins(10, 8, 10, 10) + card_layout.setSpacing(6) + + header_layout = QHBoxLayout() + title_label = QLabel(title) + title_label.setTextFormat(Qt.TextFormat.RichText) + title_label.setWordWrap(True) + header_layout.addWidget(title_label) + header_layout.addStretch(1) + + close_btn = QPushButton() + close_btn.setObjectName("close_card_button") + close_btn.setIcon(QIcon("resources/close.png")) + close_btn.setIconSize(QSize(13, 13)) + header_layout.addWidget(close_btn, 0, Qt.AlignmentFlag.AlignRight) + + def remove_card(): + card.setParent(None) + card.deleteLater() + close_btn.clicked.connect(remove_card) + + card_layout.addLayout(header_layout) + + # Embed radial bar graph inside the card using QWebEngineView + graph = QWebEngineView(card) + graph.setHtml(self._build_radial_bar_html(percentages)) + graph.setZoomFactor(0.6) + graph.setMinimumHeight(180) + card_layout.addWidget(graph) + + print(f"Card created successfully with graph. Final card visible: {card.isVisible()}") + print(f"Card size: {card.size()}, Card geometry: {card.geometry()}") + + return card + + def switch_to_results_widget(self): + self.main_content_widget.setStyleSheet(""" + #main_content_widget { + background-color: #FDEFEF; + border: 1px solid #000000; + border-top: none; + } + """) + + if self.pressed==0: + print("=== Switching to results widget ===") + self.top_button_right_panel.setObjectName("top_button_right_panel_pressed") + self.welcome_label.setVisible(False) + + # Initialize navigation system + self.current_page = 0 + self.bottom_buttons_widget.setVisible(True) + self.update_page_display() + + self.pressed=1 + print("=== Finished switching to results widget ===") + else: + print("=== Switching back to welcome ===") + self.top_button_right_panel.setObjectName("top_button_right_panel") + self.hide_all_page_widgets() + self.bottom_buttons_widget.setVisible(False) + self.welcome_label.setVisible(True) + self.pressed=0 + print("=== Finished switching back to welcome ===") + + def go_next(self): + """Handle next button click""" + if self.current_page < self.max_page: + self.current_page += 1 + self.update_page_display() + + def go_back(self): + """Handle back button click""" + if self.current_page > 0: + self.current_page -= 1 + self.update_page_display() + + def update_page_display(self): + """Update the display based on current page""" + # Hide all widgets first + self.hide_all_page_widgets() + + # Update button states + self.back_button.setEnabled(self.current_page > 0) + self.next_button.setEnabled(self.current_page < self.max_page) + + if self.current_page == 0: + # Show initial 50-year graphs + self.cards_container.setVisible(True) + self.legend_widget.setVisible(True) + self.bar_graph_widget.setVisible(True) + self.ensure_cards_visible() + elif self.current_page == 1: + # Show pie chart with the original 3 radial cards + self.cards_container.setVisible(True) + self.legend_widget.setVisible(True) + self.pie_chart_widget.setVisible(True) + self.ensure_cards_visible() + elif self.current_page == 2: + # Show 100-year graphs + self.cards_container_100.setVisible(True) + self.legend_widget_100.setVisible(True) + self.bar_graph_widget_100.setVisible(True) + elif self.current_page == 3: + # Show bubble graphs + self.bubble_cards_container.setVisible(True) + + def hide_all_page_widgets(self): + """Hide all page-specific widgets""" + # Page 0 widgets + self.cards_container.setVisible(False) + self.legend_widget.setVisible(False) + self.bar_graph_widget.setVisible(False) + + # Page 1 widgets + self.pie_chart_widget.setVisible(False) + + # Page 2 widgets + self.cards_container_100.setVisible(False) + self.legend_widget_100.setVisible(False) + self.bar_graph_widget_100.setVisible(False) + + # Page 3 widgets + self.bubble_cards_container.setVisible(False) + + def _build_pie_chart(self) -> QWidget: + """Create pie chart widget""" + pie_chart_widget = QWidget() + pie_chart_layout = QVBoxLayout(pie_chart_widget) + pie_chart_layout.setContentsMargins(20, 15, 20, 15) + pie_chart_layout.setSpacing(10) + + # Title for the pie chart + title_label = QLabel("Cost Breakdown - Pie Chart") + title_label.setStyleSheet(""" + QLabel { + font-size: 16px; + font-weight: bold; + color: #333333; + text-align: center; + } + """) + title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + pie_chart_layout.addWidget(title_label) + + # Create the pie chart using QWebEngineView + pie_view = QWebEngineView() + pie_view.setHtml(self._build_pie_chart_html()) + pie_view.setZoomFactor(0.8) + pie_view.setMinimumHeight(400) + pie_view.setMaximumHeight(500) + pie_view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + pie_view.setMinimumWidth(600) + pie_chart_layout.addWidget(pie_view) + + return pie_chart_widget + + def _build_pie_chart_html(self) -> str: + """Generate HTML for pie chart using code from Pie chart.py""" + # Sample data based on the pie chart.py format + data_with_colors = [ + { + "label": "Road user cost", + "cost": 123.93, + "percent": 47.5, + "color": "#FF8C00", + "disabled": False + }, + { + "label": "Time cost estimate", + "cost": 2.32, + "percent": 0.9, + "color": "#483D8B", + "disabled": False + }, + { + "label": "Embodied carbon emissions", + "cost": 8.53, + "percent": 3.3, + "color": "#B22222", + "disabled": False + }, + { + "label": "Initial construction cost", + "cost": 61.83, + "percent": 23.7, + "color": "#996633", + "disabled": False + }, + { + "label": "Additional CO2 e costs due to rerouting", + "cost": 29.03, + "percent": 11.1, + "color": "#8B0000", + "disabled": False + }, + { + "label": "Periodic Maintenance costs", + "cost": 1.44, + "percent": 0.4, + "color": "#F6FB05", + "disabled": False + }, + { + "label": "Periodic maintenance carbon emissions", + "cost": 16.99, + "percent": 6.4, + "color": "#A52A2A", + "disabled": False + }, + { + "label": "Annual routine inspection costs", + "cost": 14.32, + "percent": 5.5, + "color": "#4682B4", + "disabled": False + }, + { + "label": "Repair and rehabilitation costs", + "cost": 1.99, + "percent": 0.8, + "color": "#008000", + "disabled": False + }, + { + "label": "Demolition and deconstruction costs", + "cost": 0.77, + "percent": 0.3, + "color": "#800080", + "disabled": False + } + ] + + import json + data_js = json.dumps(data_with_colors) + + html = f""" + + + + Cost Breakdown Pie Chart + + + + +
+

Cost Breakdown (in Lakhs)

+
+
+
+ + + + + """ + return html + + def _build_bar_graph_100(self) -> QWidget: + """Create 100-year bar graph widget""" + bar_graph_widget = QWidget() + bar_graph_layout = QVBoxLayout(bar_graph_widget) + bar_graph_layout.setContentsMargins(20, 15, 20, 15) + bar_graph_layout.setSpacing(10) + + # Title for the bar graph + title_label = QLabel("Life-Cycle Costs for 100 years") + title_label.setStyleSheet(""" + QLabel { + font-size: 16px; + font-weight: bold; + color: #333333; + text-align: center; + } + """) + title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + bar_graph_layout.addWidget(title_label) + + # Create the bar graph using QWebEngineView + graph_view = QWebEngineView() + graph_view.setHtml(self._build_bar_graph_100_html()) + graph_view.setZoomFactor(0.8) + graph_view.setMinimumHeight(300) + graph_view.setMaximumHeight(400) + graph_view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + graph_view.setMinimumWidth(500) + bar_graph_layout.addWidget(graph_view) + + return bar_graph_widget + + def _build_bar_graph_100_html(self) -> str: + """Generate HTML for the 100-year horizontal bar graph""" + # Use similar data but adjusted for 100 years + name = [ + "Initial Construction Cost", "Initial Carbon Emission Cost", "Time Cost", + "Road User Cost", "Periodic Maintenance Costs", "Maintenance Emission Costs", + "Routine Inspection Costs", "Repair & Rehabilitation Costs", "Reconstruction Costs", + "Demolition & Disposal Cost", "Recycling Cost" + ] + # Doubled costs for 100 years simulation + cost = [ + 61.83, 29.03, 0.0, 247.86, 2.88, 0.0, 0.0, 0.0, 0.0, 18.69, 0.0 + ] + + # Clean data + temp_name = name[:11] # Exclude total + temp_cost = cost[:11] + + # Display adjustment for zero values + temp_cost_display = [] + if temp_cost: + non_zero_costs = [c for c in temp_cost if c > 0] + if non_zero_costs: + min_non_zero = min(non_zero_costs) + for j in temp_cost: + if j == 0.0: + temp_cost_display.append(min_non_zero / 2) + else: + temp_cost_display.append(j) + else: + temp_cost_display = temp_cost + else: + temp_cost_display = temp_cost + + # Color list + color_list = ['#638B48', '#6F6F6F', '#638B48', '#E09365', '#6F6F6F', '#638B48', + '#6F6F6F', '#638B48', '#638B48', '#638B48', '#638B48'] + + # Create data array for D3 + data = [] + for i, (n, c, col) in enumerate(zip(temp_name, temp_cost_display, color_list[:len(temp_name)])): + data.append({"name": n, "value": c, "color": col}) + + html = f""" + + + + + D3.js Interactive Horizontal Bar Graph - 100 Years + + + +

Life Cycle Cost for 100 Years

+
+ + + + + """ + return html + + def _create_bubble_card(self, title: str) -> QWidget: + """Create a bubble chart card""" + card = QWidget() + card.setObjectName("result_card") + card.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + card.setMinimumWidth(420) + card.setMaximumHeight(380) + + card_layout = QVBoxLayout(card) + card_layout.setContentsMargins(10, 8, 10, 10) + card_layout.setSpacing(6) + + header_layout = QHBoxLayout() + title_label = QLabel(title) + title_label.setTextFormat(Qt.TextFormat.RichText) + title_label.setWordWrap(True) + header_layout.addWidget(title_label) + header_layout.addStretch(1) + + close_btn = QPushButton() + close_btn.setObjectName("close_card_button") + close_btn.setIcon(QIcon("resources/close.png")) + close_btn.setIconSize(QSize(13, 13)) + header_layout.addWidget(close_btn, 0, Qt.AlignmentFlag.AlignRight) + + def remove_card(): + card.setParent(None) + card.deleteLater() + close_btn.clicked.connect(remove_card) + + card_layout.addLayout(header_layout) + + # Embed bubble graph inside the card using QWebEngineView + graph = QWebEngineView(card) + graph.setHtml(self._build_bubble_graph_html()) + graph.setZoomFactor(0.8) + graph.setMinimumHeight(320) + card_layout.addWidget(graph) + + return card + + def _build_bubble_graph_html(self) -> str: + """Generate HTML for bubble graph using code from Bubble graph.py""" + # Sample data based on bubble graph.py + val_a, val_b, val_c = 53.71, 40.4, 6.4 + cost_a, cost_b, cost_c = 29.03, 19.36, 16.99 + + html = f""" + + + + + Bubble Chart Layout + + + + +
+
+
+ A - Initial Carbon Emission Cost + B - Carbon Emission due to Re-Routing + C - Maintenance Emission Costs +
+
+
+ + + + + """ + return html + +#----------------Standalone-Test-Code-------------------------------- + +class MyMainWindow(QMainWindow): + def __init__(self): + super().__init__() + + self.setStyleSheet("border: none") + + self.central_widget = QWidget() + self.central_widget.setObjectName("central_widget") + self.setCentralWidget(self.central_widget) + + self.main_h_layout = QHBoxLayout(self.central_widget) + self.main_h_layout.addStretch(1) + + self.main_h_layout.addWidget(ResultsWidget(), 2) + + self.setWindowState(Qt.WindowMaximized) + + +if __name__ == "__main__": + QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) + app = QApplication(sys.argv) + window = MyMainWindow() + window.show() + sys.exit(app.exec()) \ No newline at end of file From f77764298db8152bad4cc712dd8e615df4de1bdc Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Thu, 6 Nov 2025 22:25:32 +0530 Subject: [PATCH 15/22] Integrated database and functions --- src/osbridgelcca/desktop_app/main_template.py | 67 +- .../widgets/bridge_and_traffic_data.py | 89 ++- .../carbon_emission_cost_data.py | 29 +- .../carbon_emission_data.py | 84 +- .../desktop_app/widgets/comparison_widget.py | 2 +- .../widgets/demolition_and_recycling_data.py | 56 +- .../desktop_app/widgets/financial_data.py | 34 +- .../widgets/maintenance_repair_data.py | 53 +- .../widgets/project_details_left_widget.py | 25 +- .../widgets/project_details_right_widget.py | 15 +- .../desktop_app/widgets/results_widget.py | 2 +- .../auxiliary_works_widget.py | 271 ++++--- .../structure_works_data/foundation_widget.py | 101 ++- .../sub_structure_widget.py | 224 +++--- .../super_structure_widget.py | 223 +++--- .../widgets/utils/cost_component.py | 702 ++++++++++++++++ .../desktop_app/widgets/utils/data.py | 312 ++++++++ .../desktop_app/widgets/utils/database.py | 754 ++++++++++++++++++ 18 files changed, 2616 insertions(+), 427 deletions(-) create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/cost_component.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/data.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/database.py diff --git a/src/osbridgelcca/desktop_app/main_template.py b/src/osbridgelcca/desktop_app/main_template.py index 227ad4b..d97d975 100644 --- a/src/osbridgelcca/desktop_app/main_template.py +++ b/src/osbridgelcca/desktop_app/main_template.py @@ -26,27 +26,44 @@ from widgets.demolition_and_recycling_data import DemolitionAndRecyclingData from widgets.project_details_left_widget import ProjectDetailsLeft from widgets.tab_widget import CustomTabWidget +from widgets.utils.data import * +from widgets.utils.database import DatabaseManager from PySide6.QtWidgets import QStackedWidget class UiMainWindow(object): def setupUi(self, MainWindow): + self.database_manager = DatabaseManager() # To check if tab widget is there or no self.tabs_active = False + self.results = { + COST_TOTAL_INIT_CONST: None, + COST_TOTAL_INIT_CARBON_EMISSION: None, + COST_TIME: None, + COST_TOTAL_ROAD_USER: None, + COST_ADDITIONAL_CARBON_EMISSION: None, + COST_PERIODIC_MAINTAINANCE: None, + COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION: None, + COST_TOTAL_ROUTINE_INSPECTION: None, + COST_REPAIR_REHAB: None, + COST_DEMOLITION_DISPOSAL: None, + COST_RECYCLING: None, + COST_RECONSTRUCTION: None + } # Contains name of widget and its index in tabs self.active_tab_widgets = {} self.widget_map = { - "Structure Works Data": Foundation, - "Foundation": Foundation, - "Super-Structure": SuperStructure, - "Sub-Structure": SubStructure, - "Miscellaneous": AuxiliaryWorks, - "Financial Data": FinancialData, - "Carbon Emission Data": CarbonEmissionData, - "Carbon Emission Cost Data": CarbonEmissionCostData, - "Bridge and Traffic Data": BridgeAndTrafficData, - "Maintenance and Repair": MaintenanceRepairData, - "Demolition and Recycling": DemolitionAndRecyclingData + KEY_STRUCTURE_WORKS_DATA: Foundation, + KEY_FOUNDATION: Foundation, + KEY_SUPERSTRUCTURE: SuperStructure, + KEY_SUBSTRUCTURE: SubStructure, + KEY_AUXILIARY: AuxiliaryWorks, + KEY_FINANCIAL: FinancialData, + KEY_CARBON_EMISSION: CarbonEmissionData, + KEY_CARBON_EMISSION_COST: CarbonEmissionCostData, + KEY_BRIDGE_TRAFFIC: BridgeAndTrafficData, + KEY_MAINTAINANCE_REPAIR: MaintenanceRepairData, + KEY_DEMOLITION_RECYCLE: DemolitionAndRecyclingData } if not MainWindow.objectName(): @@ -422,6 +439,9 @@ def show_comparison_widget(): self.compare.clicked.connect(show_comparison_widget) def show_project_details_widget(widget_name=None): + if widget_name == KEY_STRUCTURE_WORKS_DATA: + # treat it as foundation + widget_name = KEY_FOUNDATION if widget_name and widget_name in self.widget_map: # Tabs are already visible if self.tabs_active: @@ -435,8 +455,10 @@ def show_project_details_widget(widget_name=None): self.active_tab_widgets = {} self.tabs_active = True self.current_right_widget = CustomTabWidget(parent=self) + # Add Tab widget self.right_panel_placeholder.layout().addWidget(self.current_right_widget) + # Remove left widget first self.remove_left_widget() @@ -460,7 +482,7 @@ def show_project_details_widget(widget_name=None): if hasattr(self.current_right_widget, 'param_buttons'): for btn in self.current_right_widget.param_buttons: btn.clicked.connect(lambda checked, b=btn: show_project_details_widget(b.text().strip())) - + def remove_right_widget(): if self.current_right_widget: self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) @@ -480,6 +502,9 @@ def remove_left_widget(): def show_project_detail_widgets(self, widget_name): """Public method to show project detail widgets""" + if widget_name == KEY_STRUCTURE_WORKS_DATA: + # treat it as foundation + widget_name = KEY_FOUNDATION if widget_name and widget_name in self.widget_map: # Tabs are already visible if self.tabs_active: @@ -489,7 +514,23 @@ def show_project_detail_widgets(self, widget_name): self.current_right_widget.activate_tab(index) else: # add new tab - widget = self.widget_map[widget_name]() + widget = self.widget_map[widget_name](database=self.database_manager, parent=self) + widget.next.connect(self.next_widget) + widget.back.connect(self.prev_widget) index = self.current_right_widget.add_new_tab(widget, widget_name) # add record of this widget self.active_tab_widgets[widget_name] = index + + def next_widget(self, widget_name): + current = list(self.widget_map.keys()).index(widget_name) + next = list(self.widget_map.keys())[current + 1] + if self.current_left_widget and hasattr(self.current_left_widget, 'all_param_buttons'): + self.current_left_widget.all_param_buttons[next].click() + self.show_project_detail_widgets(next) + + def prev_widget(self, widget_name): + current = list(self.widget_map.keys()).index(widget_name) + prev = list(self.widget_map.keys())[current - 1] + if self.current_left_widget and hasattr(self.current_left_widget, 'all_param_buttons'): + self.current_left_widget.all_param_buttons[prev].click() + self.show_project_detail_widgets(prev) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py index ac4492c..c63beac 100644 --- a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py +++ b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py @@ -2,13 +2,21 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from .utils.data import * import sys import os class BridgeAndTrafficData(QWidget): closed = Signal() - def __init__(self, parent=None): + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): super().__init__() + self.parent = parent + self.database_manager = database + self.data = bridge_traffic_data.get(KEY_BRIDGE_TRAFFIC) + self.traffic_widgets = [] + self.text_box_width = 200 self.setStyleSheet(""" #central_panel_widget { @@ -130,10 +138,12 @@ def __init__(self, parent=None): QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; + color: #000000; padding: 3px 10px; } QComboBox::drop-down { border: none; + color: #000000; padding-right: 5px; } QComboBox::down-arrow { @@ -144,9 +154,13 @@ def __init__(self, parent=None): QComboBox QAbstractItemView { border: 1px solid #DDDCE0; border-radius: 5px; + color: #000000; background-color: #FFFFFF; outline: none; } + QComboBox QAbstractItemView::item { + color: #000000; + } QComboBox QAbstractItemView::item:selected { background-color: #FDEFEF; color: #000000; @@ -187,14 +201,12 @@ def __init__(self, parent=None): label = QLabel("Number of Lanes") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 0, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") - valuer_combo.addItem("1") - valuer_combo.addItem("2") - valuer_combo.addItem("3") - valuer_combo.addItem("4") - valuer_combo.addItem("5+") + valuer_combo.addItems(self.data[KEY_LANES][KEY_OPTIONS]) + self.traffic_widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 0, 1, 1, 1) info_icon = QLabel(" ") @@ -215,6 +227,7 @@ def __init__(self, parent=None): padding: 3px 10px; } """) + self.traffic_widgets.append(input_widget) grid_layout.addWidget(input_widget, 1, 1, 1, 1) info_icon = QLabel("(km)") info_icon.setStyleSheet("color: grey; font-size: 14px;") @@ -225,14 +238,14 @@ def __init__(self, parent=None): label = QLabel("Road Roughness") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 3, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") - valuer_combo.addItem("option a") - valuer_combo.addItem("option b") - valuer_combo.addItem("option c") - valuer_combo.addItem("option d") + valuer_combo.addItems(self.data[KEY_ROADROUGHNESS][KEY_OPTIONS]) + self.traffic_widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 3, 1, 1, 1) + info_icon = QLabel("(mm/km)") info_icon.setStyleSheet("color: grey; font-size: 14px;") info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) @@ -242,35 +255,31 @@ def __init__(self, parent=None): label = QLabel("Road Rise and Fall (RF)") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 4, 0, 1, 1) - input_widget = QLineEdit(self.general_widget) - input_widget.setFixedWidth(self.text_box_width) - input_widget.setStyleSheet(""" - QLineEdit { - border: 1px solid #DDDCE0; - border-radius: 10px; - padding: 3px 10px; - } - """) - grid_layout.addWidget(input_widget, 4, 1, 1, 1) + + valuer_combo = QComboBox(self.general_widget) + valuer_combo.setFixedWidth(self.text_box_width) + valuer_combo.setPlaceholderText("Select") + valuer_combo.addItems(self.data[KEY_ROAD_RISE_AND_FALL][KEY_OPTIONS]) + self.traffic_widgets.append(valuer_combo) + grid_layout.addWidget(valuer_combo, 4, 1, 1, 1) info_icon = QLabel("(m/km)") info_icon.setStyleSheet("color: grey; font-size: 14px;") info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) grid_layout.addWidget(info_icon, 4, 2, 1, 1) + # Type of Road label = QLabel("Type of Road") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 5, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") - valuer_combo.addItem("option a") - valuer_combo.addItem("option b") - valuer_combo.addItem("option c") - valuer_combo.addItem("option d") + valuer_combo.addItems(self.data[KEY_TYPE_OF_ROAD][KEY_OPTIONS]) grid_layout.addWidget(valuer_combo, 5, 1, 1, 1) - + self.traffic_widgets.append(valuer_combo) info_icon = QLabel(" ") info_icon.setStyleSheet("color: grey; font-size: 14px;") @@ -283,13 +292,12 @@ def __init__(self, parent=None): label.setWordWrap(True) label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 6, 0, 1, 1) + valuer_combo = QComboBox(self.general_widget) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") - valuer_combo.addItem("option a") - valuer_combo.addItem("option b") - valuer_combo.addItem("option c") - valuer_combo.addItem("option d") + valuer_combo.addItems(self.data[KEY_ANNUAL_INCREASE][KEY_OPTIONS]) + self.traffic_widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 6, 1, 1, 1) info_icon = QLabel("(%)") @@ -327,6 +335,7 @@ def __init__(self, parent=None): v_label.setFixedWidth(50) v_label.setStyleSheet("background-color: #FFFFFF; border: 1px solid #FFFFFF; border-radius: 10px; padding: 10px 10px 10px 1px;") v_input = QLineEdit() + self.traffic_widgets.append(v_input) v_input.setFixedWidth(self.text_box_width) v_input.setStyleSheet(""" QLineEdit { @@ -362,10 +371,13 @@ def __init__(self, parent=None): back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_BRIDGE_TRAFFIC)) self.button_h_layout.addWidget(back_button) next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(self.collect_data) + next_button.clicked.connect(lambda: self.next.emit(KEY_BRIDGE_TRAFFIC)) self.button_h_layout.addWidget(next_button) # Add initial spacing before the navigation buttons @@ -377,6 +389,25 @@ def __init__(self, parent=None): left_panel_vlayout.addWidget(self.scroll_area) + def collect_data(self): + data = [] + for widget in self.traffic_widgets: + if isinstance(widget, QComboBox): + value = widget.currentText() + elif isinstance(widget, QLineEdit): + value = widget.text() if widget.text() != "" else "0" + data.append(value) + print("Collected Data from UI:",data) + + # calculate Road User Cost Calculation + road_user_data = self.database_manager.calculate_irc_road_cost(data) + # Update Results Dict + self.parent.results[COST_TOTAL_ROAD_USER] = road_user_data + + # Calculate additional carbon emission costs + cost = self.database_manager.additional_carbon_emission_cost(data) + self.parent.results[COST_ADDITIONAL_CARBON_EMISSION] = cost + def close_widget(self): self.closed.emit() self.setParent(None) diff --git a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py index 0248e19..4bf4fae 100644 --- a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py +++ b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py @@ -2,12 +2,18 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from ..utils.data import * import sys class CarbonEmissionCostData(QWidget): closed = Signal() - def __init__(self, parent=None): - super().__init__(parent) + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): + super().__init__() + self.parent = parent + self.widget = [] + self.database_manager = database self.setStyleSheet(""" #central_panel_widget { @@ -161,6 +167,7 @@ def __init__(self, parent=None): label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 0, 0, 1, 1) input_widget = QLineEdit(self.general_widget) + self.widget.append(input_widget) input_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) input_widget.setFixedWidth(field_width) input_widget.setText("SSP2") # Set default text @@ -178,6 +185,7 @@ def __init__(self, parent=None): label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 1, 0, 1, 1) input_widget = QLineEdit(self.general_widget) + self.widget.append(input_widget) input_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) input_widget.setFixedWidth(field_width) input_widget.setText("RCP60") # Set default text @@ -200,8 +208,8 @@ def __init__(self, parent=None): scc_h_layout = QHBoxLayout(scc_widget) scc_h_layout.setContentsMargins(0,0,0,0) scc_h_layout.setSpacing(10) - input_widget = QLineEdit() + self.widget.append(input_widget) input_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) input_widget.setFixedWidth(field_width) # Adjusted width for SCC input to match the image input_widget.setText("6.3936") # Set default text @@ -239,10 +247,13 @@ def __init__(self, parent=None): back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_CARBON_EMISSION_COST)) self.button_h_layout.addWidget(back_button) next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(self.collect_data) + next_button.clicked.connect(lambda: self.next.emit(KEY_CARBON_EMISSION_COST)) self.button_h_layout.addWidget(next_button) # Add initial spacing before the navigation buttons @@ -253,6 +264,18 @@ def __init__(self, parent=None): self.scroll_content_layout.addSpacerItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) left_panel_vlayout.addWidget(self.scroll_area) + + def collect_data(self): + data = [] + for widget in self.widget: + data.append(widget.text()) + print("Collected Data from UI:",data) + + # calculate Carbon Emission Cost + carbon_cost = float(self.widget[2].text()) # Social Cost of Carbon input + carbon_emission_cost = self.database_manager.carbon_emission_cost(carbon_cost) + # Update Results Dict + self.parent.results[COST_TOTAL_INIT_CARBON_EMISSION] = carbon_cost def close_widget(self): self.closed.emit() diff --git a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py index dde9143..548fdc9 100644 --- a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py +++ b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py @@ -2,12 +2,14 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from ..utils.data import * import sys class ComponentWidget(QWidget): - def __init__(self, parent=None): + def __init__(self, data, parent=None): super().__init__(parent) - + self.widgets = [] + self.data = data self.material_rows = [] # To store references to widgets in each material row self.current_material_row_idx = 1 # Start index for material rows (0 is header) @@ -38,18 +40,20 @@ def init_ui(self): self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) # Add initial two material rows - self.add_material_row() - self.add_material_row() - - # --- Add Material Button --- - self.add_material_button = QPushButton("+ Add Material") - self.add_material_button.setObjectName("add_material_button") - self.add_material_button.clicked.connect(self.add_material_row) - self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + for item in self.data: + self.widgets.append(self.add_material_row(item)) + + + + # # --- Add Material Button --- + # self.add_material_button = QPushButton("+ Add Material") + # self.add_material_button.setObjectName("add_material_button") + # self.add_material_button.clicked.connect(self.add_material_row) + # self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) - def add_material_row(self): - row_widgets = {} + def add_material_row(self, item): + row_widgets = [item[1], item[2], item[3], item[4]] row_idx = self.current_material_row_idx # Set fixed width for input widgets. @@ -57,25 +61,23 @@ def add_material_row(self): fixed_input_width_line_edit = 80 # Width for individual line edits type_material_combo = QComboBox() - type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) + type_material_combo.addItem(item[0]) type_material_combo.setObjectName("MaterialGridInput") type_material_combo.setFixedWidth(fixed_input_width_combo) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0, alignment=Qt.AlignmentFlag.AlignHCenter) - row_widgets['type'] = type_material_combo quantity_edit = QLineEdit() - quantity_edit.setPlaceholderText("0") + quantity_edit.setText(str(item[2])) + quantity_edit.setReadOnly(True) quantity_edit.setObjectName("MaterialGridInput") quantity_edit.setFixedWidth(fixed_input_width_line_edit) self.material_grid_layout.addWidget(quantity_edit, row_idx, 1, alignment=Qt.AlignmentFlag.AlignHCenter) - row_widgets['quantity'] = quantity_edit unit_combo_m3 = QComboBox() - unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) + unit_combo_m3.addItem(str(item[1])) unit_combo_m3.setObjectName("MaterialGridInput") unit_combo_m3.setFixedWidth(fixed_input_width_combo) self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 2, alignment=Qt.AlignmentFlag.AlignHCenter) - row_widgets['unit_m3'] = unit_combo_m3 # --- Embodied Carbon Energy (LineEdit + QLabel for unit text) - Column 3 --- @@ -85,6 +87,7 @@ def add_material_row(self): embodied_carbon_layout.setSpacing(5) # Small spacing between line edit and label embodied_carbon_edit = QLineEdit() + row_widgets.append(embodied_carbon_edit) embodied_carbon_edit.setPlaceholderText("0.00") embodied_carbon_edit.setObjectName("MaterialGridInput") embodied_carbon_edit.setFixedWidth(fixed_input_width_combo) # Changed to fixed_input_width_combo @@ -98,8 +101,6 @@ def add_material_row(self): self.material_grid_layout.addLayout(embodied_carbon_layout, row_idx, 3, alignment=Qt.AlignmentFlag.AlignHCenter) - row_widgets['embodied_carbon_edit'] = embodied_carbon_edit - row_widgets['embodied_carbon_unit_label'] = embodied_carbon_unit_label # --- Carbon Emission Factor (LineEdit + QLabel for unit text) - Column 4 --- @@ -110,6 +111,7 @@ def add_material_row(self): carbon_emission_layout.addStretch() carbon_emission_edit = QLineEdit() + row_widgets.append(carbon_emission_edit) carbon_emission_edit.setPlaceholderText("0.00") carbon_emission_edit.setObjectName("MaterialGridInput") carbon_emission_edit.setFixedWidth(fixed_input_width_combo) # Changed to fixed_input_width_combo @@ -124,8 +126,6 @@ def add_material_row(self): carbon_emission_layout.addStretch() self.material_grid_layout.addLayout(carbon_emission_layout, row_idx, 4) - row_widgets['carbon_emission_edit'] = carbon_emission_edit - row_widgets['carbon_emission_unit_label'] = carbon_emission_unit_label self.material_rows.append(row_widgets) @@ -133,6 +133,10 @@ def add_material_row(self): self.updateGeometry() self.adjustSize() + return row_widgets + + + def remove_material_row_by_widgets(self, row_widgets_to_remove): if row_widgets_to_remove not in self.material_rows: @@ -185,11 +189,31 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.material_grid_layout.invalidate() self.adjustSize() + + def collect_data(self): + p = [] + for row in self.widgets: + data = { + KEY_TYPE: row[2], + KEY_GRADE: row[3], + KEY_QUANTITY: row[1], + KEY_UNIT_M3: row[0], + KEY_EMBODIED_CARBON_ENERGY: row[4].text() if row[4].text() else "0", + KEY_CARBON_EMISSION_FACTOR: row[5].text() if row[4].text() else "0", + } + p.append(data) + return p + + class CarbonEmissionData(QWidget): closed = Signal() - def __init__(self): + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): super().__init__() - + self.database_manager = database + self.material_store = self.database_manager.get_unique_materials_and_grades() + print(self.material_store) self.component_widgets = [] # To store references to each ComponentWidget instance self.setStyleSheet(""" @@ -414,15 +438,19 @@ def __init__(self): back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_CARBON_EMISSION)) self.button_h_layout.addWidget(back_button) next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(self.collect_data) + next_button.clicked.connect(lambda: self.next.emit(KEY_CARBON_EMISSION)) self.button_h_layout.addWidget(next_button) # Add the initial component layout self.add_component_layout() + # Add initial spacing before the navigation buttons self.scroll_content_layout.addLayout(self.button_h_layout) @@ -435,7 +463,7 @@ def __init__(self): def add_component_layout(self): - new_component = ComponentWidget(self) + new_component = ComponentWidget(data=self.material_store, parent=self) self.component_widgets.append(new_component) # Temporarily remove button_h_layout and the vertical spacer for insertion @@ -480,6 +508,12 @@ def close_widget(self): self.closed.emit() self.setParent(None) + def collect_data(self): + data = self.component_widgets[0].collect_data() + print("Collected Data from UI:",data) + self.database_manager.insert_carbon_emission_data(data) + + #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/src/osbridgelcca/desktop_app/widgets/comparison_widget.py b/src/osbridgelcca/desktop_app/widgets/comparison_widget.py index a64b680..93fa9fb 100644 --- a/src/osbridgelcca/desktop_app/widgets/comparison_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/comparison_widget.py @@ -9,7 +9,7 @@ class ComparisonWidget(QWidget): closed = Signal() def __init__(self, parent=None): - super().__init__(parent) + super().__init__() self.setObjectName("comparison_panel_widget") self.setStyleSheet(""" #comparison_panel_widget { diff --git a/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py b/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py index 3f5bafe..bc0d5f1 100644 --- a/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py +++ b/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py @@ -2,13 +2,19 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from .utils.data import * import sys import os class DemolitionAndRecyclingData(QWidget): closed = Signal() - def __init__(self, parent=None): + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): super().__init__() + self.parent = parent + self.database_manager = database + self.widgets = [] self.setStyleSheet(""" #central_panel_widget { @@ -166,6 +172,7 @@ def __init__(self, parent=None): demolition_layout.setContentsMargins(0,0,0,0) demolition_layout.setSpacing(10) demolition_input = QLineEdit() + self.widgets.append(demolition_input) demolition_input.setAlignment(Qt.AlignmentFlag.AlignCenter) demolition_input.setFixedWidth(field_width) demolition_input.setText("10") @@ -193,6 +200,7 @@ def __init__(self, parent=None): scrap_value_layout.setContentsMargins(0,0,0,0) scrap_value_layout.setSpacing(10) scrap_value_input = QLineEdit() + self.widgets.append(scrap_value_input) scrap_value_input.setAlignment(Qt.AlignmentFlag.AlignCenter) scrap_value_input.setFixedWidth(field_width) scrap_value_input.setText("50000") @@ -220,6 +228,7 @@ def __init__(self, parent=None): steel_scrap_layout.setContentsMargins(0,0,0,0) steel_scrap_layout.setSpacing(10) steel_scrap_input = QLineEdit() + self.widgets.append(steel_scrap_input) steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignCenter) steel_scrap_input.setFixedWidth(field_width) steel_scrap_input.setText("98") @@ -252,9 +261,11 @@ def __init__(self, parent=None): back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_DEMOLITION_RECYCLE)) self.button_h_layout.addWidget(back_button) calculate_button = QPushButton("Calculate") + calculate_button.clicked.connect(self.collect_data) calculate_button.setObjectName("nav_button") self.button_h_layout.addWidget(calculate_button) @@ -271,6 +282,49 @@ def close_widget(self): self.closed.emit() self.setParent(None) + + def collect_data(self): + data = [] + for widget in self.widgets: + print(f"Collecting data from widget: {widget}") + if isinstance(widget, QComboBox): + value = widget.currentText() + elif isinstance(widget, QLineEdit): + value = widget.text() + data.append(value) + print("Collected Data from UI:",data) + + # calculate demolition and disposal cost + total_initial_construction_cost = self.parent.results.get(COST_TOTAL_INIT_CONST) + cost = self.database_manager.demolition_and_disposal_cost(data, total_initial_construction_cost) + # Update Results Dict + self.parent.results[COST_DEMOLITION_DISPOSAL] = cost + + # recycling cost + cost = self.database_manager.recycling_cost(data) + # Update Results Dict + self.parent.results[COST_RECYCLING] = cost + + # reconstruction cost + initial_construction_cost = self.parent.results[COST_TOTAL_INIT_CONST] + demolition_cost = self.parent.results[COST_DEMOLITION_DISPOSAL] + carbon_emission_cost = self.parent.results[COST_TOTAL_INIT_CARBON_EMISSION] + time_cost = self.parent.results[COST_TIME] + roaduser_cost = self.parent.results[COST_TOTAL_ROAD_USER] + rerouting_carbon_cost = self.parent.results[COST_ADDITIONAL_CARBON_EMISSION] + + cost = self.database_manager.reconstruction_cost(initial_construction_cost, + demolition_cost, + carbon_emission_cost, + time_cost, + roaduser_cost, + rerouting_carbon_cost + ) + # Update Results Dict + self.parent.results[COST_RECONSTRUCTION] = cost + + print(f"Results:\n{self.parent.results}") + #----------------Standalone-Test-Code-------------------------------- class MyMainWindow(QMainWindow): diff --git a/src/osbridgelcca/desktop_app/widgets/financial_data.py b/src/osbridgelcca/desktop_app/widgets/financial_data.py index 523b7b9..261dbf3 100644 --- a/src/osbridgelcca/desktop_app/widgets/financial_data.py +++ b/src/osbridgelcca/desktop_app/widgets/financial_data.py @@ -2,13 +2,19 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from .utils.data import * import sys import os class FinancialData(QWidget): closed = Signal() - def __init__(self, parent=None): + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): super().__init__() + self.parent = parent + self.database_manager = database + self.widgets = [] self.setStyleSheet(""" #central_panel_widget { @@ -170,6 +176,7 @@ def __init__(self, parent=None): label1_layout.addWidget(info_icon) label1_layout.addStretch(1) input1 = QLineEdit() + self.widgets.append(input1) input1.setAlignment(Qt.AlignmentFlag.AlignLeft) input1.setFixedWidth(field_width) input1.setText("4.2500") @@ -191,6 +198,7 @@ def __init__(self, parent=None): label2 = QLabel("Interest Rate") label2.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input2 = QLineEdit() + self.widgets.append(input2) input2.setAlignment(Qt.AlignmentFlag.AlignLeft) input2.setFixedWidth(field_width) input2.setText("10") @@ -206,6 +214,7 @@ def __init__(self, parent=None): label3 = QLabel("Investment Ratio") label3.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input3 = QLineEdit() + self.widgets.append(input3) input3.setAlignment(Qt.AlignmentFlag.AlignLeft) input3.setFixedWidth(field_width) input3.setText("0.5000") @@ -220,6 +229,7 @@ def __init__(self, parent=None): label4 = QLabel("Duration of Study") label4.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input4 = QLineEdit() + self.widgets.append(input4) input4.setAlignment(Qt.AlignmentFlag.AlignLeft) input4.setFixedWidth(field_width) input4.setText("50 & 100") @@ -235,6 +245,7 @@ def __init__(self, parent=None): label5 = QLabel("Time for construction of Base Project") label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input5 = QLineEdit() + self.widgets.append(input5) input5.setAlignment(Qt.AlignmentFlag.AlignLeft) input5.setFixedWidth(field_width) input5.setText("") @@ -259,10 +270,14 @@ def __init__(self, parent=None): back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_FINANCIAL)) self.button_h_layout.addWidget(back_button) next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(self.collect_data) + + next_button.clicked.connect(lambda: self.next.emit(KEY_FINANCIAL)) self.button_h_layout.addWidget(next_button) # Add initial spacing before the navigation buttons @@ -278,6 +293,23 @@ def close_widget(self): self.closed.emit() self.setParent(None) + def collect_data(self): + data = [] + for widget in self.widgets: + print(f"Collecting data from widget: {widget}") + if isinstance(widget, QComboBox): + value = widget.currentText() + elif isinstance(widget, QLineEdit): + value = widget.text() if widget.text() != "" else "0" + data.append(value) + print("Collected Data from UI:",data) + + # calculate time cost + total_initial_construction_cost = self.parent.results.get(COST_TOTAL_INIT_CONST) + time_cost = self.database_manager.calculate_time_cost(data, total_initial_construction_cost) + # Update Results Dict + self.parent.results[COST_TIME] = time_cost + #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py b/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py index 26231fb..6a2e332 100644 --- a/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py +++ b/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py @@ -2,26 +2,28 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from .utils.data import * import sys import os class ComponentWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) - self.init_ui() - def init_ui(self): self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) self.component_first_scroll_content_layout.setSpacing(10) - - class MaintenanceRepairData(QWidget): closed = Signal() - def __init__(self, parent=None): + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): super().__init__() + self.parent = parent + self.database_manager = database + self.widget = [] self.component_widgets = [] @@ -176,6 +178,7 @@ def __init__(self, parent=None): label1 = QLabel("Periodic Maintenance Cost rate as\npercentage to total construction\ncost") label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) pmc_input = QLineEdit() + self.widget.append(pmc_input) pmc_input.setAlignment(Qt.AlignmentFlag.AlignTop) pmc_input.setFixedWidth(field_width) pmc_input.setText("0.555") @@ -197,6 +200,7 @@ def __init__(self, parent=None): label2 = QLabel("Annual Routine Inspection cost rate\nas percentage of total construction\ncost") label2.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) ari_input = QLineEdit() + self.widget.append(ari_input) ari_input.setAlignment(Qt.AlignmentFlag.AlignTop) ari_input.setFixedWidth(field_width) ari_input.setText("1") @@ -218,6 +222,7 @@ def __init__(self, parent=None): label3 = QLabel("Repair and Rehabilitation cost rate as\npercentage of total construction cost") label3.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) rr_input = QLineEdit() + self.widget.append(rr_input) rr_input.setAlignment(Qt.AlignmentFlag.AlignTop) rr_input.setFixedWidth(field_width) rr_input.setText("10") @@ -239,6 +244,7 @@ def __init__(self, parent=None): label4 = QLabel("Frequency of Periodic Maintenance") label4.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) fpm_input = QLineEdit() + self.widget.append(fpm_input) fpm_input.setAlignment(Qt.AlignmentFlag.AlignTop) fpm_input.setFixedWidth(field_width) fpm_input.setText("5") @@ -260,6 +266,7 @@ def __init__(self, parent=None): label5 = QLabel("Frequency of Routine Inspection") label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) fri_input = QLineEdit() + self.widget.append(fri_input) fri_input.setAlignment(Qt.AlignmentFlag.AlignTop) fri_input.setFixedWidth(field_width) fri_input.setText("1") @@ -291,10 +298,13 @@ def __init__(self, parent=None): back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_MAINTAINANCE_REPAIR)) self.button_h_layout.addWidget(back_button) next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(self.collect_data) + next_button.clicked.connect(lambda: self.next.emit(KEY_MAINTAINANCE_REPAIR)) self.button_h_layout.addWidget(next_button) @@ -311,6 +321,39 @@ def close_widget(self): self.closed.emit() self.setParent(None) + + def collect_data(self): + data = [] + for widget in self.widget: + if isinstance(widget, QComboBox): + value = widget.currentText() + elif isinstance(widget, QLineEdit): + value = widget.text() if widget.text() != "" else "0" + data.append(value) + + print("Collected Data from UI:",data) + + # Periodic Maintenance Cost Calculation + total_initial_construction_cost = self.parent.results.get(COST_TOTAL_INIT_CONST) + cost = self.database_manager.periodic_maintainance_cost(data, total_initial_construction_cost) + # Update Results Dict + self.parent.results[COST_PERIODIC_MAINTAINANCE] = cost + + # Routine Inspection Cost Calculation + cost = self.database_manager.routine_inspection_cost(total_initial_construction_cost) + # Update Results Dict + self.parent.results[COST_TOTAL_ROUTINE_INSPECTION] = cost + + # Repair and Rehabilitation Cost Calculation + cost = self.database_manager.repair_and_rehabilitation_cost(total_initial_construction_cost) + # Update Results Dict + self.parent.results[COST_REPAIR_REHAB] = cost + + # Periodic Maintenance Carbon Emission Cost Calculation (Concrete only) + cost = self.database_manager.periodic_maintainnce_carbon_emission_cost(data) + # Update Results Dict + self.parent.results[COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION] = cost + #----------------Standalone-Test-Code-------------------------------- # class MyMainWindow(QMainWindow): diff --git a/src/osbridgelcca/desktop_app/widgets/project_details_left_widget.py b/src/osbridgelcca/desktop_app/widgets/project_details_left_widget.py index 1dc58d1..344c982 100644 --- a/src/osbridgelcca/desktop_app/widgets/project_details_left_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/project_details_left_widget.py @@ -9,6 +9,7 @@ from widgets.structure_works_data.sub_structure_widget import SubStructure from widgets.structure_works_data.auxiliary_works_widget import AuxiliaryWorks from PySide6.QtWidgets import QStackedWidget +from .utils.data import * class ProjectDetailsLeft(QWidget): closed = Signal() @@ -21,7 +22,7 @@ def __init__(self, widget_map, parent): self.parent = parent self.current_selected_button = None - self.all_param_buttons = [] + self.all_param_buttons = {} self.setObjectName("left_panel_widget") self.setStyleSheet(""" @@ -220,14 +221,14 @@ def __init__(self, widget_map, parent): unselected_sub_icon = QIcon("resources/play-button-arrowhead.png") self.current_selected_button = None - self.all_param_buttons = [] + self.all_param_buttons = {} button_data = { - "Structure Works Data": ["Foundation", "Super-Structure", "Sub-Structure", "Miscellaneous"], - "Financial Data": [], - "Carbon Emission Data": ["Carbon Emission Cost Data"], - "Bridge and Traffic Data": [], - "Maintenance and Repair": [], - "Demolition and Recycling": [] + KEY_STRUCTURE_WORKS_DATA: [KEY_FOUNDATION, KEY_SUPERSTRUCTURE, KEY_SUBSTRUCTURE, KEY_AUXILIARY], + KEY_FINANCIAL: [], + KEY_CARBON_EMISSION: [KEY_CARBON_EMISSION_COST], + KEY_BRIDGE_TRAFFIC: [], + KEY_MAINTAINANCE_REPAIR: [], + KEY_DEMOLITION_RECYCLE: [] } for label, sublabels in button_data.items(): btn = QPushButton(label) @@ -236,11 +237,12 @@ def __init__(self, widget_map, parent): btn.setProperty("expanded", False) btn.setCursor(Qt.CursorShape.PointingHandCursor) btn.setLayoutDirection(Qt.LeftToRight) + btn.setFocusPolicy(Qt.StrongFocus) btn.setIcon(unselected_unexpanded_icon) btn.setIconSize(QSize(10, 10)) btn.clicked.connect(lambda checked ,b=btn ,name=label: self.show_structure_widget(name, b)) scroll_content_layout.addWidget(btn) - self.all_param_buttons.append(btn) + self.all_param_buttons[label] = btn if sublabels: sub_widgets = [] for sublabel in sublabels: @@ -249,13 +251,14 @@ def __init__(self, widget_map, parent): sub_btn.setProperty("selected", False) sub_btn.setIcon(unselected_sub_icon) sub_btn.setIconSize(QSize(10, 10)) + sub_btn.setFocusPolicy(Qt.StrongFocus) sub_btn.setCursor(Qt.CursorShape.PointingHandCursor) sub_btn.setLayoutDirection(Qt.LeftToRight) sub_btn.setVisible(False) sub_btn.clicked.connect(lambda checked ,b=sub_btn, name=sublabel: self.show_structure_widget(name,b)) scroll_content_layout.addWidget(sub_btn) sub_widgets.append(sub_btn) - self.all_param_buttons.append(sub_btn) + self.all_param_buttons[sublabel] = sub_btn def make_toggle(button, sub_widgets): def toggle(): @@ -325,7 +328,7 @@ def handle_button_selection(self, button_clicked=None, button_name=None): """ Handles the visual selection state of buttons in the side panel. """ - for b in self.all_param_buttons: + for b in self.all_param_buttons.values(): b.setProperty("selected", False) b.style().unpolish(b) b.style().polish(b) diff --git a/src/osbridgelcca/desktop_app/widgets/project_details_right_widget.py b/src/osbridgelcca/desktop_app/widgets/project_details_right_widget.py index ec9e20e..c04c20f 100644 --- a/src/osbridgelcca/desktop_app/widgets/project_details_right_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/project_details_right_widget.py @@ -3,12 +3,13 @@ from PySide6.QtGui import (QIcon) from PySide6.QtWidgets import (QHBoxLayout, QTextEdit, QScrollArea, QSpacerItem, QSizePolicy, QPushButton, QWidget, QLabel, QVBoxLayout, QGridLayout, QLineEdit, QComboBox) +from .utils.data import * import sys class ProjectDetailsWidget(QWidget): closed = Signal() def __init__(self, parent=None): - super().__init__(parent) + super().__init__() self.setObjectName("central_panel_widget") self.setStyleSheet(""" #central_panel_widget { @@ -381,12 +382,12 @@ def __init__(self, parent=None): self.input_param_option_layout.setContentsMargins(8, 8, 8, 8) self.input_param_option_layout.setSpacing(2) button_labels = [ - "Structure Works Data", - "Financial Data", - "Carbon Emission Data", - "Bridge and Traffic Data", - "Maintenance and Repair", - "Demolition and Recycling" + KEY_STRUCTURE_WORKS_DATA, + KEY_FINANCIAL, + KEY_CARBON_EMISSION, + KEY_BRIDGE_TRAFFIC, + KEY_MAINTAINANCE_REPAIR, + KEY_DEMOLITION_RECYCLE ] self.selected_icon = QIcon("resources/selected_icon.png") self.param_buttons = [] diff --git a/src/osbridgelcca/desktop_app/widgets/results_widget.py b/src/osbridgelcca/desktop_app/widgets/results_widget.py index cd29897..8c14cde 100644 --- a/src/osbridgelcca/desktop_app/widgets/results_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/results_widget.py @@ -14,7 +14,7 @@ class ResultsWidget(QWidget): closed = Signal() def __init__(self, parent=None): - super().__init__(parent) + super().__init__() self.setObjectName("central_panel_widget") self.setStyleSheet(""" #central_panel_widget { diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py index 6504aa2..71c5e55 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py @@ -2,34 +2,56 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from PySide6.QtGui import QDoubleValidator +from ..utils.data import * import sys class ComponentWidget(QWidget): def __init__(self, parent): super().__init__(parent) - - self.material_rows = [] # To store references to widgets in each material row - self.current_material_row_idx = 1 # Start index for material rows (0 is header) - + + self.data = construction_materials.get(KEY_AUXILIARY) + self.material_rows = [] + self.current_material_row_idx = 1 self.init_ui() + def collect_data(self): + rows_data = [] + for row in self.material_rows: + component = self.component_combobox.currentText() + material_type = row[KEY_TYPE].currentText() + material_grade = row[KEY_GRADE].currentText() + quantity = row[KEY_QUANTITY].text() + unit_m3 = row[KEY_UNIT_M3].currentText() + rate = row[KEY_RATE].text() + rate_data_source = row[KEY_RATE_DATA_SOURCE].text() + row_dict = { KEY_COMPONENT: component, + KEY_TYPE: material_type, + KEY_GRADE: material_grade, + KEY_QUANTITY: quantity if quantity.strip() else "0", + KEY_UNIT_M3: unit_m3, + KEY_RATE: rate if rate.strip() else "0.00", + KEY_RATE_DATA_SOURCE: rate_data_source + } + rows_data.append(row_dict) + return rows_data + def init_ui(self): - self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout = QVBoxLayout(self) self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) self.component_first_scroll_content_layout.setSpacing(10) - # Component Label and Dropdown with a remove button component_header_layout = QHBoxLayout() component_label = QLabel("Component:") component_label.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(component_label) self.component_combobox = QComboBox() - self.component_combobox.addItems(["Earthwork", "Concrete", "Steel", "Wood"]) + self.component_combobox.addItems(self.data.keys()) + self.component_combobox.currentTextChanged.connect(self.update_comp_material) self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) - # Add a remove button for the component self.remove_component_button = QPushButton("x") self.remove_component_button.setFixedSize(24, 24) self.remove_component_button.setStyleSheet(""" @@ -55,12 +77,10 @@ def init_ui(self): self.component_first_scroll_content_layout.addLayout(component_header_layout) - # --- Material Details Grid Layout --- self.material_grid_layout = QGridLayout() self.material_grid_layout.setHorizontalSpacing(10) self.material_grid_layout.setVerticalSpacing(5) - # Header Row headers = ["Type of Material", "Grade", "Quantity", "Unit", "Rate", "Rate Data Source"] for col, header_text in enumerate(headers): label = QLabel(header_text) @@ -68,71 +88,114 @@ def init_ui(self): label.setObjectName("MaterialGridLabel") self.material_grid_layout.addWidget(label, 0, col) - # Column stretch factors are removed as all input widgets will now have fixed widths. - # This allows the grid to size columns based on the fixed widget sizes and spacing. - self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) - # Add initial two material rows self.add_material_row() self.add_material_row() - # --- Add Material Button --- + self.update_comp_material(self.component_combobox.currentText()) + self.add_material_button = QPushButton("+ Add Material") self.add_material_button.setObjectName("add_material_button") self.add_material_button.clicked.connect(self.add_material_row) self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + def update_comp_material(self, selected_component): + """Update all material combos when component changes""" + materials = list(self.data.get(selected_component, {}).keys()) + for i in range(len(self.material_rows)): + material_combo = self.material_rows[i][KEY_TYPE] + grade_combo = self.material_rows[i][KEY_GRADE] + + # Clear and repopulate materials + material_combo.clear() + material_combo.addItems(materials) + + # Update grades for the first material + if materials: + self.update_comp_grades(material_combo.currentText(), grade_combo) + + def update_comp_grades(self, selected_material, widget): + """Update grades based on selected material""" + selected_component = self.component_combobox.currentText() + grades = self.data.get(selected_component, {}).get(selected_material, {}).get(KEY_GRADE, []) + widget.clear() + widget.addItems(grades) + + def update_comp_units(self, selected_material, widget): + selected_component = self.component_combobox.currentText() + units = self.data.get(selected_component,{}).get(selected_material,{}).get(KEY_UNITS,[]) + widget.clear() + widget.addItems(units) def add_material_row(self): + validator = QDoubleValidator() + validator.setRange(0.0, 999999.99, 2) + validator.setBottom(0.0) + validator.setNotation(QDoubleValidator.Notation.StandardNotation) + row_widgets = {} row_idx = self.current_material_row_idx - - # Set fixed width for all input widgets to 80px, as requested. - # Note: This might make wider text truncate or appear cramped depending on content. fixed_input_width = 80 + # Get current component and materials + selected_component = self.component_combobox.currentText() + materials = list(self.data.get(selected_component, {}).keys()) + + # Type of Material ComboBox type_material_combo = QComboBox() - type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) + type_material_combo.addItems(materials) type_material_combo.setObjectName("MaterialGridInput") type_material_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) - row_widgets['type'] = type_material_combo + row_widgets[KEY_TYPE] = type_material_combo + # Grade ComboBox grade_combo = QComboBox() - grade_combo.addItems(["A", "B", "C", "X", "Y", "Z", "N/A"]) grade_combo.setObjectName("MaterialGridInput") + type_material_combo.currentTextChanged.connect( + lambda text, widget=grade_combo: self.update_comp_grades(text, widget) + ) grade_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) - row_widgets['grade'] = grade_combo + row_widgets[KEY_GRADE] = grade_combo + # Quantity quantity_edit = QLineEdit() + quantity_edit.setValidator(validator) quantity_edit.setPlaceholderText("0") quantity_edit.setObjectName("MaterialGridInput") quantity_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) - row_widgets['quantity'] = quantity_edit + row_widgets[KEY_QUANTITY] = quantity_edit + # Unit unit_combo_m3 = QComboBox() - unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) + type_material_combo.currentTextChanged.connect( + lambda text, widget=unit_combo_m3: self.update_comp_units(text, widget) + ) unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others - self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout - row_widgets['unit_m3'] = unit_combo_m3 + unit_combo_m3.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) + row_widgets[KEY_UNIT_M3] = unit_combo_m3 + # Rate rate_edit = QLineEdit() + rate_edit.setValidator(validator) rate_edit.setPlaceholderText("0.00") rate_edit.setObjectName("MaterialGridInput") rate_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) - row_widgets['rate'] = rate_edit + row_widgets[KEY_RATE] = rate_edit + # Rate Data Source rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") rate_data_source_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) - row_widgets['rate_data_source'] = rate_data_source_edit + row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit + # Remove button remove_button = QPushButton("x") remove_button.setFixedSize(24, 24) remove_button.setStyleSheet(""" @@ -159,14 +222,15 @@ def add_material_row(self): self.material_rows.append(row_widgets) self.current_material_row_idx += 1 - self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget - self.adjustSize() # Adjust the size of the component widget - + self.updateGeometry() + self.adjustSize() def remove_material_row_by_widgets(self, row_widgets_to_remove): + """Remove a material row from the grid""" if row_widgets_to_remove not in self.material_rows: return + # Find the row index in the grid row_idx_in_grid = -1 for i, row_dict in enumerate(self.material_rows): if row_dict == row_widgets_to_remove: @@ -176,6 +240,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): if row_idx_in_grid == -1: return + # Remove all widgets in the row for col in range(self.material_grid_layout.columnCount()): item = self.material_grid_layout.itemAtPosition(row_idx_in_grid, col) if item: @@ -191,9 +256,11 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): sub_item.widget().deleteLater() self.material_grid_layout.removeItem(layout) + # Remove from tracking list self.material_rows.remove(row_widgets_to_remove) self.current_material_row_idx -= 1 + # Shift remaining rows up for r_idx in range(row_idx_in_grid, self.current_material_row_idx + 1): for c_idx in range(self.material_grid_layout.columnCount()): item = self.material_grid_layout.itemAtPosition(r_idx + 1, c_idx) @@ -207,17 +274,22 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.material_grid_layout.removeItem(layout) self.material_grid_layout.addLayout(layout, r_idx, c_idx) - self.updateGeometry() # Call updateGeometry on self - self.update() # Call update on self + self.updateGeometry() + self.update() self.material_grid_layout.invalidate() - self.adjustSize() # Adjust the size of the component widget + self.adjustSize() + class AuxiliaryWorks(QWidget): closed = Signal() - def __init__(self, parent=None): - super().__init__(parent) + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): + super().__init__() + self.parent = parent + self.database_manager = database self.setObjectName("central_panel_widget") - self.component_widgets = [] # To store references to each ComponentWidget instance + self.component_widgets = [] self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -234,7 +306,6 @@ def __init__(self, parent=None): } QScrollArea { - background-color: transparent; outline: none; } @@ -316,38 +387,35 @@ def __init__(self, parent=None): border-color: #606060; } - /* Styling for component_first_widget (the container for the nested scroll area) */ #component_first_widget { background-color: transparent; - margin-top: 10px; /* Add some space above each component block */ + margin-top: 10px; } - #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + #component_first_scroll_content_widget { background-color: #FFFFFF; padding: 10px; - border-radius: 8px; } - /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ QPushButton#nav_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; - min-width: 80px; /* Ensure a minimum width */ + min-width: 80px; } QPushButton#nav_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#nav_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; } - /* Styling for QComboBox */ + QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; @@ -376,7 +444,6 @@ def __init__(self, parent=None): background-color: #FDEFEF; } - /* Styling for material grid elements */ #MaterialGridLabel { font-weight: bold; color: #3F3E5E; @@ -393,24 +460,25 @@ def __init__(self, parent=None): border: 1px solid #DDDCE0; background-color: #FFFFFF; } - /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ + QPushButton#add_material_button, QPushButton#add_component_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; } QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; } """) + left_panel_vlayout = QVBoxLayout(self) left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) @@ -424,55 +492,48 @@ def __init__(self, parent=None): self.scroll_area.setWidget(scroll_content_widget) self.scroll_content_layout = QVBoxLayout(scroll_content_widget) - self.scroll_content_layout.setContentsMargins(0,0,0,0) + self.scroll_content_layout.setContentsMargins(0, 0, 0, 0) self.scroll_content_layout.setSpacing(0) - # Create the Add Component button and connect it self.add_component_button = QPushButton("+ Add Component") self.add_component_button.setObjectName("add_component_button") self.add_component_button.clicked.connect(self.add_component_layout) - # Create the navigation buttons layout self.button_h_layout = QHBoxLayout() self.button_h_layout.setSpacing(10) - self.button_h_layout.setContentsMargins(10,10,10,10) - - # Adjust these stretch factors to control the position - self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + self.button_h_layout.setContentsMargins(10, 10, 10, 10) + self.button_h_layout.addStretch(6) back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_AUXILIARY)) self.button_h_layout.addWidget(back_button) next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(lambda: self.next.emit(KEY_AUXILIARY)) + next_button.clicked.connect(self.save_data) self.button_h_layout.addWidget(next_button) - # Add the initial component layout self.add_component_layout() - # Add initial spacing before the navigation buttons self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) def add_component_layout(self): new_component = ComponentWidget(self) self.component_widgets.append(new_component) - new_component.remove_component_button.clicked.connect(lambda: self.remove_component_layout(new_component)) + new_component.remove_component_button.clicked.connect( + lambda: self.remove_component_layout(new_component) + ) - # Temporarily remove button_h_layout and add_component_button for insertion if self.scroll_content_layout.indexOf(self.add_component_button) != -1: self.scroll_content_layout.removeWidget(self.add_component_button) if self.scroll_content_layout.indexOf(self.button_h_layout) != -1: self.scroll_content_layout.removeItem(self.button_h_layout) - # Insert the new component self.scroll_content_layout.addWidget(new_component) - - # Re-add the 'Add Component' button self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) - - # Re-add the navigation buttons layout self.scroll_content_layout.addLayout(self.button_h_layout) self.scroll_area.widget().updateGeometry() @@ -485,37 +546,27 @@ def remove_component_layout(self, component_to_remove): component_to_remove.deleteLater() self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + + def collect_data(self): + all_data = [] + for component_widget in self.component_widgets: + component_data = component_widget.collect_data() + all_data.append(component_data) + return all_data + + def save_data(self): + data = self.collect_data() + print("\nCollected Data from UI:",data) + + self.database_manager.input_data_row(KEY_SUPERSTRUCTURE, data) + # calculating total initial cost + total_init_cost = self.database_manager.calculate_total_initial_cost() + # Update Results Dict + self.parent.results[COST_TOTAL_INIT_CONST] = float(total_init_cost) def expand_scroll_area(self): self.central_widget.layout().invalidate() def close_widget(self): self.closed.emit() - self.setParent(None) - -#----------------Standalone-Test-Code-------------------------------- - -# class MyMainWindow(QMainWindow): -# def __init__(self): -# super().__init__() - -# self.setStyleSheet("border: none") - -# self.central_widget = QWidget() -# self.central_widget.setObjectName("central_widget") -# self.setCentralWidget(self.central_widget) - -# self.main_h_layout = QHBoxLayout(self.central_widget) -# self.main_h_layout.addStretch(1) - -# self.main_h_layout.addWidget(AuxiliaryWorks(), 2) - -# self.setWindowState(Qt.WindowMaximized) - - -# if __name__ == "__main__": -# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) -# app = QApplication(sys.argv) -# window = MyMainWindow() -# window.show() -# sys.exit(app.exec()) \ No newline at end of file + self.setParent(None) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py index 4bf5e24..3b47caf 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py @@ -2,12 +2,14 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from PySide6.QtGui import QDoubleValidator +from ..utils.data import * import sys class ComponentWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) - + self.data = construction_materials.get(KEY_FOUNDATION) self.material_rows = [] # To store references to widgets in each material row self.current_material_row_idx = 1 # Start index for material rows (0 is header) @@ -25,7 +27,8 @@ def init_ui(self): component_header_layout.addWidget(component_label) self.component_combobox = QComboBox() - self.component_combobox.addItems(["Earthwork", "Concrete", "Steel", "Wood"]) + self.component_combobox.currentTextChanged.connect(self.update_comp_material) + self.component_combobox.addItems(self.data.keys()) self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) @@ -77,14 +80,60 @@ def init_ui(self): self.add_material_row() self.add_material_row() + self.update_comp_material(self.component_combobox.currentText()) + # --- Add Material Button --- self.add_material_button = QPushButton("+ Add Material") self.add_material_button.setObjectName("add_material_button") self.add_material_button.clicked.connect(self.add_material_row) self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + def collect_data(self): + rows_data = [] + for row in self.material_rows: + component = self.component_combobox.currentText() + material_type = row[KEY_TYPE].currentText() + material_grade = row[KEY_GRADE].currentText() + quantity = row[KEY_QUANTITY].text() + unit_m3 = row[KEY_UNIT_M3].currentText() + rate = row[KEY_RATE].text() + rate_data_source = row[KEY_RATE_DATA_SOURCE].text() + row_dict = { KEY_COMPONENT: component, + KEY_TYPE: material_type, + KEY_GRADE: material_grade, + KEY_QUANTITY: quantity if quantity.strip() else "0", + KEY_UNIT_M3: unit_m3, + KEY_RATE: rate if rate.strip() else "0.00", + KEY_RATE_DATA_SOURCE: rate_data_source + } + rows_data.append(row_dict) + return rows_data + + def update_comp_material(self, selected_component): + materials = self.data.get(selected_component).keys() + for i in range(len(self.material_rows)): + material_combo = self.material_rows[i][KEY_TYPE] + material_combo.clear() + material_combo.addItems(materials) + + def update_comp_grades(self, selected_material, widget): + selected_component = self.component_combobox.currentText() + grades = self.data.get(selected_component,{}).get(selected_material,{}).get(KEY_GRADE,[]) + widget.clear() + widget.addItems(grades) + + def update_comp_units(self, selected_material, widget): + selected_component = self.component_combobox.currentText() + units = self.data.get(selected_component,{}).get(selected_material,{}).get(KEY_UNITS,[]) + widget.clear() + widget.addItems(units) def add_material_row(self): + validator = QDoubleValidator() + validator.setRange(0.0, 999999.99, 2) + validator.setBottom(0.0) + validator.setNotation(QDoubleValidator.Notation.StandardNotation) + row_widgets = {} row_idx = self.current_material_row_idx @@ -93,45 +142,50 @@ def add_material_row(self): fixed_input_width = 80 type_material_combo = QComboBox() - type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) type_material_combo.setObjectName("MaterialGridInput") type_material_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) - row_widgets['type'] = type_material_combo + row_widgets[KEY_TYPE] = type_material_combo grade_combo = QComboBox() - grade_combo.addItems(["A", "B", "C", "X", "Y", "Z", "N/A"]) grade_combo.setObjectName("MaterialGridInput") + type_material_combo.currentTextChanged.connect( + lambda text, widget=grade_combo: self.update_comp_grades(text, widget) + ) grade_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) - row_widgets['grade'] = grade_combo + row_widgets[KEY_GRADE] = grade_combo quantity_edit = QLineEdit() + quantity_edit.setValidator(validator) quantity_edit.setPlaceholderText("0") quantity_edit.setObjectName("MaterialGridInput") quantity_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) - row_widgets['quantity'] = quantity_edit + row_widgets[KEY_QUANTITY] = quantity_edit unit_combo_m3 = QComboBox() - unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) unit_combo_m3.setObjectName("MaterialGridInput") + type_material_combo.currentTextChanged.connect( + lambda text, widget=unit_combo_m3: self.update_comp_units(text, widget) + ) unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout - row_widgets['unit_m3'] = unit_combo_m3 + row_widgets[KEY_UNIT_M3] = unit_combo_m3 rate_edit = QLineEdit() + rate_edit.setValidator(validator) rate_edit.setPlaceholderText("0.00") rate_edit.setObjectName("MaterialGridInput") rate_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) - row_widgets['rate'] = rate_edit + row_widgets[KEY_RATE] = rate_edit rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") rate_data_source_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) - row_widgets['rate_data_source'] = rate_data_source_edit + row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit remove_button = QPushButton("x") remove_button.setFixedSize(24, 24) @@ -214,8 +268,11 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): class Foundation(QWidget): closed = Signal() - def __init__(self, parent=None): - super().__init__(parent) + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): + super().__init__() + self.database_manager = database self.setObjectName("central_panel_widget") self.component_widgets = [] # To store references to each ComponentWidget instance self.setStyleSheet(""" @@ -440,12 +497,10 @@ def __init__(self, parent=None): # Adjust these stretch factors to control the position self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right - back_button = QPushButton("Back") - back_button.setObjectName("nav_button") - self.button_h_layout.addWidget(back_button) - next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(self.save_data) + next_button.clicked.connect(lambda: self.next.emit(KEY_FOUNDATION)) self.button_h_layout.addWidget(next_button) # Add the initial component layout @@ -455,6 +510,18 @@ def __init__(self, parent=None): self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) + def collect_data(self): + all_data = [] + for component_widget in self.component_widgets: + component_data = component_widget.collect_data() + all_data.append(component_data) + return all_data + + def save_data(self): + data = self.collect_data() + print("\nCollected Data from UI:",data) + self.database_manager.input_data_row(KEY_FOUNDATION, data) + def add_component_layout(self): new_component = ComponentWidget(self) self.component_widgets.append(new_component) diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py index 0e5b9e6..5d26cbc 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py @@ -2,34 +2,58 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from PySide6.QtGui import QDoubleValidator +from ..utils.data import * import sys class ComponentWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) + self.data = construction_materials.get(KEY_SUBSTRUCTURE) - self.material_rows = [] # To store references to widgets in each material row - self.current_material_row_idx = 1 # Start index for material rows (0 is header) + self.material_rows = [] + self.current_material_row_idx = 1 self.init_ui() + + def collect_data(self): + rows_data = [] + for row in self.material_rows: + component = self.component_combobox.currentText() + material_type = row[KEY_TYPE].currentText() + material_grade = row[KEY_GRADE].currentText() + quantity = row[KEY_QUANTITY].text() + unit_m3 = row[KEY_UNIT_M3].currentText() + rate = row[KEY_RATE].text() + rate_data_source = row[KEY_RATE_DATA_SOURCE].text() + row_dict = { KEY_COMPONENT: component, + KEY_TYPE: material_type, + KEY_GRADE: material_grade, + KEY_QUANTITY: quantity if quantity.strip() else "0", + KEY_UNIT_M3: unit_m3, + KEY_RATE: rate if rate.strip() else "0.00", + KEY_RATE_DATA_SOURCE: rate_data_source + } + rows_data.append(row_dict) + return rows_data + def init_ui(self): - self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout = QVBoxLayout(self) self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) self.component_first_scroll_content_layout.setSpacing(10) - # Component Label and Dropdown with a remove button component_header_layout = QHBoxLayout() component_label = QLabel("Component:") component_label.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(component_label) self.component_combobox = QComboBox() - self.component_combobox.addItems(["Earthwork", "Concrete", "Steel", "Wood"]) + self.component_combobox.addItems(self.data.keys()) + self.component_combobox.currentTextChanged.connect(self.update_comp_material) self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) - # Add a remove button for the component self.remove_component_button = QPushButton("x") self.remove_component_button.setFixedSize(24, 24) self.remove_component_button.setStyleSheet(""" @@ -54,12 +78,10 @@ def init_ui(self): self.component_first_scroll_content_layout.addLayout(component_header_layout) - # --- Material Details Grid Layout --- self.material_grid_layout = QGridLayout() self.material_grid_layout.setHorizontalSpacing(10) self.material_grid_layout.setVerticalSpacing(5) - # Header Row headers = ["Type of Material", "Grade", "Quantity", "Unit", "Rate", "Rate Data Source"] for col, header_text in enumerate(headers): label = QLabel(header_text) @@ -67,70 +89,93 @@ def init_ui(self): label.setObjectName("MaterialGridLabel") self.material_grid_layout.addWidget(label, 0, col) - # Column stretch factors are removed as all input widgets will now have fixed widths. - # This allows the grid to size columns based on the fixed widget sizes and spacing. - self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) - # Add initial two material rows self.add_material_row() self.add_material_row() - # --- Add Material Button --- + self.update_comp_material(self.component_combobox.currentText()) + self.add_material_button = QPushButton("+ Add Material") self.add_material_button.setObjectName("add_material_button") self.add_material_button.clicked.connect(self.add_material_row) self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) - + + def update_comp_material(self, selected_component): + materials = self.data.get(selected_component).keys() + for i in range(len(self.material_rows)): + material_combo = self.material_rows[i][KEY_TYPE] + material_combo.clear() + material_combo.addItems(materials) + + def update_comp_grades(self, selected_material, widget): + selected_component = self.component_combobox.currentText() + grades = self.data.get(selected_component,{}).get(selected_material,{}).get(KEY_GRADE,[]) + widget.clear() + widget.addItems(grades) + + def update_comp_units(self, selected_material, widget): + selected_component = self.component_combobox.currentText() + units = self.data.get(selected_component,{}).get(selected_material,{}).get(KEY_UNITS,[]) + widget.clear() + widget.addItems(units) def add_material_row(self): + validator = QDoubleValidator() + validator.setRange(0.0, 999999.99, 2) + validator.setBottom(0.0) + validator.setNotation(QDoubleValidator.Notation.StandardNotation) + row_widgets = {} row_idx = self.current_material_row_idx - # Set fixed width for all input widgets to 80px, as requested. - # Note: This might make wider text truncate or appear cramped depending on content. fixed_input_width = 80 type_material_combo = QComboBox() - type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) type_material_combo.setObjectName("MaterialGridInput") type_material_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) - row_widgets['type'] = type_material_combo + row_widgets[KEY_TYPE] = type_material_combo grade_combo = QComboBox() - grade_combo.addItems(["A", "B", "C", "X", "Y", "Z", "N/A"]) + type_material_combo.currentTextChanged.connect( + lambda text, widget=grade_combo: self.update_comp_grades(text, widget) + ) grade_combo.setObjectName("MaterialGridInput") grade_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) - row_widgets['grade'] = grade_combo + row_widgets[KEY_GRADE] = grade_combo quantity_edit = QLineEdit() + quantity_edit.setValidator(validator) quantity_edit.setPlaceholderText("0") quantity_edit.setObjectName("MaterialGridInput") quantity_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) - row_widgets['quantity'] = quantity_edit + row_widgets[KEY_QUANTITY] = quantity_edit unit_combo_m3 = QComboBox() - unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) + type_material_combo.currentTextChanged.connect( + lambda text, widget=unit_combo_m3: self.update_comp_units(text, widget) + ) unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others - self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout - row_widgets['unit_m3'] = unit_combo_m3 + unit_combo_m3.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) + row_widgets[KEY_UNIT_M3] = unit_combo_m3 rate_edit = QLineEdit() + rate_edit.setValidator(validator) rate_edit.setPlaceholderText("0.00") rate_edit.setObjectName("MaterialGridInput") rate_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) - row_widgets['rate'] = rate_edit + row_widgets[KEY_RATE] = rate_edit rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") rate_data_source_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) - row_widgets['rate_data_source'] = rate_data_source_edit + row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit remove_button = QPushButton("x") remove_button.setFixedSize(24, 24) @@ -149,7 +194,6 @@ def add_material_row(self): } QPushButton:pressed { background-color: #FF6666; - color: white; } """) remove_button.clicked.connect(lambda: self.remove_material_row_by_widgets(row_widgets)) @@ -158,8 +202,8 @@ def add_material_row(self): self.material_rows.append(row_widgets) self.current_material_row_idx += 1 - self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget - self.adjustSize() # Adjust the size of the component widget + self.updateGeometry() + self.adjustSize() def remove_material_row_by_widgets(self, row_widgets_to_remove): @@ -169,7 +213,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): row_idx_in_grid = -1 for i, row_dict in enumerate(self.material_rows): if row_dict == row_widgets_to_remove: - row_idx_in_grid = i + 1 # +1 because row 0 is header + row_idx_in_grid = i + 1 break if row_idx_in_grid == -1: @@ -206,18 +250,20 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.material_grid_layout.removeItem(layout) self.material_grid_layout.addLayout(layout, r_idx, c_idx) - self.updateGeometry() # Call updateGeometry on self - self.update() # Call update on self + self.updateGeometry() + self.update() self.material_grid_layout.invalidate() - self.adjustSize() # Adjust the size of the component widget + self.adjustSize() class SubStructure(QWidget): closed = Signal() - def __init__(self, parent=None): - super().__init__(parent) - self.parent = parent + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): + super().__init__() + self.database_manager = database self.setObjectName("central_panel_widget") - self.component_widgets = [] # To store references to each ComponentWidget instance + self.component_widgets = [] self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -316,38 +362,35 @@ def __init__(self, parent=None): border-color: #606060; } - /* Styling for component_first_widget (the container for the nested scroll area) */ #component_first_widget { background-color: transparent; - margin-top: 10px; /* Add some space above each component block */ + margin-top: 10px; } - #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + #component_first_scroll_content_widget { background-color: #FFFFFF; padding: 10px; border-radius: 8px; } - /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ QPushButton#nav_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; - min-width: 80px; /* Ensure a minimum width */ + min-width: 80px; } QPushButton#nav_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#nav_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; } - /* Styling for QComboBox */ QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; @@ -376,7 +419,6 @@ def __init__(self, parent=None): background-color: #FDEFEF; } - /* Styling for material grid elements */ #MaterialGridLabel { font-weight: bold; color: #3F3E5E; @@ -393,22 +435,21 @@ def __init__(self, parent=None): border: 1px solid #DDDCE0; background-color: #FFFFFF; } - /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ QPushButton#add_material_button, QPushButton#add_component_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; } QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; } """) left_panel_vlayout = QVBoxLayout(self) @@ -427,31 +468,29 @@ def __init__(self, parent=None): self.scroll_content_layout.setContentsMargins(0,0,0,0) self.scroll_content_layout.setSpacing(0) - # Create the Add Component button and connect it self.add_component_button = QPushButton("+ Add Component") self.add_component_button.setObjectName("add_component_button") self.add_component_button.clicked.connect(self.add_component_layout) - # Create the navigation buttons layout self.button_h_layout = QHBoxLayout() self.button_h_layout.setSpacing(10) self.button_h_layout.setContentsMargins(10,10,10,10) - # Adjust these stretch factors to control the position - self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + self.button_h_layout.addStretch(6) back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_SUBSTRUCTURE)) self.button_h_layout.addWidget(back_button) next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(lambda: self.next.emit(KEY_SUBSTRUCTURE)) + next_button.clicked.connect(self.save_data) self.button_h_layout.addWidget(next_button) - # Add the initial component layout self.add_component_layout() - # Add initial spacing before the navigation buttons self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) @@ -460,19 +499,15 @@ def add_component_layout(self): self.component_widgets.append(new_component) new_component.remove_component_button.clicked.connect(lambda: self.remove_component_layout(new_component)) - # Temporarily remove button_h_layout and add_component_button for insertion if self.scroll_content_layout.indexOf(self.add_component_button) != -1: self.scroll_content_layout.removeWidget(self.add_component_button) if self.scroll_content_layout.indexOf(self.button_h_layout) != -1: self.scroll_content_layout.removeItem(self.button_h_layout) - # Insert the new component self.scroll_content_layout.addWidget(new_component) - # Re-add the 'Add Component' button self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) - # Re-add the navigation buttons layout self.scroll_content_layout.addLayout(self.button_h_layout) self.scroll_area.widget().updateGeometry() @@ -486,36 +521,21 @@ def remove_component_layout(self, component_to_remove): self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + def collect_data(self): + all_data = [] + for component_widget in self.component_widgets: + component_data = component_widget.collect_data() + all_data.append(component_data) + return all_data + + def save_data(self): + data = self.collect_data() + print("\nCollected Data from UI:",data) + self.database_manager.input_data_row(KEY_SUBSTRUCTURE, data) + def expand_scroll_area(self): self.central_widget.layout().invalidate() def close_widget(self): self.closed.emit() - self.setParent(None) - -#----------------Standalone-Test-Code-------------------------------- - -# class MyMainWindow(QMainWindow): -# def __init__(self): -# super().__init__() - -# self.setStyleSheet("border: none") - -# self.central_widget = QWidget() -# self.central_widget.setObjectName("central_widget") -# self.setCentralWidget(self.central_widget) - -# self.main_h_layout = QHBoxLayout(self.central_widget) -# self.main_h_layout.addStretch(1) - -# self.main_h_layout.addWidget(SubStructure(), 2) - -# self.setWindowState(Qt.WindowMaximized) - - -# if __name__ == "__main__": -# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) -# app = QApplication(sys.argv) -# window = MyMainWindow() -# window.show() -# sys.exit(app.exec()) \ No newline at end of file + self.setParent(None) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py index 931721d..303bd9f 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py @@ -2,34 +2,57 @@ from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) from PySide6.QtGui import QIcon +from PySide6.QtGui import QDoubleValidator +from ..utils.data import * import sys class ComponentWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) - - self.material_rows = [] # To store references to widgets in each material row - self.current_material_row_idx = 1 # Start index for material rows (0 is header) + self.data = construction_materials.get(KEY_SUPERSTRUCTURE) + self.material_rows = [] + self.current_material_row_idx = 1 self.init_ui() + + def collect_data(self): + rows_data = [] + for row in self.material_rows: + component = self.component_combobox.currentText() + material_type = row[KEY_TYPE].currentText() + material_grade = row[KEY_GRADE].currentText() + quantity = row[KEY_QUANTITY].text() + unit_m3 = row[KEY_UNIT_M3].currentText() + rate = row[KEY_RATE].text() + rate_data_source = row[KEY_RATE_DATA_SOURCE].text() + row_dict = { KEY_COMPONENT: component, + KEY_TYPE: material_type, + KEY_GRADE: material_grade, + KEY_QUANTITY: quantity if quantity.strip() else "0", + KEY_UNIT_M3: unit_m3, + KEY_RATE: rate if rate.strip() else "0.00", + KEY_RATE_DATA_SOURCE: rate_data_source + } + rows_data.append(row_dict) + return rows_data + def init_ui(self): - self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout = QVBoxLayout(self) self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) self.component_first_scroll_content_layout.setSpacing(10) - # Component Label and Dropdown with a remove button component_header_layout = QHBoxLayout() component_label = QLabel("Component:") component_label.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(component_label) self.component_combobox = QComboBox() - self.component_combobox.addItems(["Earthwork", "Concrete", "Steel", "Wood"]) + self.component_combobox.currentTextChanged.connect(self.update_comp_material) + self.component_combobox.addItems(self.data.keys()) self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) - # Add a remove button for the component self.remove_component_button = QPushButton("x") self.remove_component_button.setFixedSize(24, 24) self.remove_component_button.setStyleSheet(""" @@ -55,12 +78,10 @@ def init_ui(self): self.component_first_scroll_content_layout.addLayout(component_header_layout) - # --- Material Details Grid Layout --- self.material_grid_layout = QGridLayout() self.material_grid_layout.setHorizontalSpacing(10) self.material_grid_layout.setVerticalSpacing(5) - # Header Row headers = ["Type of Material", "Grade", "Quantity", "Unit", "Rate", "Rate Data Source"] for col, header_text in enumerate(headers): label = QLabel(header_text) @@ -68,70 +89,93 @@ def init_ui(self): label.setObjectName("MaterialGridLabel") self.material_grid_layout.addWidget(label, 0, col) - # Column stretch factors are removed as all input widgets will now have fixed widths. - # This allows the grid to size columns based on the fixed widget sizes and spacing. - self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) - # Add initial two material rows self.add_material_row() self.add_material_row() - # --- Add Material Button --- + self.update_comp_material(self.component_combobox.currentText()) + self.add_material_button = QPushButton("+ Add Material") self.add_material_button.setObjectName("add_material_button") self.add_material_button.clicked.connect(self.add_material_row) self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) - + + def update_comp_material(self, selected_component): + materials = self.data.get(selected_component).keys() + for i in range(len(self.material_rows)): + material_combo = self.material_rows[i][KEY_TYPE] + material_combo.clear() + material_combo.addItems(materials) + + def update_comp_grades(self, selected_material, widget): + selected_component = self.component_combobox.currentText() + grades = self.data.get(selected_component,{}).get(selected_material,{}).get(KEY_GRADE,[]) + widget.clear() + widget.addItems(grades) + + def update_comp_units(self, selected_material, widget): + selected_component = self.component_combobox.currentText() + units = self.data.get(selected_component,{}).get(selected_material,{}).get(KEY_UNITS,[]) + widget.clear() + widget.addItems(units) def add_material_row(self): + validator = QDoubleValidator() + validator.setRange(0.0, 999999.99, 2) + validator.setBottom(0.0) + validator.setNotation(QDoubleValidator.Notation.StandardNotation) + row_widgets = {} row_idx = self.current_material_row_idx - # Set fixed width for all input widgets to 80px, as requested. - # Note: This might make wider text truncate or appear cramped depending on content. fixed_input_width = 80 type_material_combo = QComboBox() - type_material_combo.addItems(["Sand", "Gravel", "Cement", "Water", "Admixture", "Rebar", "Other"]) type_material_combo.setObjectName("MaterialGridInput") type_material_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) - row_widgets['type'] = type_material_combo + row_widgets[KEY_TYPE] = type_material_combo # CHANGE 7: Changed key to KEY_TYPE grade_combo = QComboBox() - grade_combo.addItems(["A", "B", "C", "X", "Y", "Z", "N/A"]) + type_material_combo.currentTextChanged.connect( + lambda text, widget=grade_combo: self.update_comp_grades(text, widget) + ) grade_combo.setObjectName("MaterialGridInput") grade_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) - row_widgets['grade'] = grade_combo + row_widgets[KEY_GRADE] = grade_combo quantity_edit = QLineEdit() + quantity_edit.setValidator(validator) quantity_edit.setPlaceholderText("0") quantity_edit.setObjectName("MaterialGridInput") quantity_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) - row_widgets['quantity'] = quantity_edit + row_widgets[KEY_QUANTITY] = quantity_edit unit_combo_m3 = QComboBox() - unit_combo_m3.addItems(["m³", "ft³", "kg", "ton", "litre"]) + type_material_combo.currentTextChanged.connect( + lambda text, widget=unit_combo_m3: self.update_comp_units(text, widget) + ) unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others - self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout - row_widgets['unit_m3'] = unit_combo_m3 + unit_combo_m3.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) + row_widgets[KEY_UNIT_M3] = unit_combo_m3 rate_edit = QLineEdit() + rate_edit.setValidator(validator) rate_edit.setPlaceholderText("0.00") rate_edit.setObjectName("MaterialGridInput") rate_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) - row_widgets['rate'] = rate_edit + row_widgets[KEY_RATE] = rate_edit rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") rate_data_source_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) - row_widgets['rate_data_source'] = rate_data_source_edit + row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit remove_button = QPushButton("x") remove_button.setFixedSize(24, 24) @@ -159,8 +203,8 @@ def add_material_row(self): self.material_rows.append(row_widgets) self.current_material_row_idx += 1 - self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget - self.adjustSize() # Adjust the size of the component widget + self.updateGeometry() + self.adjustSize() def remove_material_row_by_widgets(self, row_widgets_to_remove): @@ -170,7 +214,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): row_idx_in_grid = -1 for i, row_dict in enumerate(self.material_rows): if row_dict == row_widgets_to_remove: - row_idx_in_grid = i + 1 # +1 because row 0 is header + row_idx_in_grid = i + 1 break if row_idx_in_grid == -1: @@ -207,17 +251,20 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.material_grid_layout.removeItem(layout) self.material_grid_layout.addLayout(layout, r_idx, c_idx) - self.updateGeometry() # Call updateGeometry on self - self.update() # Call update on self + self.updateGeometry() + self.update() self.material_grid_layout.invalidate() - self.adjustSize() # Adjust the size of the component widget + self.adjustSize() class SuperStructure(QWidget): closed = Signal() - def __init__(self, parent=None): - super().__init__(parent) + next = Signal(str) + back = Signal(str) + def __init__(self, database, parent=None): + super().__init__() + self.database_manager = database self.setObjectName("central_panel_widget") - self.component_widgets = [] # To store references to each ComponentWidget instance + self.component_widgets = [] self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -316,38 +363,35 @@ def __init__(self, parent=None): border-color: #606060; } - /* Styling for component_first_widget (the container for the nested scroll area) */ #component_first_widget { background-color: transparent; - margin-top: 10px; /* Add some space above each component block */ + margin-top: 10px; } - #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + #component_first_scroll_content_widget { background-color: #FFFFFF; padding: 10px; border-radius: 8px; } - /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ QPushButton#nav_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; - min-width: 80px; /* Ensure a minimum width */ + min-width: 80px; } QPushButton#nav_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#nav_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; } - /* Styling for QComboBox */ QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; @@ -376,7 +420,6 @@ def __init__(self, parent=None): background-color: #FDEFEF; } - /* Styling for material grid elements */ #MaterialGridLabel { font-weight: bold; color: #3F3E5E; @@ -393,22 +436,21 @@ def __init__(self, parent=None): border: 1px solid #DDDCE0; background-color: #FFFFFF; } - /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ QPushButton#add_material_button, QPushButton#add_component_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; } QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; } """) left_panel_vlayout = QVBoxLayout(self) @@ -427,31 +469,29 @@ def __init__(self, parent=None): self.scroll_content_layout.setContentsMargins(0,0,0,0) self.scroll_content_layout.setSpacing(0) - # Create the Add Component button and connect it self.add_component_button = QPushButton("+ Add Component") self.add_component_button.setObjectName("add_component_button") self.add_component_button.clicked.connect(self.add_component_layout) - # Create the navigation buttons layout self.button_h_layout = QHBoxLayout() self.button_h_layout.setSpacing(10) self.button_h_layout.setContentsMargins(10,10,10,10) - # Adjust these stretch factors to control the position - self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + self.button_h_layout.addStretch(6) back_button = QPushButton("Back") back_button.setObjectName("nav_button") + back_button.clicked.connect(lambda: self.back.emit(KEY_SUPERSTRUCTURE)) self.button_h_layout.addWidget(back_button) next_button = QPushButton("Next") next_button.setObjectName("nav_button") + next_button.clicked.connect(lambda: self.next.emit(KEY_SUPERSTRUCTURE)) + next_button.clicked.connect(self.save_data) self.button_h_layout.addWidget(next_button) - # Add the initial component layout self.add_component_layout() - # Add initial spacing before the navigation buttons self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) @@ -460,19 +500,15 @@ def add_component_layout(self): self.component_widgets.append(new_component) new_component.remove_component_button.clicked.connect(lambda: self.remove_component_layout(new_component)) - # Temporarily remove button_h_layout and add_component_button for insertion if self.scroll_content_layout.indexOf(self.add_component_button) != -1: self.scroll_content_layout.removeWidget(self.add_component_button) if self.scroll_content_layout.indexOf(self.button_h_layout) != -1: self.scroll_content_layout.removeItem(self.button_h_layout) - # Insert the new component self.scroll_content_layout.addWidget(new_component) - # Re-add the 'Add Component' button self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) - # Re-add the navigation buttons layout self.scroll_content_layout.addLayout(self.button_h_layout) self.scroll_area.widget().updateGeometry() @@ -486,36 +522,21 @@ def remove_component_layout(self, component_to_remove): self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + def collect_data(self): + all_data = [] + for component_widget in self.component_widgets: + component_data = component_widget.collect_data() + all_data.append(component_data) + return all_data + + def save_data(self): + data = self.collect_data() + print("\nCollected Data from UI:",data) + self.database_manager.input_data_row(KEY_SUPERSTRUCTURE, data) + def expand_scroll_area(self): self.central_widget.layout().invalidate() def close_widget(self): self.closed.emit() - self.setParent(None) - -#----------------Standalone-Test-Code-------------------------------- - -# class MyMainWindow(QMainWindow): -# def __init__(self): -# super().__init__() - -# self.setStyleSheet("border: none") - -# self.central_widget = QWidget() -# self.central_widget.setObjectName("central_widget") -# self.setCentralWidget(self.central_widget) - -# self.main_h_layout = QHBoxLayout(self.central_widget) -# self.main_h_layout.addStretch(1) - -# self.main_h_layout.addWidget(SuperStructure(), 2) - -# self.setWindowState(Qt.WindowMaximized) - - -# if __name__ == "__main__": -# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) -# app = QApplication(sys.argv) -# window = MyMainWindow() -# window.show() -# sys.exit(app.exec()) \ No newline at end of file + self.setParent(None) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/utils/cost_component.py b/src/osbridgelcca/desktop_app/widgets/utils/cost_component.py new file mode 100644 index 0000000..48310b9 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/cost_component.py @@ -0,0 +1,702 @@ +# 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, present_worth_factor): +# """ +# 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 present_worth_factor: Discounting factor for future costs (present_worth_factor) +# """ +# self.amount = amount +# self.category = category +# self.is_initial = is_initial +# self.is_recurring = is_recurring +# self.present_worth_factor = present_worth_factor + +# @abstractmethod +# def calculate_cost(self): +# """Abstract method to be implemented by subclasses for cost calculation.""" +# pass + + +# class InitialConstructionCost(CostComponent): +# """Covers material, labor, and equipment costs for bridge construction.""" + +# 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 + + +# 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 + + +# 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 + + +# 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 + + +# 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 + + +# 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)) +# 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 + + +# 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)) +# 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 + + +# 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)) +# 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 + + +# 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)) +# cost = repair_cost_rate * construction_cost * pwf +# super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) + +# def calculate_cost(self): +# return self.amount + + +# 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 +# super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, present_worth_factor=pwf) + +# def calculate_cost(self): +# return self.amount + + +# 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 +# super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, present_worth_factor=pwf) + +# def calculate_cost(self): +# return self.amount +# # ------------------------------------------------------------------------------------------------------------ + +from abc import ABC, abstractmethod +import os +import sqlite3 +from typing import Optional, List, Dict, Any +from dataclasses import dataclass, field + +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): + """ + 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 present_worth_factor: Discounting factor for future costs (present_worth_factor) + """ + self.amount = amount + self.category = category + self.is_initial = is_initial + self.is_recurring = is_recurring + self.present_worth_factor = present_worth_factor + + @abstractmethod + def calculate_cost(self): + """Abstract method to be implemented by subclasses for cost calculation.""" + pass + + +class InitialConstructionCost(CostComponent): + """Covers material, labor, and equipment costs for bridge construction.""" + + 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 + + +class InitialCarbonEmissionCost(CostComponent): + """Calculates initial carbon emissions from material production and transport.""" + + def __init__(self, material_quantity, carbon_emission_factor, carbon_cost): + amount = (material_quantity * carbon_emission_factor) * carbon_cost + super().__init__(amount=amount, 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 + +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 + + +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 + + +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 + + +class PeriodicMaintenanceCost(CostComponent): + """Includes expenses for routine maintenance activities.""" + + def __init__(self, maintenance_cost_rate, construction_cost, discount_rate=0, period=1, design_life=1): + pwf = sum(1 / ((1 + discount_rate) ** (i * period)) for i in range(1, int(design_life / period))) + 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 + + +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))) + 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 + + +class RoutineInspectionCost(CostComponent): + """Annual cost of inspections for structural integrity.""" + + def __init__(self, inspection_cost_rate, construction_cost, discount_rate=0, design_life=1, period=1): + pwf = ((1 + discount_rate) ** (design_life - 1)) / (discount_rate * (1 + discount_rate) ** design_life) + cost = inspection_cost_rate * construction_cost * pwf + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) + self.inspection_cost_rate = inspection_cost_rate + self.construction_cost = construction_cost + self.period = period + self.discount_rate = discount_rate + self.design_life = design_life + + def calculate_cost(self): + return self.inspection_cost_rate * self.construction_cost * self.present_worth_factor + + +class RepairAndRehabilitationCost(CostComponent): + """Covers major structural repairs and retrofitting.""" + + def __init__(self, repair_cost_rate, construction_cost=0, discount_rate=0, period=1, design_life=1): + pwf = 1 / ((1 + discount_rate) ** (period)) + cost = repair_cost_rate * construction_cost * pwf + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) + + def calculate_cost(self): + return self.amount + + +class DemolitionCost(CostComponent): + """Costs incurred at the end of bridge life for demolition and disposal.""" + + def __init__(self, demolition_rate, construction_cost=0, discount_rate=0, design_life=1): + 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) + self.demolition_rate = demolition_rate + self.construction_cost = construction_cost + self.discount_rate = discount_rate + self.design_life = design_life + + def calculate_cost(self): + return self.amount + + +class RecyclingCost(CostComponent): + """Accounts for material salvage and repurposing costs.""" + + def __init__(self, scrap_value, quantity=0, scrap_rate=1.0, discount_rate=0, design_life=1): + pwf = 1 / ((1 + discount_rate) ** design_life) + cost = scrap_value * quantity * scrap_rate * pwf + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, present_worth_factor=pwf) + self.scrap_value = scrap_value + self.quantity = quantity + self.scrap_rate = scrap_rate + self.discount_rate = discount_rate + self.design_life = design_life + + def calculate_cost(self): + return self.amount + + +class ReconstructionCost(CostComponent): + """Accounts for partial or complete reconstruction of the bridge due to structural failures or obsolescence.""" + + def __init__(self, demolition_cost, reconstruction_cost, reconstruction_carbon_cost, reconstruction_time_cost, reconstruction_roaduser_cost, reconstruction_rerouting_carbon_cost, design_life, discount_rate): + pwf = 1 / ((1 + discount_rate) ** design_life) + cost = (demolition_cost + reconstruction_cost + reconstruction_carbon_cost + reconstruction_time_cost + reconstruction_roaduser_cost + reconstruction_rerouting_carbon_cost) * pwf + super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, present_worth_factor=pwf) + + def calculate_cost(self): + return self.amount + + + + + +@dataclass +class UserInputs: + user_materials: List[Dict[str, Any]] = field(default_factory=lambda: [ + {"material": "concrete", "grade": "M40", "unit": "cum", "quantity": 214, "rate": 11994}, + {"material": "steel", "grade": "E 250(Fe 410W)A", "unit": "MT", "quantity": 27.99, "rate": 91565}, + {"material": "steel", "grade": "E 300(Fe 440)", "unit": "MT", "quantity": 5.69, "rate": 185100}, + ]) + road_user_inputs: Dict[str, Any] = field(default_factory=lambda: { + "Lane_Type": "Single Lane Roads", + "Roughness": 2000, + "RF": 5, + "reroute_distance": 1, + "Vehicles": [ + {"Vehicle_Type": "Small Cars", "Count": 1000}, + {"Vehicle_Type": "Big Cars", "Count": 2000}, + {"Vehicle_Type": "Two Wheelers", "Count": 4000}, + ] + }) + user_input_steel_quantity: float = 0.0 + user_input_steel_unit: str = "MT" + analysis_period: float = 50.0 + design_life: float = 50.0 + discount_rate: float = 0.0425 + construction_time: float = 0.75 + reroute_distance: float = 1.0 + interest_rate: float = 0.1 + investment_ratio: float = 0.5 + concrete_emission_factor: float = 0.084 + concrete_co2_emission_factor: float = 0.487032864540167 + steel_emission_factor: float = 2.6 + co2_emission_per_km: float = 0.1213 + carbon_cost: float = 6.3936 + maintenance_cost_rate: float = 0.0055 + maintenance_period: float = 5.0 + inspection_rate: float = 0.01 + inspection_period: float = 1.0 + repair_cost_rate: float = 0.10 + repair_period: float = 30.0 + demolition_rate: float = 0.10 + scrap_value: float = 50000.0 + scrap_rate: float = 0.98 + + + +#COST CALULATIONS + + + +if __name__ == "__main__": + + # === USER INPUTS (all overridable parameters grouped) === + inputs = UserInputs() + user_materials = inputs.user_materials + road_user_inputs = inputs.road_user_inputs + user_input_steel_quantity = inputs.user_input_steel_quantity + user_input_steel_unit = inputs.user_input_steel_unit + analysis_period = inputs.analysis_period + design_life = inputs.design_life + discount_rate = inputs.discount_rate + construction_time = inputs.construction_time + reroute_distance = inputs.reroute_distance + interest_rate = inputs.interest_rate + investment_ratio = inputs.investment_ratio + concrete_emission_factor = inputs.concrete_emission_factor + concrete_co2_emission_factor = inputs.concrete_co2_emission_factor + steel_emission_factor = inputs.steel_emission_factor + co2_emission_per_km = inputs.co2_emission_per_km + carbon_cost = inputs.carbon_cost + maintenance_cost_rate = inputs.maintenance_cost_rate + maintenance_period = inputs.maintenance_period + inspection_rate = inputs.inspection_rate + inspection_period = inputs.inspection_period + repair_cost_rate = inputs.repair_cost_rate + repair_period = inputs.repair_period + demolition_rate = inputs.demolition_rate + scrap_value = inputs.scrap_value + scrap_rate = inputs.scrap_rate + + # 1. Initial Construction Cost Calculation + total_initial_construction_cost = 0 + for item in user_materials: + component = InitialConstructionCost( + quantity=item["quantity"], + rate=item["rate"] + ) + total_initial_construction_cost += component.calculate_cost() + print("Total Initial Construction Cost:", total_initial_construction_cost) # INR + + # 2. Initial Carbon Emission Cost Calculation (Concrete + Steel) + total_concrete_kg = 0 + for item in user_materials: + if item["material"] == "concrete": + qty = item["quantity"] + unit = item["unit"].lower() + if unit == "cum": + qty = qty * 2549.25 # density: kg/cum + total_concrete_kg += qty + total_steel_kg = 0 + for item in user_materials: + if item["material"] == "steel": + qty = item["quantity"] + unit = item["unit"].upper() + if unit == "MT": + qty = qty * 1000 # 1 MT = 1000 kg + total_steel_kg += qty + total_carbon_emission_cost = ( + (total_concrete_kg * concrete_emission_factor) + (total_steel_kg * steel_emission_factor) + ) * carbon_cost + print("Total Initial Carbon Emission Cost:", total_carbon_emission_cost) # INR + + # 3. Time Cost Calculation + time_cost_component = TimeCost( + construction_cost=total_initial_construction_cost, + interest_rate=interest_rate, + time=construction_time, + investment_ratio=investment_ratio + ) + print("Time Cost:", time_cost_component.calculate_cost()) # INR + + # 4. Road User Cost Calculation + + total_vehicles_affected = sum(v["Count"] for v in road_user_inputs["Vehicles"]) + + # Use a relative path for the database + db_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'databases', 'IRC_Road_Costs.db') + db_path = os.path.abspath(db_path) + + total_road_user_cost = 0 + # construction_time and reroute_distance are now shared variables + with sqlite3.connect(db_path) as conn: + cursor = conn.cursor() + cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") + table_name = cursor.fetchone()[0] + for vehicle in road_user_inputs["Vehicles"]: + vehicle_type = vehicle["Vehicle_Type"] + count = vehicle["Count"] + lane_type = road_user_inputs["Lane_Type"] + roughness = road_user_inputs["Roughness"] + rf = road_user_inputs["RF"] + cursor.execute(f''' + SELECT Grand_Cost FROM {table_name} WHERE + Vehicle_Type = ? AND Lane_Type = ? AND Roughness = ? AND RF = ? + ''', (vehicle_type, lane_type, roughness, rf)) + result = cursor.fetchone() + if result: + grand_cost = result[0] + ct = construction_time * reroute_distance + road_user_cost_component = RoadUserCost( + vehicles_affected=count, + vehicle_operation_cost=grand_cost, + construction_time=ct + ) + total_road_user_cost += road_user_cost_component.calculate_cost() + else: + print(f"No Grand_Cost found for {vehicle_type}, {lane_type}, {roughness}, {rf}") + print("Total Road User Cost:", total_road_user_cost) # INR + + + # 5. Additional Carbon Emission Cost Calculation + vehicles_affected = sum(v["Count"] for v in road_user_inputs["Vehicles"]) + additional_carbon_inputs = { + "vehicles_affected": vehicles_affected, + "reroute_distance": reroute_distance, + } + additional_carbon_emission_component = AdditionalCarbonEmissionCost( + vehicles_affected=additional_carbon_inputs["vehicles_affected"], + reroute_distance=additional_carbon_inputs["reroute_distance"], + co2_emission_per_km=co2_emission_per_km, + carbon_cost=carbon_cost + ) + print("Additional Carbon Emission Cost:", additional_carbon_emission_component.calculate_cost()) # INR + + # 6. Periodic Maintenance Cost Calculation + period = maintenance_period + periodic_maintenance_component = PeriodicMaintenanceCost( + maintenance_cost_rate=maintenance_cost_rate, + construction_cost=total_initial_construction_cost, + discount_rate=discount_rate, + period=period, + design_life=design_life + ) + print("Periodic Maintenance Cost:", periodic_maintenance_component.calculate_cost()) # INR + + # 7. Periodic Maintenance Carbon Emission Cost Calculation (Concrete only) + maintenance_concrete_kg = 0 + for item in user_materials: + if item["material"].lower() == "concrete": + qty = item["quantity"] + unit = item["unit"].lower() + if unit == "cum": + qty = qty * 2549.25 # density: kg/cum + # If already in kg, use as is + maintenance_concrete_kg += qty + maintenance_concrete_emission_factor = concrete_co2_emission_factor + maintenance_carbon_cost = carbon_cost + maintenance_discount_rate = discount_rate + maintenance_period = period + maintenance_design_life = design_life + periodic_maintenance_concrete_carbon_component = PeriodicMaintenanceCarbonCost( + material_quantity=maintenance_concrete_kg, + carbon_emission_factor=maintenance_concrete_emission_factor, + carbon_cost=maintenance_carbon_cost, + discount_rate=maintenance_discount_rate, + period=maintenance_period, + design_life=maintenance_design_life + ) + print("Periodic Maintenance Carbon Emission Cost (Concrete only):", periodic_maintenance_concrete_carbon_component.calculate_cost()) # INR + + # 8. Annual Routine Inspection Cost Calculation + inspection_discount_rate = discount_rate + inspection_design_life = design_life + inspection_component = RoutineInspectionCost( + inspection_cost_rate=inspection_rate, # Use inspection_rate as inspection cost rate + construction_cost=total_initial_construction_cost, # Always use total_initial_construction_cost + discount_rate=inspection_discount_rate, + design_life=inspection_design_life, + period=inspection_period # always annual + ) + total_routine_inspection_cost = inspection_component.calculate_cost() + print("Total Routine Inspection Cost:", total_routine_inspection_cost) # INR + + # 9. Repair and Rehabilitation Cost Calculation + repair_period = repair_period + repair_component = RepairAndRehabilitationCost( + repair_cost_rate=repair_cost_rate, + construction_cost=total_initial_construction_cost, + discount_rate=discount_rate, + period=repair_period, + design_life=design_life + ) + print("Repair and Rehabilitation Cost:", repair_component.calculate_cost()) # INR + + # 10. Demolition and Disposal Cost Calculation + demolition_discount_rate = discount_rate + demolition_design_life = design_life + demolition_component = DemolitionCost( + demolition_rate=demolition_rate, + construction_cost=total_initial_construction_cost, + discount_rate=demolition_discount_rate, + design_life=demolition_design_life + ) + print("Demolition and Disposal Cost:", demolition_component.calculate_cost()) # INR + + # 11. Recycling Cost Calculation + recycling_design_life = design_life + user_input_steel_quantity = 0 # eg (user input, 15 MT or 15000 kg) + user_input_steel_unit = "MT" # eg (user input, can be 'MT' or 'kg') + if user_input_steel_unit.lower() == "kg": + user_input_steel_quantity_mt = user_input_steel_quantity / 1000 + else: + user_input_steel_quantity_mt = user_input_steel_quantity + recycling_component = RecyclingCost( + scrap_value=scrap_value, + quantity=user_input_steel_quantity_mt, + scrap_rate=scrap_rate, + discount_rate=discount_rate, + design_life=recycling_design_life + ) + print("Recycling Cost (user-input steel only):", recycling_component.calculate_cost()) # INR + + # 12. Reconstruction Cost Calculation + demolition_cost = demolition_component.calculate_cost() + reconstruction_cost = total_initial_construction_cost + reconstruction_carbon_cost = total_carbon_emission_cost + reconstruction_time_cost = time_cost_component.calculate_cost() + reconstruction_roaduser_cost = road_user_cost_component.calculate_cost() + reconstruction_rerouting_carbon_cost = additional_carbon_emission_component.calculate_cost() + reconstruction_design_life = design_life + reconstruction_result = 0 + if analysis_period > design_life: + reconstruction_component = ReconstructionCost( + demolition_cost=demolition_cost, + reconstruction_cost=reconstruction_cost, + reconstruction_carbon_cost=reconstruction_carbon_cost, + reconstruction_time_cost=reconstruction_time_cost, + reconstruction_roaduser_cost=reconstruction_roaduser_cost, + reconstruction_rerouting_carbon_cost=reconstruction_rerouting_carbon_cost, + design_life=reconstruction_design_life, + discount_rate=discount_rate + ) + reconstruction_result = reconstruction_component.calculate_cost() + else: + reconstruction_result = 0 + print("Reconstruction Cost:", reconstruction_result) # INR + + + + + + # --- To be connected to UI --- + results = { + "Total Initial Construction Cost": total_initial_construction_cost, + "Total Initial Carbon Emission Cost": total_carbon_emission_cost, + "Time Cost": time_cost_component.calculate_cost(), + "Total Road User Cost": total_road_user_cost, + "Additional Carbon Emission Cost": additional_carbon_emission_component.calculate_cost(), + "Periodic Maintenance Cost": periodic_maintenance_component.calculate_cost(), + "Periodic Maintenance Carbon Emission Cost": periodic_maintenance_concrete_carbon_component.calculate_cost(), + "Total Routine Inspection Cost": total_routine_inspection_cost, + "Repair and Rehabilitation Cost": repair_component.calculate_cost(), + "Demolition and Disposal Cost": demolition_component.calculate_cost(), + "Recycling Cost": recycling_component.calculate_cost(), + "Reconstruction Cost": reconstruction_result + } + + + + + + + + + + diff --git a/src/osbridgelcca/desktop_app/widgets/utils/data.py b/src/osbridgelcca/desktop_app/widgets/utils/data.py new file mode 100644 index 0000000..2cd282a --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/data.py @@ -0,0 +1,312 @@ +KEY_STRUCTURE_WORKS_DATA = "Structure Works Data" +KEY_FOUNDATION = "Foundation" +KEY_SUBSTRUCTURE = "Sub-Structure" +KEY_SUPERSTRUCTURE = "Super-Structure" +KEY_AUXILIARY = "Miscellaneous" +KEY_FINANCIAL = "Financial Data" +KEY_CARBON_EMISSION = "Carbon Emission Data" +KEY_CARBON_EMISSION_COST = "Carbon Emission Cost Data" +KEY_BRIDGE_TRAFFIC = "Bridge and Traffic Data" +KEY_MAINTAINANCE_REPAIR = "Maintenance and Repair" +KEY_DEMOLITION_RECYCLE = "Demolition and Recycling" + +KEY_GRADE = "grade" +KEY_TYPE = "type" +KEY_QUANTITY = "quantity" +KEY_UNIT_M3 = "unit_m3" +KEY_RATE = "rate" +KEY_RATE_DATA_SOURCE = "rate_data_source" +KEY_COMPONENT = "component" +KEY_UNITS = "units" +KEY_OPTIONS = "options" + + +KEY_LANES = "lanes" +KEY_ROADROUGHNESS = "road_roughness" +KEY_ROAD_RISE_AND_FALL = "road_rise_and_fall" +KEY_TYPE_OF_ROAD = "type_of_road" +KEY_ANNUAL_INCREASE = "annual_increase" + + +KEY_EMBODIED_CARBON_ENERGY = "embodied_carbon_energy" +KEY_CARBON_EMISSION_FACTOR = "carbon_emission_factor" + +# Result Dictionary +COST_TOTAL_INIT_CONST = "Total Initial Construction Cost" +COST_TOTAL_INIT_CARBON_EMISSION = "Total Initial Carbon Emission Cost" +COST_TIME = "Time Cost" +COST_TOTAL_ROAD_USER = "Total Road User Cost" +COST_ADDITIONAL_CARBON_EMISSION = "Additional Carbon Emission Cost" +COST_PERIODIC_MAINTAINANCE = "Periodic Maintenance Cost" +COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION = "Periodic Maintenance Carbon Emission Cost" +COST_TOTAL_ROUTINE_INSPECTION = "Total Routine Inspection Cost" +COST_REPAIR_REHAB = "Repair and Rehabilitation Cost" +COST_DEMOLITION_DISPOSAL = "Demolition and Disposal Cost" +COST_RECYCLING = "Recycling Cost" +COST_RECONSTRUCTION = "Reconstruction Cost" + +construction_materials = { + KEY_FOUNDATION: { + "Pile": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + }, + "Excavation": { + "Rock": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Soft Rock": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Medium Soil": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Clay": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Marshy Soil": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Soft Murrum": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Loam": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Stiff Clay": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Gravel": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Hard Laterite": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Marine Clay": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Other": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + }, + "Pile Cap": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + } + }, + + KEY_SUBSTRUCTURE: { + "Pier": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Paint": { + KEY_GRADE: ["Epoxy", "Oil Paint", "Primer"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + }, + "Pier Cap": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Paint": { + KEY_GRADE: ["Epoxy", "Oil Paint", "Primer"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Steel Anchor Rods": { + KEY_GRADE: ["E250", "E350"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + } + }, + + KEY_SUPERSTRUCTURE: { + "Girder": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Pre-stressed Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Tendons": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Structural Steel": { + KEY_GRADE: ["E250", "E350"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Shear Connectors": { + KEY_GRADE: ["E250", "E350"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Paint": { + KEY_GRADE: ["Epoxy", "Oil Paint", "Primer", "Anti-Corrosive Paint"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + }, + "Deck Slab": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + } + + }, + + KEY_AUXILIARY: { + "Bearings": { + "Structural Steel": { + KEY_GRADE: ["E250", "E350"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Rubber": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + }, + "Railing & Crash Barrier": { + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Structural Steel": { + KEY_GRADE: ["E250", "E350"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Paint": { + KEY_GRADE: ["Epoxy", "Oil Paint", "Primer", "Anti-Corrosive Paint"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + }, + "Drainage": { + "PVC": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Structural Steel": { + KEY_GRADE: ["E250", "E350"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "FRP": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + }, + "Asphalt & Utilities": { + "Asphalt": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Paint": { + KEY_GRADE: ["Epoxy", "Oil Paint", "Primer", "Anti-Corrosive Paint"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + }, + "Waterproofing": { + KEY_GRADE: [], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + } + +} + + + +bridge_traffic_data = { + KEY_BRIDGE_TRAFFIC: { + KEY_LANES: { + KEY_OPTIONS: [ + "2", + "4" + ], + + }, + + KEY_ROADROUGHNESS: { + KEY_OPTIONS: ["2000", "3000", "4000", "5000", "6000", "7000", "8000", "9000", "10000"], + + }, + + KEY_ROAD_RISE_AND_FALL: { + KEY_OPTIONS: ["0", "5", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55", "60", "65", "70", "75", "80", "85", "90", "95", "100"], + + }, + + KEY_TYPE_OF_ROAD: { + KEY_OPTIONS: [ + "Urban Road", + "Rural Road", + "Highway", + "Expressway" + ], + + }, + KEY_ANNUAL_INCREASE: { + KEY_OPTIONS: ["8", "9", "10", "12"], + + }, + } + +} \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/utils/database.py b/src/osbridgelcca/desktop_app/widgets/utils/database.py new file mode 100644 index 0000000..f5f3b70 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/database.py @@ -0,0 +1,754 @@ +import sqlite3 +from typing import List, Dict, Tuple +from .data import * +from .cost_component import ( InitialConstructionCost, TimeCost, RoadUserCost, + AdditionalCarbonEmissionCost, PeriodicMaintenanceCost, + RoutineInspectionCost, RepairAndRehabilitationCost, + DemolitionCost, RecyclingCost, ReconstructionCost, + PeriodicMaintenanceCarbonCost +) + +class DatabaseManager: + """Database manager for Structure Works Data""" + + def __init__(self, db_path: str = "widgets/utils/structure_works.db", recreate: bool = True): + """ + Initialize database connection and create tables if they don't exist + + Args: + db_path: Path to the database file + recreate: If True, delete existing database and create fresh. If False, use existing database. + """ + self.db_path = db_path + self.conn = None + self.create_database(recreate=recreate) + self.carbon_cost = 6.3936 + + def create_database(self, recreate: bool = True): + """ + Create database tables with proper schema + + Args: + recreate: If True, delete existing database and create fresh + """ + import os + + # Ensure directory exists + os.makedirs(os.path.dirname(self.db_path), exist_ok=True) + + # Delete existing database if recreate is True + if recreate and os.path.exists(self.db_path): + os.remove(self.db_path) + print(f"Deleted existing database: {self.db_path}") + + self.conn = sqlite3.connect(self.db_path) + cursor = self.conn.cursor() + + # Create struct_works_data table first with comp_id as PRIMARY KEY + cursor.execute(''' + CREATE TABLE IF NOT EXISTS struct_works_data ( + comp_id INTEGER PRIMARY KEY AUTOINCREMENT, + type TEXT NOT NULL CHECK(type IN ( + 'Foundation', + 'Sub-Structure', + 'Super-Structure', + 'Miscellaneous' + )), + component_type TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + + # Create component table with comp_id as FOREIGN KEY + cursor.execute(''' + CREATE TABLE IF NOT EXISTS component ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + comp_id INTEGER NOT NULL, + type_material TEXT NOT NULL, + grade TEXT NOT NULL, + quantity REAL NOT NULL DEFAULT 0, + unit TEXT NOT NULL, + rate REAL NOT NULL DEFAULT 0.0, + rate_data_source TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (comp_id) REFERENCES struct_works_data(comp_id) ON DELETE CASCADE + ) + ''') + + # Create financial_data table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS financial_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + real_discount_rate REAL NOT NULL, + interest_rate REAL NOT NULL, + investment_ratio REAL NOT NULL, + duration_of_study INTEGER NOT NULL, + time_of_project INTEGER NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + + ) + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS carbon_emission( + type_material TEXT NOT NULL, + grade TEXT NOT NULL, + quantity REAL NOT NULL DEFAULT 0, + unit TEXT NOT NULL, + emission_factor REAL NOT NULL, + embodied REAL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (type_material, grade, unit) + ) + ''') + + self.conn.commit() + + def insert_structure_work(self, work_type: str, component_type: str) -> int: + """ + Insert a new structure work entry + + Args: + work_type: Type of structure work (Foundation, Sub-Structure, etc.) + component_type: Type of component (e.g., 'Pile', 'Beam', etc.) + + Returns: + comp_id: The auto-generated component ID (PRIMARY KEY) + """ + cursor = self.conn.cursor() + + cursor.execute(''' + INSERT INTO struct_works_data (type, component_type) + VALUES (?, ?) + ''', (work_type, component_type)) + + comp_id = cursor.lastrowid + self.conn.commit() + return comp_id + + def insert_component(self, comp_id: int, type_material: str, grade: str, + quantity: float, unit: str, rate: float, + rate_data_source: str = None) -> int: + """ + Insert a new component (material row) + + Args: + comp_id: Foreign key referencing struct_works_data.comp_id + type_material: Type of material (e.g., 'Steel Re', 'Concrete') + grade: Material grade (e.g., 'Fe415', 'M25') + quantity: Quantity of material + unit: Unit of measurement + rate: Rate per unit + rate_data_source: Source of rate data (optional) + + Returns: + id: The ID of the newly created component row + """ + cursor = self.conn.cursor() + cursor.execute(''' + INSERT INTO component (comp_id, type_material, grade, quantity, unit, rate, rate_data_source) + VALUES (?, ?, ?, ?, ?, ?, ?) + ''', (comp_id, type_material, grade, quantity, unit, rate, rate_data_source)) + + component_id = cursor.lastrowid + self.conn.commit() + return component_id + + def input_data_row(self, work_type: str, rows_data: List[Dict]) -> int: + """ + Input complete data row with structure work and multiple components + + Args: + work_type: Type of structure work (Foundation, Sub-Structure, etc.) + rows_data: List of dictionaries containing component data + + Returns: + comp_id: The auto-generated component ID from struct_works_data + + Example: + rows_data = [ + { + KEY_COMPONENT: "Pile", + KEY_TYPE: "Steel Re", + KEY_GRADE: "Fe415", + KEY_QUANTITY: "100", + KEY_UNIT_M3: "cum", + KEY_RATE: "5000.00", + KEY_RATE_DATA_SOURCE: "Market Survey" + }, + ... + ] + """ + if not rows_data: + raise ValueError("rows_data cannot be empty") + + for row in rows_data: + # Get component type from first row + component_type = row[0].get(KEY_COMPONENT, "Unknown") + + # Create structure work entry - this generates comp_id + comp_id = self.insert_structure_work(work_type, component_type) + + # Insert all component rows with the generated comp_id + for row_dict in row: + type_material = row_dict.get(KEY_TYPE, "") + grade = row_dict.get(KEY_GRADE, "") + quantity = float(row_dict.get(KEY_QUANTITY, 0)) + unit = row_dict.get(KEY_UNIT_M3, "") + rate = float(row_dict.get(KEY_RATE, 0.0)) + rate_data_source = row_dict.get(KEY_RATE_DATA_SOURCE, "") + + self.insert_component( + comp_id=comp_id, + type_material=type_material, + grade=grade, + quantity=quantity, + unit=unit, + rate=rate, + rate_data_source=rate_data_source + ) + + def delete_structure_work(self, comp_id: int): + """Delete a structure work and all its components (CASCADE)""" + cursor = self.conn.cursor() + cursor.execute('DELETE FROM struct_works_data WHERE comp_id = ?', (comp_id,)) + self.conn.commit() + + def delete_component(self, component_id: int): + """Delete a specific component by its ID""" + cursor = self.conn.cursor() + cursor.execute('DELETE FROM component WHERE id = ?', (component_id,)) + self.conn.commit() + + def get_all_structure_works(self) -> List[Tuple]: + """Get summary of all structure works""" + cursor = self.conn.cursor() + cursor.execute(''' + SELECT sw.comp_id, sw.type, sw.component_type, COUNT(c.id) as component_count + FROM struct_works_data sw + LEFT JOIN component c ON sw.comp_id = c.comp_id + GROUP BY sw.comp_id + ORDER BY sw.type, sw.comp_id + ''') + return cursor.fetchall() + + def get_components_by_comp_id(self, comp_id: int) -> List[Tuple]: + """Get all component rows for a specific comp_id""" + cursor = self.conn.cursor() + cursor.execute(''' + SELECT id, comp_id, type_material, grade, quantity, unit, rate, rate_data_source + FROM component + WHERE comp_id = ? + ORDER BY id + ''', (comp_id,)) + return cursor.fetchall() + + def _insert_financial_data(self, data: List[float]) -> int: + """ + Insert a new row into the financial_data table + + Args: + data: List containing [real_discount_rate, interest_rate, investment_ratio, duration_of_study] + in the same sequence as the table columns + + Returns: + id: The ID of the newly created financial data row + + Raises: + ValueError: If the data list doesn't contain exactly 4 elements + """ + if len(data) != 5: + raise ValueError("Data list must contain exactly 4 elements: [real_discount_rate, interest_rate, investment_ratio, duration_of_study]") + + cursor = self.conn.cursor() + + + cursor.execute(''' + INSERT INTO financial_data ( + real_discount_rate, + interest_rate, + investment_ratio, + duration_of_study, + time_of_project + ) + VALUES (?, ?, ?, ?,?) + ''', data) + + financial_data_id = cursor.lastrowid + self.conn.commit() + return financial_data_id + + def get_all_materials_info(self) -> List[Dict]: + """ + Retrieve all unique material types, grades, and quantities from the component table + + Returns: + List of dictionaries containing material information with keys: + - type_material: The type of material + - grade: The grade of the material + - quantity: The quantity used + - unit: The unit of measurement + """ + cursor = self.conn.cursor() + cursor.execute(''' + SELECT DISTINCT type_material, grade, quantity, unit, rate + FROM component + ORDER BY type_material, grade + ''') + + results = [] + for row in cursor.fetchall(): + material_info = { + 'type_material': row[0], + 'grade': row[1], + 'quantity': row[2], + 'unit': row[3], + 'rate': row[4] + } + results.append(material_info) + + return results + + def get_unique_materials_and_grades(self) -> List[List[str]]: + """ + Retrieve all unique material and grade pairs from the component table + as a list of lists: [[type_material, grade], ...] + """ + + + cursor = self.conn.cursor() + cursor.execute(''' + SELECT DISTINCT type_material, grade, unit, SUM(quantity) as total_quantity + FROM component + GROUP BY type_material, grade, unit + ''') + + + output =[[row[0], row[1], row[2], row[3]] for row in cursor.fetchall()] + p = [] + for item in output: + s = item[0] + " (" + item[1] + ")" + p.append([s, item[2], item[3], item[0], item[1]]) + return p + + def insert_carbon_emission_data(self, data_list): + """ + Insert multiple records into CARBON_EMISSION table + + Args: + data_list: List of dictionaries containing carbon emission data + """ + try: + cursor = self.conn.cursor() + + # SQL insert statement + insert_query = ''' + INSERT INTO CARBON_EMISSION + (type_material, grade, quantity, unit, emission_factor, embodied) + VALUES (?, ?, ?, ?, ?, ?) + ''' + + # Prepare data for batch insert + records = [] + for data in data_list: + print(data) + record = ( + data.get(KEY_TYPE, ''), + data.get(KEY_GRADE, ''), + float(data.get(KEY_QUANTITY, 0)), + data.get(KEY_UNIT_M3, ''), + float(data.get(KEY_CARBON_EMISSION_FACTOR, 0)), + float(data.get(KEY_EMBODIED_CARBON_ENERGY, 0)) + + ) + records.append(record) + # Execute batch insert + cursor.executemany(insert_query, records) + self.conn.commit() + + print(f"Successfully inserted {len(records)} records") + return True + + except sqlite3.Error as e: + print(f"Database error: {e}") + self.conn.rollback() + return False + except Exception as e: + print(f"Error: {e}") + return False + + def get_carbon_emission_data(self) -> List[Dict]: + cursor = self.conn.cursor() + cursor.execute(''' + SELECT type_material, unit, quantity, emission_factor + FROM carbon_emission + ''') + + results = [] + for row in cursor.fetchall(): + material_info = { + KEY_TYPE: row[0], + KEY_UNIT_M3: row[1], + KEY_QUANTITY: row[2], + KEY_RATE: row[3], + KEY_CARBON_EMISSION_FACTOR: row[3] + } + results.append(material_info) + return results + + def close(self): + """Close database connection""" + if self.conn: + self.conn.close() + + #========================Calculations======================== + + # 1. Called on next from Auxiliary Cost Data.py + def calculate_total_initial_cost(self) -> float: + data = self.get_all_materials_info() + total_cost = 0.0 + for item in data: + component = InitialConstructionCost( + quantity=item["quantity"], + rate=item["rate"] + ) + total_cost += component.calculate_cost() + print("\nTotal Initial Construction Cost:", total_cost) + return total_cost + + # 3. Called on next from financial_data.py + def calculate_time_cost(self, data: List, total_init_construct_cost: float) -> float: + # Insert financial data into the database + self._insert_financial_data(data) + time_cost_component = TimeCost( + construction_cost=total_init_construct_cost, + interest_rate=float(data[1]), + time=float(data[4]), + investment_ratio=float(data[2]) + ) + print("\nTime Cost:", time_cost_component.calculate_cost()) + return time_cost_component.calculate_cost() + + # 2. Called on next from carbon_emission_cost_data.py + def carbon_emission_cost(self, carbon_cost: float) -> float: + # Updated Carbon cost + self.carbon_cost = carbon_cost + + unit_conversions = { + "cum": 2549.25, + "kg": 1.0, + "mt": 1000.0, + "rmt": 1.0, + "sqm": 1.0, + "ltr": 1.0 + } + + # Get carbon emission data from the database + data = self.get_carbon_emission_data() + + total_carbon_emission_cost= 0.0 + for item in data: + total_component = 0.0 + qty = float(item.get(KEY_QUANTITY)) + unit = item.get(KEY_UNIT_M3) + emission_factor = float(item.get(KEY_CARBON_EMISSION_FACTOR)) + # convert quantity to kg + qty = qty * unit_conversions.get(unit.lower()) + total_component += qty + carbon_emission_cost = (total_component * emission_factor) * carbon_cost; + total_carbon_emission_cost += carbon_emission_cost + print(f"Carbon Emission Cost for {item.get(KEY_TYPE)}:", total_carbon_emission_cost) + + print("\nTotal Carbon Emission Cost:", total_carbon_emission_cost) + return total_carbon_emission_cost + + IRC_ROAD_COSTS_DATA = { + # Format: (Vehicle_Type, Lane_Type, Roughness, RF): Grand_Cost + ("Car", "2", "Good", "Rolling"): 15.50, + ("Car", "2", "Good", "Hilly"): 18.20, + ("Car", "2", "Fair", "Rolling"): 17.80, + ("Car", "2", "Fair", "Hilly"): 20.90, + ("Car", "2", "Poor", "Rolling"): 22.40, + ("Car", "2", "Poor", "Hilly"): 26.10, + ("Car", "4", "Good", "Rolling"): 14.20, + ("Car", "4", "Good", "Hilly"): 16.80, + ("Car", "4", "Fair", "Rolling"): 16.50, + ("Car", "4", "Fair", "Hilly"): 19.40, + ("Car", "4", "Poor", "Rolling"): 21.10, + ("Car", "4", "Poor", "Hilly"): 24.70, + + ("Bus", "2", "Good", "Rolling"): 45.80, + ("Bus", "2", "Good", "Hilly"): 52.30, + ("Bus", "2", "Fair", "Rolling"): 48.90, + ("Bus", "2", "Fair", "Hilly"): 55.80, + ("Bus", "2", "Poor", "Rolling"): 56.20, + ("Bus", "2", "Poor", "Hilly"): 64.10, + ("Bus", "4", "Good", "Rolling"): 42.50, + ("Bus", "4", "Good", "Hilly"): 48.70, + ("Bus", "4", "Fair", "Rolling"): 45.60, + ("Bus", "4", "Fair", "Hilly"): 52.10, + ("Bus", "4", "Poor", "Rolling"): 53.80, + ("Bus", "4", "Poor", "Hilly"): 61.40, + + ("HCV", "2", "Good", "Rolling"): 78.90, + ("HCV", "2", "Good", "Hilly"): 89.20, + ("HCV", "2", "Fair", "Rolling"): 84.50, + ("HCV", "2", "Fair", "Hilly"): 95.60, + ("HCV", "2", "Poor", "Rolling"): 96.80, + ("HCV", "2", "Poor", "Hilly"): 109.50, + ("HCV", "4", "Good", "Rolling"): 75.40, + ("HCV", "4", "Good", "Hilly"): 85.30, + ("HCV", "4", "Fair", "Rolling"): 81.20, + ("HCV", "4", "Fair", "Hilly"): 91.80, + ("HCV", "4", "Poor", "Rolling"): 93.70, + ("HCV", "4", "Poor", "Hilly"): 106.00, + + ("MCV", "2", "Good", "Rolling"): 56.70, + ("MCV", "2", "Good", "Hilly"): 64.80, + ("MCV", "2", "Fair", "Rolling"): 61.20, + ("MCV", "2", "Fair", "Hilly"): 69.90, + ("MCV", "2", "Poor", "Rolling"): 70.50, + ("MCV", "2", "Poor", "Hilly"): 80.40, + ("MCV", "4", "Good", "Rolling"): 54.30, + ("MCV", "4", "Good", "Hilly"): 62.10, + ("MCV", "4", "Fair", "Rolling"): 58.80, + ("MCV", "4", "Fair", "Hilly"): 67.20, + ("MCV", "4", "Poor", "Rolling"): 68.20, + ("MCV", "4", "Poor", "Hilly"): 77.90, + + ("LCV", "2", "Good", "Rolling"): 38.40, + ("LCV", "2", "Good", "Hilly"): 44.10, + ("LCV", "2", "Fair", "Rolling"): 41.60, + ("LCV", "2", "Fair", "Hilly"): 47.70, + ("LCV", "2", "Poor", "Rolling"): 48.30, + ("LCV", "2", "Poor", "Hilly"): 55.40, + ("LCV", "4", "Good", "Rolling"): 36.80, + ("LCV", "4", "Good", "Hilly"): 42.30, + ("LCV", "4", "Fair", "Rolling"): 40.10, + ("LCV", "4", "Fair", "Hilly"): 46.00, + ("LCV", "4", "Poor", "Rolling"): 46.90, + ("LCV", "4", "Poor", "Hilly"): 53.80, + } + + # 4. Called on next from bridge_and_traffic_data.py + def calculate_irc_road_cost(self, data: List) -> float: + # fetch Construction time + cursor = self.conn.cursor() + cursor.execute(''' + SELECT time_of_project + FROM financial_data + ''') + construction_time = float(cursor.fetchone()[0]) + + total_road_user_cost = 0 + lanes = data[0] + roughness = data[2] + rf = data[3] + + key = ["Car", "Bus", "HCV", "MCV", "LCV"] + for vehicle in key: + count = int(data[6+key.index(vehicle)]) + key_vehicle = (vehicle, lanes, roughness, rf) + cost_per_km = self.IRC_ROAD_COSTS_DATA.get(key_vehicle, "") + if cost_per_km: + ct = construction_time * float(data[1]) + road_user_cost_component = RoadUserCost( + vehicles_affected=count, + vehicle_operation_cost=cost_per_km, + construction_time=ct + ) + total_road_user_cost += road_user_cost_component.calculate_cost() + else: + print(f"No Grand_Cost found for {vehicle}, {lanes}, {roughness}, {rf}") + + print("\nTotal Road User Cost:", total_road_user_cost) # INR + return total_road_user_cost + + # 5. Called on next from bridge_and_traffic_data.py + def additional_carbon_emission_cost(self, data: List) -> float: + total_vehicles_affected = 0 + for i in range(6,11): + total_vehicles_affected += float(data[i]) + + reroute_distance = float(data[1]) + + additional_carbon_emission_component = AdditionalCarbonEmissionCost( + vehicles_affected=total_vehicles_affected, + reroute_distance=reroute_distance, + co2_emission_per_km=0.1213, + carbon_cost=self.carbon_cost + ) + print("\nAdditional Carbon Emission Cost:", additional_carbon_emission_component.calculate_cost()) # INR + return additional_carbon_emission_component.calculate_cost() + + # 6. Called on next from maintainance_repair_data.py + def periodic_maintainance_cost(self, data: List, total_initial_cost) -> float: + maintenance_cost_rate = PeriodicMaintenanceCost( + maintenance_cost_rate=float(data[0]), + construction_cost=total_initial_cost, + discount_rate=0.0425, + period=float(data[3]), + design_life=50.0 + ) + print("\nPeriodic Maintenance Cost:", maintenance_cost_rate.calculate_cost()) # INR + + return maintenance_cost_rate.calculate_cost() + + # 7. Periodic Maintenance Carbon Emission Cost Calculation (Concrete only) + def periodic_maintainnce_carbon_emission_cost(self, data: List): + unit_conversions = { + "cum": 2549.25, + "kg": 1.0, + "mt": 1000.0, + "rmt": 1.0, + "sqm": 1.0, + "ltr": 1.0 + } + + # Get carbon emission data from the database + c_data = self.get_carbon_emission_data() + + maintenance_concrete_emission_factor = 0 + maintenance_concrete_kg = 0 + for item in c_data: + type = item.get(KEY_TYPE) + if type[-8:].lower() == "concrete": + qty = item.get(KEY_QUANTITY) + unit = item.get(KEY_UNIT_M3) + # convert quantity to kg + qty = qty * unit_conversions.get(unit.lower()) + maintenance_concrete_emission_factor = item.get(KEY_CARBON_EMISSION_FACTOR) + maintenance_concrete_kg += qty + + maintenance_carbon_cost = 6.3936 + maintenance_discount_rate = 0.0425 + maintenance_period = float(data[3]) + maintenance_design_life = 50.0 + periodic_maintenance_concrete_carbon_component = PeriodicMaintenanceCarbonCost( + material_quantity=maintenance_concrete_kg, + carbon_emission_factor=maintenance_concrete_emission_factor, + carbon_cost=maintenance_carbon_cost, + discount_rate=maintenance_discount_rate, + period=maintenance_period, + design_life=maintenance_design_life + ) + print("\nPeriodic Maintenance Carbon Emission Cost (Concrete only):", periodic_maintenance_concrete_carbon_component.calculate_cost()) # INR + return periodic_maintenance_concrete_carbon_component.calculate_cost() + + # 8. Annual Routine Inspection Cost Calculation + def routine_inspection_cost(self, total_initial_construction_cost: float) -> float: + + inspection_discount_rate = 0.0425 + inspection_design_life = 50.0 + + inspection_rate = 0.01 + inspection_period = 1.0 + + inspection_component = RoutineInspectionCost( + inspection_cost_rate=inspection_rate, # Use inspection_rate as inspection cost rate + construction_cost=total_initial_construction_cost, # Always use total_initial_construction_cost + discount_rate=inspection_discount_rate, + design_life=inspection_design_life, + period=inspection_period # always annual + ) + total_routine_inspection_cost = inspection_component.calculate_cost() + print("\nTotal Routine Inspection Cost:", total_routine_inspection_cost) # INR + return total_routine_inspection_cost + + # 9. Repair and Rehabilitation Cost Calculation + def repair_and_rehabilitation_cost(self, total_initial_construction_cost: float) -> float: + repair_period = 30.0 + repair_cost_rate = 0.10 + + discount_rate = 0.0425 + design_life = 50.0 + + repair_component = RepairAndRehabilitationCost( + repair_cost_rate=repair_cost_rate, + construction_cost=total_initial_construction_cost, + discount_rate=discount_rate, + period=repair_period, + design_life=design_life + ) + print("\nRepair and Rehabilitation Cost:", repair_component.calculate_cost()) # INR + return repair_component.calculate_cost() + + # 10. Demolition and Disposal Cost Calculation + def demolition_and_disposal_cost(self, data: List, total_initial_construction_cost: float) -> float: + demolition_discount_rate = 0.0425 + demolition_design_life = 50.0 + demolition_rate = float(float(data[0])/100) # e.g., 0.05 for 5% + + demolition_component = DemolitionCost( + demolition_rate=demolition_rate, + construction_cost=total_initial_construction_cost, + discount_rate=demolition_discount_rate, + design_life=demolition_design_life + ) + print("\nDemolition and Disposal Cost:", demolition_component.calculate_cost()) # INR + return demolition_component.calculate_cost() + + # 11. Recycling Cost Calculation + def recycling_cost(self, data: List) -> float: + scrap_value = float(data[1]) + recycling_design_life = 50.0 + scrap_rate = 0.98 + + discount_rate = 0.0425 + + user_input_steel_quantity_mt = 0.0 + + recycling_component = RecyclingCost( + scrap_value=scrap_value, + quantity=user_input_steel_quantity_mt, + scrap_rate=scrap_rate, + discount_rate=discount_rate, + design_life=recycling_design_life + ) + print("\nRecycling Cost (user-input steel only):", recycling_component.calculate_cost()) # INR + return recycling_component.calculate_cost() + + # 12. Reconstruction Cost Calculation + def reconstruction_cost(self, initial_construction_cost: float, + demolition_cost: float, + carbon_emission_cost: float, + time_cost: float, + roaduser_cost: float, + rerouting_carbon_cost: float) -> float: + + reconstruction_cost = initial_construction_cost + demolition_cost = demolition_cost + reconstruction_carbon_cost = carbon_emission_cost + reconstruction_time_cost = time_cost + reconstruction_roaduser_cost = roaduser_cost + reconstruction_rerouting_carbon_cost = rerouting_carbon_cost + reconstruction_design_life = 50.0 + + reconstruction_result = 0 + analysis_period = 75.0 + + design_life = 50.0 + discount_rate = 0.0425 + + if analysis_period > design_life: + reconstruction_component = ReconstructionCost( + demolition_cost=demolition_cost, + reconstruction_cost=reconstruction_cost, + reconstruction_carbon_cost=reconstruction_carbon_cost, + reconstruction_time_cost=reconstruction_time_cost, + reconstruction_roaduser_cost=reconstruction_roaduser_cost, + reconstruction_rerouting_carbon_cost=reconstruction_rerouting_carbon_cost, + design_life=reconstruction_design_life, + discount_rate=discount_rate + ) + reconstruction_result = reconstruction_component.calculate_cost() + else: + reconstruction_result = 0 + print("\nReconstruction Cost:", reconstruction_result) # INR + return reconstruction_result + + \ No newline at end of file From cca6f1485296acc1656735adda77faead79cb569 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Wed, 12 Nov 2025 10:25:55 +0530 Subject: [PATCH 16/22] Added save and change stated in structural modules --- .../auxiliary_works_widget.py | 72 ++++++++++++++--- .../structure_works_data/foundation_widget.py | 78 ++++++++++++++++--- .../sub_structure_widget.py | 76 +++++++++++++++--- .../super_structure_widget.py | 77 +++++++++++++++--- .../desktop_app/widgets/utils/database.py | 47 ++++++++--- 5 files changed, 296 insertions(+), 54 deletions(-) diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py index 71c5e55..1e10518 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py @@ -1,6 +1,6 @@ from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal -from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame, QMessageBox) from PySide6.QtGui import QIcon from PySide6.QtGui import QDoubleValidator from ..utils.data import * @@ -9,11 +9,13 @@ class ComponentWidget(QWidget): def __init__(self, parent): super().__init__(parent) - + self.parent_widget = parent + self._initializing = True self.data = construction_materials.get(KEY_AUXILIARY) self.material_rows = [] self.current_material_row_idx = 1 self.init_ui() + self._initializing = False def collect_data(self): rows_data = [] @@ -36,6 +38,17 @@ def collect_data(self): rows_data.append(row_dict) return rows_data + def _on_value_changed(self, *_args): + if self._initializing: + return + if self.parent_widget and hasattr(self.parent_widget, "mark_state_changed"): + self.parent_widget.mark_state_changed() + + def _on_type_material_changed(self, text, grade_widget, unit_widget): + self.update_comp_grades(text, grade_widget) + self.update_comp_units(text, unit_widget) + self._on_value_changed() + def init_ui(self): self.component_first_scroll_content_layout = QVBoxLayout(self) self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) @@ -49,6 +62,7 @@ def init_ui(self): self.component_combobox = QComboBox() self.component_combobox.addItems(self.data.keys()) self.component_combobox.currentTextChanged.connect(self.update_comp_material) + self.component_combobox.currentTextChanged.connect(self._on_value_changed) self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) @@ -106,6 +120,7 @@ def update_comp_material(self, selected_component): for i in range(len(self.material_rows)): material_combo = self.material_rows[i][KEY_TYPE] grade_combo = self.material_rows[i][KEY_GRADE] + unit_combo = self.material_rows[i][KEY_UNIT_M3] # Clear and repopulate materials material_combo.clear() @@ -114,6 +129,8 @@ def update_comp_material(self, selected_component): # Update grades for the first material if materials: self.update_comp_grades(material_combo.currentText(), grade_combo) + self.update_comp_units(material_combo.currentText(), unit_combo) + self._on_value_changed() def update_comp_grades(self, selected_material, widget): """Update grades based on selected material""" @@ -153,12 +170,10 @@ def add_material_row(self): # Grade ComboBox grade_combo = QComboBox() grade_combo.setObjectName("MaterialGridInput") - type_material_combo.currentTextChanged.connect( - lambda text, widget=grade_combo: self.update_comp_grades(text, widget) - ) grade_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) row_widgets[KEY_GRADE] = grade_combo + grade_combo.currentTextChanged.connect(self._on_value_changed) # Quantity quantity_edit = QLineEdit() @@ -168,16 +183,15 @@ def add_material_row(self): quantity_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) row_widgets[KEY_QUANTITY] = quantity_edit + quantity_edit.textChanged.connect(self._on_value_changed) # Unit unit_combo_m3 = QComboBox() - type_material_combo.currentTextChanged.connect( - lambda text, widget=unit_combo_m3: self.update_comp_units(text, widget) - ) unit_combo_m3.setObjectName("MaterialGridInput") unit_combo_m3.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) row_widgets[KEY_UNIT_M3] = unit_combo_m3 + unit_combo_m3.currentTextChanged.connect(self._on_value_changed) # Rate rate_edit = QLineEdit() @@ -187,6 +201,7 @@ def add_material_row(self): rate_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) row_widgets[KEY_RATE] = rate_edit + rate_edit.textChanged.connect(self._on_value_changed) # Rate Data Source rate_data_source_edit = QLineEdit() @@ -194,6 +209,7 @@ def add_material_row(self): rate_data_source_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit + rate_data_source_edit.textChanged.connect(self._on_value_changed) # Remove button remove_button = QPushButton("x") @@ -220,10 +236,16 @@ def add_material_row(self): self.material_grid_layout.addWidget(remove_button, row_idx, 6) row_widgets['remove_button'] = remove_button + type_material_combo.currentTextChanged.connect( + lambda text, grade_widget=grade_combo, unit_widget=unit_combo_m3: self._on_type_material_changed(text, grade_widget, unit_widget) + ) + self.material_rows.append(row_widgets) self.current_material_row_idx += 1 self.updateGeometry() self.adjustSize() + self._on_type_material_changed(type_material_combo.currentText(), grade_combo, unit_combo_m3) + self._on_value_changed() def remove_material_row_by_widgets(self, row_widgets_to_remove): """Remove a material row from the grid""" @@ -278,6 +300,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.update() self.material_grid_layout.invalidate() self.adjustSize() + self._on_value_changed() class AuxiliaryWorks(QWidget): @@ -290,6 +313,10 @@ def __init__(self, database, parent=None): self.database_manager = database self.setObjectName("central_panel_widget") self.component_widgets = [] + self._initializing = True + self.state_changed = False + # To store Id'd of current data + self.data_id = [] self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -511,14 +538,14 @@ def __init__(self, database, parent=None): next_button = QPushButton("Next") next_button.setObjectName("nav_button") - next_button.clicked.connect(lambda: self.next.emit(KEY_AUXILIARY)) - next_button.clicked.connect(self.save_data) + next_button.clicked.connect(self.on_next_clicked) self.button_h_layout.addWidget(next_button) self.add_component_layout() self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) + self._initializing = False def add_component_layout(self): new_component = ComponentWidget(self) @@ -538,6 +565,7 @@ def add_component_layout(self): self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + self.mark_state_changed() def remove_component_layout(self, component_to_remove): if component_to_remove in self.component_widgets: @@ -546,6 +574,7 @@ def remove_component_layout(self, component_to_remove): component_to_remove.deleteLater() self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + self.mark_state_changed() def collect_data(self): all_data = [] @@ -553,17 +582,38 @@ def collect_data(self): component_data = component_widget.collect_data() all_data.append(component_data) return all_data + + def mark_state_changed(self): + if self._initializing: + return + self.state_changed = True def save_data(self): data = self.collect_data() print("\nCollected Data from UI:",data) - self.database_manager.input_data_row(KEY_SUPERSTRUCTURE, data) + if self.data_id: + self.data_id = self.database_manager.replace_structure_work_rows(KEY_AUXILIARY, data, self.data_id) + else: + self.data_id = self.database_manager.input_data_row(KEY_AUXILIARY, data) # calculating total initial cost total_init_cost = self.database_manager.calculate_total_initial_cost() # Update Results Dict self.parent.results[COST_TOTAL_INIT_CONST] = float(total_init_cost) + self.state_changed = False + def on_next_clicked(self): + if not self.state_changed: + self.next.emit(KEY_AUXILIARY) + return + if self.data_id: + message = "Do you want to replace previous data?" + else: + message = "Do you want to save data?" + reply = QMessageBox.question(self, "Confirm", message, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) + if reply == QMessageBox.StandardButton.Yes: + self.save_data() + self.next.emit(KEY_AUXILIARY) def expand_scroll_area(self): self.central_widget.layout().invalidate() diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py index 3b47caf..6a7202f 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py @@ -1,6 +1,6 @@ from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal -from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame, QMessageBox) from PySide6.QtGui import QIcon from PySide6.QtGui import QDoubleValidator from ..utils.data import * @@ -9,11 +9,14 @@ class ComponentWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) + self.parent_widget = parent + self._initializing = True self.data = construction_materials.get(KEY_FOUNDATION) self.material_rows = [] # To store references to widgets in each material row self.current_material_row_idx = 1 # Start index for material rows (0 is header) self.init_ui() + self._initializing = False def init_ui(self): self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self @@ -29,6 +32,7 @@ def init_ui(self): self.component_combobox = QComboBox() self.component_combobox.currentTextChanged.connect(self.update_comp_material) self.component_combobox.addItems(self.data.keys()) + self.component_combobox.currentTextChanged.connect(self._on_value_changed) self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) @@ -109,12 +113,30 @@ def collect_data(self): rows_data.append(row_dict) return rows_data + def _on_value_changed(self, *_args): + if getattr(self, "_initializing", False): + return + if self.parent_widget and hasattr(self.parent_widget, "mark_state_changed"): + self.parent_widget.mark_state_changed() + + def _on_type_material_changed(self, text, grade_widget, unit_widget): + self.update_comp_grades(text, grade_widget) + self.update_comp_units(text, unit_widget) + self._on_value_changed() + def update_comp_material(self, selected_component): materials = self.data.get(selected_component).keys() for i in range(len(self.material_rows)): material_combo = self.material_rows[i][KEY_TYPE] + grade_combo = self.material_rows[i][KEY_GRADE] + unit_combo = self.material_rows[i][KEY_UNIT_M3] material_combo.clear() material_combo.addItems(materials) + current_text = material_combo.currentText() + if current_text: + self.update_comp_grades(current_text, grade_combo) + self.update_comp_units(current_text, unit_combo) + self._on_value_changed() def update_comp_grades(self, selected_material, widget): selected_component = self.component_combobox.currentText() @@ -149,12 +171,10 @@ def add_material_row(self): grade_combo = QComboBox() grade_combo.setObjectName("MaterialGridInput") - type_material_combo.currentTextChanged.connect( - lambda text, widget=grade_combo: self.update_comp_grades(text, widget) - ) grade_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) row_widgets[KEY_GRADE] = grade_combo + grade_combo.currentTextChanged.connect(self._on_value_changed) quantity_edit = QLineEdit() quantity_edit.setValidator(validator) @@ -163,15 +183,14 @@ def add_material_row(self): quantity_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) row_widgets[KEY_QUANTITY] = quantity_edit + quantity_edit.textChanged.connect(self._on_value_changed) unit_combo_m3 = QComboBox() unit_combo_m3.setObjectName("MaterialGridInput") - type_material_combo.currentTextChanged.connect( - lambda text, widget=unit_combo_m3: self.update_comp_units(text, widget) - ) unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout row_widgets[KEY_UNIT_M3] = unit_combo_m3 + unit_combo_m3.currentTextChanged.connect(self._on_value_changed) rate_edit = QLineEdit() rate_edit.setValidator(validator) @@ -180,12 +199,14 @@ def add_material_row(self): rate_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) row_widgets[KEY_RATE] = rate_edit + rate_edit.textChanged.connect(self._on_value_changed) rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") rate_data_source_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit + rate_data_source_edit.textChanged.connect(self._on_value_changed) remove_button = QPushButton("x") remove_button.setFixedSize(24, 24) @@ -211,10 +232,16 @@ def add_material_row(self): self.material_grid_layout.addWidget(remove_button, row_idx, 6) row_widgets['remove_button'] = remove_button + type_material_combo.currentTextChanged.connect( + lambda text, grade_widget=grade_combo, unit_widget=unit_combo_m3: self._on_type_material_changed(text, grade_widget, unit_widget) + ) + self.material_rows.append(row_widgets) self.current_material_row_idx += 1 self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget self.adjustSize() # Adjust the size of the component widget + self._on_type_material_changed(type_material_combo.currentText(), grade_combo, unit_combo_m3) + self._on_value_changed() def remove_material_row_by_widgets(self, row_widgets_to_remove): @@ -265,6 +292,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.update() # Call update on self self.material_grid_layout.invalidate() self.adjustSize() # Adjust the size of the component widget + self._on_value_changed() class Foundation(QWidget): closed = Signal() @@ -273,8 +301,12 @@ class Foundation(QWidget): def __init__(self, database, parent=None): super().__init__() self.database_manager = database + # Track inserted comp_id values for replace-on-save + self.data_id = [] self.setObjectName("central_panel_widget") self.component_widgets = [] # To store references to each ComponentWidget instance + self._initializing = True + self.state_changed = False self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -499,8 +531,7 @@ def __init__(self, database, parent=None): next_button = QPushButton("Next") next_button.setObjectName("nav_button") - next_button.clicked.connect(self.save_data) - next_button.clicked.connect(lambda: self.next.emit(KEY_FOUNDATION)) + next_button.clicked.connect(self.on_next_clicked) self.button_h_layout.addWidget(next_button) # Add the initial component layout @@ -509,6 +540,7 @@ def __init__(self, database, parent=None): # Add initial spacing before the navigation buttons self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) + self._initializing = False def collect_data(self): all_data = [] @@ -517,10 +549,32 @@ def collect_data(self): all_data.append(component_data) return all_data + def mark_state_changed(self): + if self._initializing: + return + self.state_changed = True + def save_data(self): data = self.collect_data() - print("\nCollected Data from UI:",data) - self.database_manager.input_data_row(KEY_FOUNDATION, data) + print("\nCollected Data from UI:",data) + if self.data_id: + self.data_id = self.database_manager.replace_structure_work_rows(KEY_FOUNDATION, data, self.data_id) + else: + self.data_id = self.database_manager.input_data_row(KEY_FOUNDATION, data) + self.state_changed = False + + def on_next_clicked(self): + if not self.state_changed: + self.next.emit(KEY_FOUNDATION) + return + if self.data_id: + message = "Do you want to replace previous data?" + else: + message = "Do you want to save data?" + reply = QMessageBox.question(self, "Confirm", message, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) + if reply == QMessageBox.StandardButton.Yes: + self.save_data() + self.next.emit(KEY_FOUNDATION) def add_component_layout(self): new_component = ComponentWidget(self) @@ -544,6 +598,7 @@ def add_component_layout(self): self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + self.mark_state_changed() def remove_component_layout(self, component_to_remove): if component_to_remove in self.component_widgets: @@ -552,6 +607,7 @@ def remove_component_layout(self, component_to_remove): component_to_remove.deleteLater() self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + self.mark_state_changed() def expand_scroll_area(self): self.central_widget.layout().invalidate() diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py index 5d26cbc..250aef5 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py @@ -1,6 +1,6 @@ from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal -from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame, QMessageBox) from PySide6.QtGui import QIcon from PySide6.QtGui import QDoubleValidator from ..utils.data import * @@ -9,12 +9,15 @@ class ComponentWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) + self.parent_widget = parent + self._initializing = True self.data = construction_materials.get(KEY_SUBSTRUCTURE) self.material_rows = [] self.current_material_row_idx = 1 self.init_ui() + self._initializing = False def collect_data(self): @@ -38,6 +41,17 @@ def collect_data(self): rows_data.append(row_dict) return rows_data + def _on_value_changed(self, *_args): + if getattr(self, "_initializing", False): + return + if self.parent_widget and hasattr(self.parent_widget, "mark_state_changed"): + self.parent_widget.mark_state_changed() + + def _on_type_material_changed(self, text, grade_widget, unit_widget): + self.update_comp_grades(text, grade_widget) + self.update_comp_units(text, unit_widget) + self._on_value_changed() + def init_ui(self): self.component_first_scroll_content_layout = QVBoxLayout(self) self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) @@ -51,6 +65,7 @@ def init_ui(self): self.component_combobox = QComboBox() self.component_combobox.addItems(self.data.keys()) self.component_combobox.currentTextChanged.connect(self.update_comp_material) + self.component_combobox.currentTextChanged.connect(self._on_value_changed) self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) @@ -105,8 +120,15 @@ def update_comp_material(self, selected_component): materials = self.data.get(selected_component).keys() for i in range(len(self.material_rows)): material_combo = self.material_rows[i][KEY_TYPE] + grade_combo = self.material_rows[i][KEY_GRADE] + unit_combo = self.material_rows[i][KEY_UNIT_M3] material_combo.clear() material_combo.addItems(materials) + current_text = material_combo.currentText() + if current_text: + self.update_comp_grades(current_text, grade_combo) + self.update_comp_units(current_text, unit_combo) + self._on_value_changed() def update_comp_grades(self, selected_material, widget): selected_component = self.component_combobox.currentText() @@ -138,13 +160,11 @@ def add_material_row(self): row_widgets[KEY_TYPE] = type_material_combo grade_combo = QComboBox() - type_material_combo.currentTextChanged.connect( - lambda text, widget=grade_combo: self.update_comp_grades(text, widget) - ) grade_combo.setObjectName("MaterialGridInput") grade_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) row_widgets[KEY_GRADE] = grade_combo + grade_combo.currentTextChanged.connect(self._on_value_changed) quantity_edit = QLineEdit() quantity_edit.setValidator(validator) @@ -153,15 +173,14 @@ def add_material_row(self): quantity_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) row_widgets[KEY_QUANTITY] = quantity_edit + quantity_edit.textChanged.connect(self._on_value_changed) unit_combo_m3 = QComboBox() - type_material_combo.currentTextChanged.connect( - lambda text, widget=unit_combo_m3: self.update_comp_units(text, widget) - ) unit_combo_m3.setObjectName("MaterialGridInput") unit_combo_m3.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) row_widgets[KEY_UNIT_M3] = unit_combo_m3 + unit_combo_m3.currentTextChanged.connect(self._on_value_changed) rate_edit = QLineEdit() rate_edit.setValidator(validator) @@ -170,12 +189,14 @@ def add_material_row(self): rate_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) row_widgets[KEY_RATE] = rate_edit + rate_edit.textChanged.connect(self._on_value_changed) rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") rate_data_source_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit + rate_data_source_edit.textChanged.connect(self._on_value_changed) remove_button = QPushButton("x") remove_button.setFixedSize(24, 24) @@ -200,10 +221,16 @@ def add_material_row(self): self.material_grid_layout.addWidget(remove_button, row_idx, 6) row_widgets['remove_button'] = remove_button + type_material_combo.currentTextChanged.connect( + lambda text, grade_widget=grade_combo, unit_widget=unit_combo_m3: self._on_type_material_changed(text, grade_widget, unit_widget) + ) + self.material_rows.append(row_widgets) self.current_material_row_idx += 1 self.updateGeometry() self.adjustSize() + self._on_type_material_changed(type_material_combo.currentText(), grade_combo, unit_combo_m3) + self._on_value_changed() def remove_material_row_by_widgets(self, row_widgets_to_remove): @@ -254,6 +281,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.update() self.material_grid_layout.invalidate() self.adjustSize() + self._on_value_changed() class SubStructure(QWidget): closed = Signal() @@ -262,8 +290,12 @@ class SubStructure(QWidget): def __init__(self, database, parent=None): super().__init__() self.database_manager = database + # Track inserted comp_id values for replace-on-save + self.data_id = [] self.setObjectName("central_panel_widget") self.component_widgets = [] + self._initializing = True + self.state_changed = False self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -485,14 +517,14 @@ def __init__(self, database, parent=None): next_button = QPushButton("Next") next_button.setObjectName("nav_button") - next_button.clicked.connect(lambda: self.next.emit(KEY_SUBSTRUCTURE)) - next_button.clicked.connect(self.save_data) + next_button.clicked.connect(self.on_next_clicked) self.button_h_layout.addWidget(next_button) self.add_component_layout() self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) + self._initializing = False def add_component_layout(self): new_component = ComponentWidget(self) @@ -512,6 +544,7 @@ def add_component_layout(self): self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + self.mark_state_changed() def remove_component_layout(self, component_to_remove): if component_to_remove in self.component_widgets: @@ -520,6 +553,7 @@ def remove_component_layout(self, component_to_remove): component_to_remove.deleteLater() self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + self.mark_state_changed() def collect_data(self): all_data = [] @@ -528,10 +562,32 @@ def collect_data(self): all_data.append(component_data) return all_data + def mark_state_changed(self): + if self._initializing: + return + self.state_changed = True + def save_data(self): data = self.collect_data() print("\nCollected Data from UI:",data) - self.database_manager.input_data_row(KEY_SUBSTRUCTURE, data) + if self.data_id: + self.data_id = self.database_manager.replace_structure_work_rows(KEY_SUBSTRUCTURE, data, self.data_id) + else: + self.data_id = self.database_manager.input_data_row(KEY_SUBSTRUCTURE, data) + self.state_changed = False + + def on_next_clicked(self): + if not self.state_changed: + self.next.emit(KEY_SUBSTRUCTURE) + return + if self.data_id: + message = "Do you want to replace previous data?" + else: + message = "Do you want to save data?" + reply = QMessageBox.question(self, "Confirm", message, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) + if reply == QMessageBox.StandardButton.Yes: + self.save_data() + self.next.emit(KEY_SUBSTRUCTURE) def expand_scroll_area(self): self.central_widget.layout().invalidate() diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py index 303bd9f..f4d95d0 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py @@ -1,6 +1,6 @@ from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal -from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame, QMessageBox) from PySide6.QtGui import QIcon from PySide6.QtGui import QDoubleValidator from ..utils.data import * @@ -9,12 +9,14 @@ class ComponentWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) + self.parent_widget = parent + self._initializing = True self.data = construction_materials.get(KEY_SUPERSTRUCTURE) self.material_rows = [] self.current_material_row_idx = 1 self.init_ui() - + self._initializing = False def collect_data(self): rows_data = [] @@ -37,6 +39,17 @@ def collect_data(self): rows_data.append(row_dict) return rows_data + def _on_value_changed(self, *_args): + if getattr(self, "_initializing", False): + return + if self.parent_widget and hasattr(self.parent_widget, "mark_state_changed"): + self.parent_widget.mark_state_changed() + + def _on_type_material_changed(self, text, grade_widget, unit_widget): + self.update_comp_grades(text, grade_widget) + self.update_comp_units(text, unit_widget) + self._on_value_changed() + def init_ui(self): self.component_first_scroll_content_layout = QVBoxLayout(self) self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) @@ -50,6 +63,7 @@ def init_ui(self): self.component_combobox = QComboBox() self.component_combobox.currentTextChanged.connect(self.update_comp_material) self.component_combobox.addItems(self.data.keys()) + self.component_combobox.currentTextChanged.connect(self._on_value_changed) self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) @@ -105,8 +119,15 @@ def update_comp_material(self, selected_component): materials = self.data.get(selected_component).keys() for i in range(len(self.material_rows)): material_combo = self.material_rows[i][KEY_TYPE] + grade_combo = self.material_rows[i][KEY_GRADE] + unit_combo = self.material_rows[i][KEY_UNIT_M3] material_combo.clear() material_combo.addItems(materials) + current_text = material_combo.currentText() + if current_text: + self.update_comp_grades(current_text, grade_combo) + self.update_comp_units(current_text, unit_combo) + self._on_value_changed() def update_comp_grades(self, selected_material, widget): selected_component = self.component_combobox.currentText() @@ -138,13 +159,11 @@ def add_material_row(self): row_widgets[KEY_TYPE] = type_material_combo # CHANGE 7: Changed key to KEY_TYPE grade_combo = QComboBox() - type_material_combo.currentTextChanged.connect( - lambda text, widget=grade_combo: self.update_comp_grades(text, widget) - ) grade_combo.setObjectName("MaterialGridInput") grade_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) row_widgets[KEY_GRADE] = grade_combo + grade_combo.currentTextChanged.connect(self._on_value_changed) quantity_edit = QLineEdit() quantity_edit.setValidator(validator) @@ -153,15 +172,14 @@ def add_material_row(self): quantity_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) row_widgets[KEY_QUANTITY] = quantity_edit + quantity_edit.textChanged.connect(self._on_value_changed) unit_combo_m3 = QComboBox() - type_material_combo.currentTextChanged.connect( - lambda text, widget=unit_combo_m3: self.update_comp_units(text, widget) - ) unit_combo_m3.setObjectName("MaterialGridInput") unit_combo_m3.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) row_widgets[KEY_UNIT_M3] = unit_combo_m3 + unit_combo_m3.currentTextChanged.connect(self._on_value_changed) rate_edit = QLineEdit() rate_edit.setValidator(validator) @@ -170,12 +188,14 @@ def add_material_row(self): rate_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) row_widgets[KEY_RATE] = rate_edit + rate_edit.textChanged.connect(self._on_value_changed) rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") rate_data_source_edit.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit + rate_data_source_edit.textChanged.connect(self._on_value_changed) remove_button = QPushButton("x") remove_button.setFixedSize(24, 24) @@ -201,10 +221,16 @@ def add_material_row(self): self.material_grid_layout.addWidget(remove_button, row_idx, 6) row_widgets['remove_button'] = remove_button + type_material_combo.currentTextChanged.connect( + lambda text, grade_widget=grade_combo, unit_widget=unit_combo_m3: self._on_type_material_changed(text, grade_widget, unit_widget) + ) + self.material_rows.append(row_widgets) self.current_material_row_idx += 1 self.updateGeometry() self.adjustSize() + self._on_type_material_changed(type_material_combo.currentText(), grade_combo, unit_combo_m3) + self._on_value_changed() def remove_material_row_by_widgets(self, row_widgets_to_remove): @@ -255,6 +281,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.update() self.material_grid_layout.invalidate() self.adjustSize() + self._on_value_changed() class SuperStructure(QWidget): closed = Signal() @@ -263,8 +290,12 @@ class SuperStructure(QWidget): def __init__(self, database, parent=None): super().__init__() self.database_manager = database + # Track inserted comp_id values for replace-on-save + self.data_id = [] self.setObjectName("central_panel_widget") self.component_widgets = [] + self._initializing = True + self.state_changed = False self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -486,14 +517,14 @@ def __init__(self, database, parent=None): next_button = QPushButton("Next") next_button.setObjectName("nav_button") - next_button.clicked.connect(lambda: self.next.emit(KEY_SUPERSTRUCTURE)) - next_button.clicked.connect(self.save_data) + next_button.clicked.connect(self.on_next_clicked) self.button_h_layout.addWidget(next_button) self.add_component_layout() self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) + self._initializing = False def add_component_layout(self): new_component = ComponentWidget(self) @@ -513,6 +544,7 @@ def add_component_layout(self): self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + self.mark_state_changed() def remove_component_layout(self, component_to_remove): if component_to_remove in self.component_widgets: @@ -521,6 +553,7 @@ def remove_component_layout(self, component_to_remove): component_to_remove.deleteLater() self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() + self.mark_state_changed() def collect_data(self): all_data = [] @@ -529,10 +562,32 @@ def collect_data(self): all_data.append(component_data) return all_data + def mark_state_changed(self): + if self._initializing: + return + self.state_changed = True + def save_data(self): data = self.collect_data() print("\nCollected Data from UI:",data) - self.database_manager.input_data_row(KEY_SUPERSTRUCTURE, data) + if self.data_id: + self.data_id = self.database_manager.replace_structure_work_rows(KEY_SUPERSTRUCTURE, data, self.data_id) + else: + self.data_id = self.database_manager.input_data_row(KEY_SUPERSTRUCTURE, data) + self.state_changed = False + + def on_next_clicked(self): + if not self.state_changed: + self.next.emit(KEY_SUPERSTRUCTURE) + return + if self.data_id: + message = "Do you want to replace previous data?" + else: + message = "Do you want to save data?" + reply = QMessageBox.question(self, "Confirm", message, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) + if reply == QMessageBox.StandardButton.Yes: + self.save_data() + self.next.emit(KEY_SUPERSTRUCTURE) def expand_scroll_area(self): self.central_widget.layout().invalidate() diff --git a/src/osbridgelcca/desktop_app/widgets/utils/database.py b/src/osbridgelcca/desktop_app/widgets/utils/database.py index f5f3b70..5167fa4 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/database.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/database.py @@ -158,7 +158,7 @@ def insert_component(self, comp_id: int, type_material: str, grade: str, self.conn.commit() return component_id - def input_data_row(self, work_type: str, rows_data: List[Dict]) -> int: + def input_data_row(self, work_type: str, rows_data: List[Dict]) -> List[int]: """ Input complete data row with structure work and multiple components @@ -167,31 +167,36 @@ def input_data_row(self, work_type: str, rows_data: List[Dict]) -> int: rows_data: List of dictionaries containing component data Returns: - comp_id: The auto-generated component ID from struct_works_data + List of comp_id values created in struct_works_data for the provided rows Example: rows_data = [ - { - KEY_COMPONENT: "Pile", - KEY_TYPE: "Steel Re", - KEY_GRADE: "Fe415", - KEY_QUANTITY: "100", - KEY_UNIT_M3: "cum", - KEY_RATE: "5000.00", - KEY_RATE_DATA_SOURCE: "Market Survey" - }, + [ + { + KEY_COMPONENT: "Pile", + KEY_TYPE: "Steel Re", + KEY_GRADE: "Fe415", + KEY_QUANTITY: "100", + KEY_UNIT_M3: "cum", + KEY_RATE: "5000.00", + KEY_RATE_DATA_SOURCE: "Market Survey" + }, + ... + ], ... ] """ if not rows_data: raise ValueError("rows_data cannot be empty") + created_comp_ids: List[int] = [] for row in rows_data: # Get component type from first row component_type = row[0].get(KEY_COMPONENT, "Unknown") # Create structure work entry - this generates comp_id comp_id = self.insert_structure_work(work_type, component_type) + created_comp_ids.append(comp_id) # Insert all component rows with the generated comp_id for row_dict in row: @@ -211,6 +216,26 @@ def input_data_row(self, work_type: str, rows_data: List[Dict]) -> int: rate=rate, rate_data_source=rate_data_source ) + + return created_comp_ids + + def replace_structure_work_rows(self, work_type: str, rows_data: List[Dict], old_comp_ids: List[int]) -> List[int]: + """ + Delete existing structure work rows by comp_id and insert new rows. + + Args: + work_type: Type of structure work (Foundation, Sub-Structure, etc.) + rows_data: New rows data to insert (same structure as input_data_row) + old_comp_ids: List of comp_id values to delete before inserting new data + + Returns: + List[int]: Newly created comp_id values for the inserted data + """ + if old_comp_ids: + for comp_id in old_comp_ids: + self.delete_structure_work(comp_id) + + return self.input_data_row(work_type, rows_data) def delete_structure_work(self, comp_id: int): """Delete a structure work and all its components (CASCADE)""" From 869216e23ee105b348fb0850e765b7d29acf441a Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Thu, 13 Nov 2025 20:04:04 +0530 Subject: [PATCH 17/22] Updated Road User UI --- .../widgets/bridge_and_traffic_data.py | 249 +++++++++++++++--- .../carbon_emission_cost_data.py | 2 +- .../carbon_emission_data.py | 122 +++++---- .../widgets/demolition_and_recycling_data.py | 64 ++++- .../desktop_app/widgets/financial_data.py | 33 ++- .../widgets/maintenance_repair_data.py | 32 ++- .../auxiliary_works_widget.py | 107 +++++++- .../structure_works_data/foundation_widget.py | 243 ++++++++++------- .../sub_structure_widget.py | 115 +++++++- .../super_structure_widget.py | 117 +++++++- .../desktop_app/widgets/title_bar.py | 2 +- .../desktop_app/widgets/utils/data.py | 44 ++-- .../desktop_app/widgets/utils/database.py | 142 +++++----- 13 files changed, 963 insertions(+), 309 deletions(-) diff --git a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py index c63beac..aef648c 100644 --- a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py +++ b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py @@ -1,3 +1,4 @@ +from math import comb from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) @@ -198,7 +199,7 @@ def __init__(self, database, parent=None): grid_layout.setVerticalSpacing(20) # Number of Lanes - label = QLabel("Number of Lanes") + label = QLabel("Alternate Road Carriageway") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 0, 0, 1, 1) @@ -234,6 +235,26 @@ def __init__(self, database, parent=None): info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) grid_layout.addWidget(info_icon, 1, 2, 1, 1) + # Additional Travel Time + label = QLabel("Additional Travel Time") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 2, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + input_widget.setFixedWidth(self.text_box_width) + input_widget.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + self.traffic_widgets.append(input_widget) + grid_layout.addWidget(input_widget, 2, 1, 1, 1) + info_icon = QLabel("(min)") + info_icon.setStyleSheet("color: grey; font-size: 14px;") + info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(info_icon, 2, 2, 1, 1) + # Road Roughness label = QLabel("Road Roughness") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) @@ -242,7 +263,8 @@ def __init__(self, database, parent=None): valuer_combo = QComboBox(self.general_widget) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") - valuer_combo.addItems(self.data[KEY_ROADROUGHNESS][KEY_OPTIONS]) + valuer_combo.addItems(self.data[KEY_ROADROUGHNESS][KEY_OPTIONS] + ["Custom"]) + valuer_combo.currentIndexChanged.connect(lambda index,combo=valuer_combo: self.custom_combo_input(index, combo)) self.traffic_widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 3, 1, 1, 1) @@ -251,15 +273,16 @@ def __init__(self, database, parent=None): info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) grid_layout.addWidget(info_icon, 3, 2, 1, 1) - # Road Rise and Fall (RF) - label = QLabel("Road Rise and Fall (RF)") + # Road Rise + label = QLabel("Road Rise") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 4, 0, 1, 1) valuer_combo = QComboBox(self.general_widget) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") - valuer_combo.addItems(self.data[KEY_ROAD_RISE_AND_FALL][KEY_OPTIONS]) + valuer_combo.addItems(self.data[KEY_ROAD_RISE_AND_FALL][KEY_OPTIONS] + ["Custom"]) + valuer_combo.currentIndexChanged.connect(lambda index,combo=valuer_combo: self.custom_combo_input(index, combo)) self.traffic_widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 4, 1, 1, 1) @@ -268,76 +291,198 @@ def __init__(self, database, parent=None): info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) grid_layout.addWidget(info_icon, 4, 2, 1, 1) - - # Type of Road - label = QLabel("Type of Road") + # Road Fall + label = QLabel("Road Fall") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 5, 0, 1, 1) valuer_combo = QComboBox(self.general_widget) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") - valuer_combo.addItems(self.data[KEY_TYPE_OF_ROAD][KEY_OPTIONS]) + valuer_combo.addItems(self.data[KEY_ROAD_RISE_AND_FALL][KEY_OPTIONS] + ["Custom"]) + valuer_combo.currentIndexChanged.connect(lambda index,combo=valuer_combo: self.custom_combo_input(index, combo)) + self.traffic_widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 5, 1, 1, 1) - self.traffic_widgets.append(valuer_combo) - info_icon = QLabel(" ") + info_icon = QLabel("(m/km)") info_icon.setStyleSheet("color: grey; font-size: 14px;") info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) grid_layout.addWidget(info_icon, 5, 2, 1, 1) - # Annual Increase in Traffic - label = QLabel("Annual Increaase in Traffic if Re-Routing duration increases more than a year ") - label.setFixedWidth(200) - label.setWordWrap(True) + # Type of Road + label = QLabel("Type of Road") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 6, 0, 1, 1) valuer_combo = QComboBox(self.general_widget) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") - valuer_combo.addItems(self.data[KEY_ANNUAL_INCREASE][KEY_OPTIONS]) - self.traffic_widgets.append(valuer_combo) + valuer_combo.addItems(self.data[KEY_TYPE_OF_ROAD][KEY_OPTIONS]) grid_layout.addWidget(valuer_combo, 6, 1, 1, 1) + self.traffic_widgets.append(valuer_combo) - info_icon = QLabel("(%)") - info_icon.setStyleSheet("color: grey; font-size: 14px; padding-top: 14px;") + info_icon = QLabel(" ") + info_icon.setStyleSheet("color: grey; font-size: 14px;") info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) grid_layout.addWidget(info_icon, 6, 2, 1, 1) + # Crash Rate + label = QLabel("Crash Rate") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 7, 0, 1, 1) + input_widget = QLineEdit(self.general_widget) + input_widget.setFixedWidth(self.text_box_width) + input_widget.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + self.traffic_widgets.append(input_widget) + grid_layout.addWidget(input_widget, 7, 1, 1, 1) + info_icon = QLabel("(accidents/million km)") + info_icon.setStyleSheet("color: grey; font-size: 14px;") + info_icon.setAlignment(Qt.AlignmentFlag.AlignLeft) + grid_layout.addWidget(info_icon, 7, 2, 1, 1) + + + # Category of accidents=Start============================================================== + # Composition of Various Vehicles - # Remove the old label and vehicle_widget from the grid - # Instead, create a horizontal layout for this row composition_row_widget = QWidget(self.general_widget) composition_row_layout = QHBoxLayout(composition_row_widget) composition_row_layout.setContentsMargins(0, 0, 0, 0) composition_row_layout.setSpacing(20) # Space between label and box + + # The white box (vehicle_widget) as before + vehicle_widget = QWidget(self.general_widget) + vehicle_widget.setStyleSheet("background-color: #FFFFFF; border-radius: 10px; border: 1px solid #DDDCE0;") + vehicle_layout = QGridLayout(vehicle_widget) + vehicle_layout.setContentsMargins(8, 8, 8, 8) + + vehicle_type_label = QLabel("Type of Accident") + vehicle_type_label.setAlignment(Qt.AlignCenter) + vehicle_type_label.setStyleSheet(""" + QLabel { + border: 0px; + padding: 3px 10px; + } + """) - # The label - composition_label = QLabel("Composition of Various Vehicles") - composition_label.setAlignment(Qt.AlignLeft | Qt.AlignTop) - composition_row_layout.addWidget(composition_label, alignment=Qt.AlignTop) + per_dist_label = QLabel("% Accident Distribution") + per_dist_label.setAlignment(Qt.AlignCenter) + per_dist_label.setStyleSheet(""" + QLabel { + border: 0px; + padding: 3px 10px; + } + """) + + vehicle_layout.addWidget(vehicle_type_label, 0, 0) + vehicle_layout.addWidget(per_dist_label, 0, 1) + vehicles = [ + "Minor Injury", + "Major Injury", + "Fatal" + ] + + for i, vehicle in enumerate(vehicles): + v_label = QLabel(f"{vehicle}:") + v_label.setFixedHeight(40) + v_label.setAlignment(Qt.AlignCenter) + v_label.setStyleSheet("background-color: #FFFFFF; border: 1px solid #FFFFFF; border-radius: 10px; padding: 10px 10px 10px 1px;") + v0_input = QLineEdit() + self.traffic_widgets.append(v0_input) + v0_input.setFixedWidth(self.text_box_width*2) + v0_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background: #FFFFFF; + } + """) + vehicle_layout.addWidget(v_label, i+1, 0) + vehicle_layout.addWidget(v0_input, i+1, 1) + + v_label1 = QLabel("") + v_label1.setStyleSheet(" padding: 10px 10px 10px 1px;") + + composition_row_layout.addWidget(vehicle_widget, alignment=Qt.AlignTop) + + # Add the composition_row_widget to the main grid, spanning columns 0-2 + grid_layout.addWidget(v_label1, 8, 4, 1, 4) + grid_layout.addWidget(composition_row_widget, 8, 0, 1, 4) + + # Category of accidents=End============================================================== + + # Vehicle =Start================================================================ + + # Composition of Various Vehicles + composition_row_widget = QWidget(self.general_widget) + composition_row_layout = QHBoxLayout(composition_row_widget) + composition_row_layout.setContentsMargins(0, 0, 0, 0) + composition_row_layout.setSpacing(20) # Space between label and box + # The white box (vehicle_widget) as before vehicle_widget = QWidget(self.general_widget) - vehicle_widget.setStyleSheet("background-color: #FFFFFF; border-radius: 10px; border: 1px solid #DDDCE0") - vehicle_widget.setFixedWidth(400) - vehicle_widget.setFixedHeight(250) + vehicle_widget.setStyleSheet("background-color: #FFFFFF; border-radius: 10px; border: 1px solid #DDDCE0;") vehicle_layout = QGridLayout(vehicle_widget) - vehicle_layout.setContentsMargins(0, 0, 0, 0) - # vehicle_layout.setHorizontalSpacing(7) - # vehicle_layout.setVerticalSpacing(8) + vehicle_layout.setContentsMargins(8, 8, 8, 8) + + vehicle_type_label = QLabel("Type of Vehicle") + vehicle_type_label.setAlignment(Qt.AlignCenter) + vehicle_type_label.setStyleSheet(""" + QLabel { + border: 0px; + padding: 3px 10px; + } + """) + + composition_label = QLabel("Composition of Various Vehicles") + composition_label.setAlignment(Qt.AlignCenter) + composition_label.setStyleSheet(""" + QLabel { + border: 0px; + padding: 3px 10px; + } + """) + + per_dist_label = QLabel("% Accident Distribution") + per_dist_label.setAlignment(Qt.AlignCenter) + per_dist_label.setStyleSheet(""" + QLabel { + border: 0px; + padding: 3px 10px; + } + """) + + vehicle_layout.addWidget(vehicle_type_label, 0, 0) + vehicle_layout.addWidget(composition_label, 0, 1) + vehicle_layout.addWidget(per_dist_label, 0, 2) + + vehicles = [ + "Two Wheeler", + "Small Car", + "Big Car", + "Ordinary Bus", + "Deluxe Bus", + "LCV", + "MCV", + "HCV" + ] - vehicles = ["Cars", "Buses", "HCV", "MCV", "LCV"] for i, vehicle in enumerate(vehicles): v_label = QLabel(f"{vehicle}:") v_label.setFixedHeight(40) - v_label.setFixedWidth(50) + v_label.setAlignment(Qt.AlignCenter) v_label.setStyleSheet("background-color: #FFFFFF; border: 1px solid #FFFFFF; border-radius: 10px; padding: 10px 10px 10px 1px;") - v_input = QLineEdit() - self.traffic_widgets.append(v_input) - v_input.setFixedWidth(self.text_box_width) - v_input.setStyleSheet(""" + v0_input = QLineEdit() + self.traffic_widgets.append(v0_input) + v0_input.setFixedWidth(self.text_box_width) + v0_input.setStyleSheet(""" QLineEdit { border: 1px solid #DDDCE0; border-radius: 10px; @@ -345,17 +490,31 @@ def __init__(self, database, parent=None): background: #FFFFFF; } """) - vehicle_layout.addWidget(v_label, i, 0) - vehicle_layout.addWidget(v_input, i, 1) + v1_input = QLineEdit() + self.traffic_widgets.append(v1_input) + v1_input.setFixedWidth(self.text_box_width) + v1_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background: #FFFFFF; + } + """) + vehicle_layout.addWidget(v_label, i+1, 0) + vehicle_layout.addWidget(v0_input, i+1, 1) + vehicle_layout.addWidget(v1_input, i+1, 2) - v_label1 = QLabel("(PCU/D)") + v_label1 = QLabel("(Vehicles/Day)") v_label1.setStyleSheet(" padding: 10px 10px 10px 1px;") composition_row_layout.addWidget(vehicle_widget, alignment=Qt.AlignTop) # Add the composition_row_widget to the main grid, spanning columns 0-2 - grid_layout.addWidget(v_label1, 7, 3, 1, 3) - grid_layout.addWidget(composition_row_widget, 7, 0, 1, 3) + grid_layout.addWidget(v_label1, 9, 4, 1, 4) + grid_layout.addWidget(composition_row_widget, 9, 0, 1, 4) + + # Vehicle Data=End============================================================== self.general_layout.addLayout(grid_layout) self.general_layout.addStretch(1) @@ -389,6 +548,12 @@ def __init__(self, database, parent=None): left_panel_vlayout.addWidget(self.scroll_area) + def custom_combo_input(self, index, combo): + if combo.itemText(index) == "Custom": + combo.setEditable(True) + combo.lineEdit().setText("") + combo.lineEdit().setPlaceholderText("Type here...") + def collect_data(self): data = [] for widget in self.traffic_widgets: diff --git a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py index 4bf4fae..2cb4e86 100644 --- a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py +++ b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py @@ -275,7 +275,7 @@ def collect_data(self): carbon_cost = float(self.widget[2].text()) # Social Cost of Carbon input carbon_emission_cost = self.database_manager.carbon_emission_cost(carbon_cost) # Update Results Dict - self.parent.results[COST_TOTAL_INIT_CARBON_EMISSION] = carbon_cost + self.parent.results[COST_TOTAL_INIT_CARBON_EMISSION] = carbon_emission_cost def close_widget(self): self.closed.emit() diff --git a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py index 548fdc9..2a41901 100644 --- a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py +++ b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py @@ -43,100 +43,124 @@ def init_ui(self): for item in self.data: self.widgets.append(self.add_material_row(item)) - - - # # --- Add Material Button --- - # self.add_material_button = QPushButton("+ Add Material") - # self.add_material_button.setObjectName("add_material_button") - # self.add_material_button.clicked.connect(self.add_material_row) - # self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + # --- Add Material Button --- + self.add_material_button = QPushButton("+ Add Material") + self.add_material_button.setObjectName("add_material_button") + self.add_material_button.clicked.connect(self.add_material_row) + self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) - - def add_material_row(self, item): - row_widgets = [item[1], item[2], item[3], item[4]] + def add_material_row(self, item=[]): row_idx = self.current_material_row_idx + row_widgets = [] # Set fixed width for input widgets. - fixed_input_width_combo = 80 # Width for individual combo boxes - fixed_input_width_line_edit = 80 # Width for individual line edits - - type_material_combo = QComboBox() - type_material_combo.addItem(item[0]) - type_material_combo.setObjectName("MaterialGridInput") - type_material_combo.setFixedWidth(fixed_input_width_combo) - self.material_grid_layout.addWidget(type_material_combo, row_idx, 0, alignment=Qt.AlignmentFlag.AlignHCenter) - + fixed_input_width_combo = 80 + fixed_input_width_line_edit = 80 + + # Type of Material (Column 0) + if item: + type_material = QComboBox() + type_material.addItem(item[0]) + else: + type_material = QLineEdit() + type_material.setPlaceholderText("Material...") + type_material.setObjectName("MaterialGridInput") + type_material.setFixedWidth(fixed_input_width_combo) + self.material_grid_layout.addWidget(type_material, row_idx, 0, alignment=Qt.AlignmentFlag.AlignHCenter) + + # Quantity (Column 1) quantity_edit = QLineEdit() - quantity_edit.setText(str(item[2])) - quantity_edit.setReadOnly(True) + if item: + quantity_edit.setText(str(item[2])) + quantity_edit.setReadOnly(True) quantity_edit.setObjectName("MaterialGridInput") quantity_edit.setFixedWidth(fixed_input_width_line_edit) self.material_grid_layout.addWidget(quantity_edit, row_idx, 1, alignment=Qt.AlignmentFlag.AlignHCenter) - unit_combo_m3 = QComboBox() - unit_combo_m3.addItem(str(item[1])) - unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width_combo) - self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 2, alignment=Qt.AlignmentFlag.AlignHCenter) - + # Unit (Column 2) + if item: + unit_combo = QComboBox() + unit_combo.addItem(str(item[1])) + else: + unit_combo = QLineEdit() + unit_combo.setPlaceholderText("Unit...") + unit_combo.setObjectName("MaterialGridInput") + unit_combo.setFixedWidth(fixed_input_width_combo) + self.material_grid_layout.addWidget(unit_combo, row_idx, 2, alignment=Qt.AlignmentFlag.AlignHCenter) + + # Build row_widgets array in correct order: [unit, quantity, type, grade, embodied_carbon, carbon_emission] + if item: + row_widgets = [item[1], item[2], item[3], item[4]] + else: + row_widgets = [unit_combo, quantity_edit, type_material, None] # --- Embodied Carbon Energy (LineEdit + QLabel for unit text) - Column 3 --- embodied_carbon_layout = QHBoxLayout() embodied_carbon_layout.addStretch() embodied_carbon_layout.setContentsMargins(0, 0, 0, 0) - embodied_carbon_layout.setSpacing(5) # Small spacing between line edit and label + embodied_carbon_layout.setSpacing(5) embodied_carbon_edit = QLineEdit() - row_widgets.append(embodied_carbon_edit) embodied_carbon_edit.setPlaceholderText("0.00") embodied_carbon_edit.setObjectName("MaterialGridInput") - embodied_carbon_edit.setFixedWidth(fixed_input_width_combo) # Changed to fixed_input_width_combo + embodied_carbon_edit.setFixedWidth(fixed_input_width_combo) embodied_carbon_layout.addWidget(embodied_carbon_edit) - # Replaced QComboBox with QLabel for static text embodied_carbon_unit_label = QLabel("(MJ/kg)") embodied_carbon_unit_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) - embodied_carbon_unit_label.setStyleSheet("color: #3F3E5E; font-size: 11px;") # Style to match other text + embodied_carbon_unit_label.setStyleSheet("color: #3F3E5E; font-size: 11px;") embodied_carbon_layout.addWidget(embodied_carbon_unit_label) - self.material_grid_layout.addLayout(embodied_carbon_layout, row_idx, 3, alignment=Qt.AlignmentFlag.AlignHCenter) - + + # Add embodied carbon edit to row_widgets (index 4) + row_widgets.append(embodied_carbon_edit) # --- Carbon Emission Factor (LineEdit + QLabel for unit text) - Column 4 --- carbon_emission_layout = QHBoxLayout() carbon_emission_layout.setContentsMargins(0, 0, 0, 0) - carbon_emission_layout.setSpacing(5) # Small spacing between line edit and label - + carbon_emission_layout.setSpacing(5) carbon_emission_layout.addStretch() carbon_emission_edit = QLineEdit() - row_widgets.append(carbon_emission_edit) carbon_emission_edit.setPlaceholderText("0.00") carbon_emission_edit.setObjectName("MaterialGridInput") - carbon_emission_edit.setFixedWidth(fixed_input_width_combo) # Changed to fixed_input_width_combo + carbon_emission_edit.setFixedWidth(fixed_input_width_combo) carbon_emission_layout.addWidget(carbon_emission_edit) - # Replaced QComboBox with QLabel for static text - carbon_emission_unit_label = QLabel("kg CO2e/kg") + # Create the carbon emission unit label + if item: + carbon_emission_unit_label = QLabel("kg CO2e/" + str(item[1])) + else: + carbon_emission_unit_label = QLabel("kg CO2e/kg") carbon_emission_unit_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) - carbon_emission_unit_label.setStyleSheet("color: #3F3E5E; font-size: 11px;") # Style to match other text + carbon_emission_unit_label.setStyleSheet("color: #3F3E5E; font-size: 11px;") carbon_emission_layout.addWidget(carbon_emission_unit_label) carbon_emission_layout.addStretch() - self.material_grid_layout.addLayout(carbon_emission_layout, row_idx, 4) + + # Add carbon emission edit to row_widgets (index 5) + row_widgets.append(carbon_emission_edit) + # Only connect if it's a custom row (unit_combo is QLineEdit, not QComboBox) + if not item: # This means it's a custom material row + def update_carbon_unit(text): + unit_text = text.strip() if text.strip() else "kg" + carbon_emission_unit_label.setText(f"kg CO2e/{unit_text}") + + unit_combo.textChanged.connect(update_carbon_unit) self.material_rows.append(row_widgets) self.current_material_row_idx += 1 self.updateGeometry() self.adjustSize() - return row_widgets - - + if not item: + # append widget to self.widget + self.widgets.append(row_widgets) + return row_widgets def remove_material_row_by_widgets(self, row_widgets_to_remove): if row_widgets_to_remove not in self.material_rows: @@ -194,10 +218,10 @@ def collect_data(self): p = [] for row in self.widgets: data = { - KEY_TYPE: row[2], - KEY_GRADE: row[3], - KEY_QUANTITY: row[1], - KEY_UNIT_M3: row[0], + KEY_TYPE: row[2].text() if isinstance(row[2], QLineEdit) else row[2], + KEY_GRADE: row[3].text() if isinstance(row[3], QLineEdit) else "", + KEY_QUANTITY: row[1].text() if isinstance(row[1], QLineEdit) else row[1], + KEY_UNIT_M3: row[0].text() if isinstance(row[0], QLineEdit) else row[0], KEY_EMBODIED_CARBON_ENERGY: row[4].text() if row[4].text() else "0", KEY_CARBON_EMISSION_FACTOR: row[5].text() if row[4].text() else "0", } diff --git a/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py b/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py index bc0d5f1..01d4bac 100644 --- a/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py +++ b/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py @@ -203,7 +203,7 @@ def __init__(self, database, parent=None): self.widgets.append(scrap_value_input) scrap_value_input.setAlignment(Qt.AlignmentFlag.AlignCenter) scrap_value_input.setFixedWidth(field_width) - scrap_value_input.setText("50000") + scrap_value_input.setText("26000") scrap_value_input.setStyleSheet(""" QLineEdit { border: 1px solid #DDDCE0; @@ -231,7 +231,7 @@ def __init__(self, database, parent=None): self.widgets.append(steel_scrap_input) steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignCenter) steel_scrap_input.setFixedWidth(field_width) - steel_scrap_input.setText("98") + steel_scrap_input.setText("95") steel_scrap_input.setStyleSheet(""" QLineEdit { border: 1px solid #DDDCE0; @@ -247,6 +247,62 @@ def __init__(self, database, parent=None): steel_scrap_layout.addStretch(1) grid_layout.addWidget(steel_scrap_widget, 2, 1, 1, 1, alignment=Qt.AlignLeft) + # 4. Scrap Value of Steel Rebar + label = QLabel("Scrap Value of Steel Rebar") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 3, 0, 1, 1) + scrap_value_widget = QWidget(self.general_widget) + scrap_value_layout = QHBoxLayout(scrap_value_widget) + scrap_value_layout.setContentsMargins(0,0,0,0) + scrap_value_layout.setSpacing(10) + scrap_value_input = QLineEdit() + self.widgets.append(scrap_value_input) + scrap_value_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + scrap_value_input.setFixedWidth(field_width) + scrap_value_input.setText("32500") + scrap_value_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + scrap_value_layout.addWidget(scrap_value_input) + scrap_value_layout.addWidget(QLabel("(INR/MT)")) + suggested_label2 = QLabel("Suggested") + suggested_label2.setStyleSheet("color: #B3AEAE; font-size: 10px;") + scrap_value_layout.addWidget(suggested_label2) + scrap_value_layout.addStretch(1) + grid_layout.addWidget(scrap_value_widget, 3, 1, 1, 1, alignment=Qt.AlignLeft) + + # 4. Steel Rebar Scrap + label = QLabel("Steel Rebar Scrap") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 4, 0, 1, 1) + steel_scrap_widget = QWidget(self.general_widget) + steel_scrap_layout = QHBoxLayout(steel_scrap_widget) + steel_scrap_layout.setContentsMargins(0,0,0,0) + steel_scrap_layout.setSpacing(10) + steel_scrap_input = QLineEdit() + self.widgets.append(steel_scrap_input) + steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + steel_scrap_input.setFixedWidth(field_width) + steel_scrap_input.setText("75") + steel_scrap_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + steel_scrap_layout.addWidget(steel_scrap_input) + steel_scrap_layout.addWidget(QLabel("(%)")) + suggested_label3 = QLabel("Suggested") + suggested_label3.setStyleSheet("color: #B3AEAE; font-size: 10px;") + steel_scrap_layout.addWidget(suggested_label3) + steel_scrap_layout.addStretch(1) + grid_layout.addWidget(steel_scrap_widget, 4, 1, 1, 1, alignment=Qt.AlignLeft) + self.general_layout.addLayout(grid_layout) self.general_layout.addStretch(1) self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) @@ -292,6 +348,10 @@ def collect_data(self): elif isinstance(widget, QLineEdit): value = widget.text() data.append(value) + + data[0] = float(data[0])/100 + data[2] = float(data[2])/100 + print("Collected Data from UI:",data) # calculate demolition and disposal cost diff --git a/src/osbridgelcca/desktop_app/widgets/financial_data.py b/src/osbridgelcca/desktop_app/widgets/financial_data.py index 261dbf3..3c7e7fa 100644 --- a/src/osbridgelcca/desktop_app/widgets/financial_data.py +++ b/src/osbridgelcca/desktop_app/widgets/financial_data.py @@ -226,13 +226,13 @@ def __init__(self, database, parent=None): grid_layout.addWidget(suggested3, 2, 3, alignment=Qt.AlignVCenter) # 4. Duration of Study - label4 = QLabel("Duration of Study") + label4 = QLabel("Design Life") label4.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input4 = QLineEdit() self.widgets.append(input4) input4.setAlignment(Qt.AlignmentFlag.AlignLeft) input4.setFixedWidth(field_width) - input4.setText("50 & 100") + input4.setText("50") input4.setStyleSheet(input1.styleSheet()) unit4 = QLabel("(years)") suggested4 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") @@ -256,6 +256,21 @@ def __init__(self, database, parent=None): grid_layout.addWidget(unit5, 4, 2, alignment=Qt.AlignVCenter) grid_layout.addWidget(QWidget(), 4, 3) # Empty cell for suggested + # 6. Analysis Period + label5 = QLabel("Analysis Period") + label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + input5 = QLineEdit() + self.widgets.append(input5) + input5.setAlignment(Qt.AlignmentFlag.AlignLeft) + input5.setFixedWidth(field_width) + input5.setText("50") + input5.setStyleSheet(input1.styleSheet()) + unit5 = QLabel("(years)") + grid_layout.addWidget(label5, 5, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input5, 5, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit5, 5, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(QWidget(), 5, 3) # Empty cell for suggested + self.general_layout.addLayout(grid_layout) self.general_layout.addStretch(1) self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) @@ -296,13 +311,21 @@ def close_widget(self): def collect_data(self): data = [] for widget in self.widgets: - print(f"Collecting data from widget: {widget}") if isinstance(widget, QComboBox): value = widget.currentText() elif isinstance(widget, QLineEdit): value = widget.text() if widget.text() != "" else "0" - data.append(value) - print("Collected Data from UI:",data) + data.append(value) + # percentage + data[0] = float(data[0])/100 + data[1] = float(data[1])/100 + self.database_manager.analysis_period = float(data[5]) + + # save discount_rate and design_life + self.database_manager.discount_rate = data[0] + self.database_manager.design_life = float(data[3]) + + print("Collected Data from UI:",data) # calculate time cost total_initial_construction_cost = self.parent.results.get(COST_TOTAL_INIT_CONST) diff --git a/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py b/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py index 6a2e332..3991934 100644 --- a/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py +++ b/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py @@ -181,7 +181,7 @@ def __init__(self, database, parent=None): self.widget.append(pmc_input) pmc_input.setAlignment(Qt.AlignmentFlag.AlignTop) pmc_input.setFixedWidth(field_width) - pmc_input.setText("0.555") + pmc_input.setText("0.55") pmc_input.setStyleSheet(""" QLineEdit { border: 1px solid #DDDCE0; @@ -284,6 +284,26 @@ def __init__(self, database, parent=None): grid_layout.addWidget(fri_unit, 4, 2, alignment=Qt.AlignVCenter) grid_layout.addWidget(fri_suggested, 4, 3, alignment=Qt.AlignVCenter) + # 6. Frequency of Major Repairs + label5 = QLabel("Frequency of Major Repairs") + label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + fri_input = QLineEdit() + self.widget.append(fri_input) + fri_input.setAlignment(Qt.AlignmentFlag.AlignTop) + fri_input.setFixedWidth(field_width) + fri_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + fri_unit = QLabel("(years)") + grid_layout.addWidget(label5, 5, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_input, 5, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_unit, 5, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(QLabel(), 5, 3, alignment=Qt.AlignVCenter) + self.general_layout.addLayout(grid_layout) self.general_layout.addStretch(1) self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) @@ -330,6 +350,10 @@ def collect_data(self): elif isinstance(widget, QLineEdit): value = widget.text() if widget.text() != "" else "0" data.append(value) + + data[0] = float(data[0])/100 + data[1] = float(data[1])/100 + data[2] = float(data[2])/100 print("Collected Data from UI:",data) @@ -340,17 +364,17 @@ def collect_data(self): self.parent.results[COST_PERIODIC_MAINTAINANCE] = cost # Routine Inspection Cost Calculation - cost = self.database_manager.routine_inspection_cost(total_initial_construction_cost) + cost = self.database_manager.routine_inspection_cost(data, total_initial_construction_cost) # Update Results Dict self.parent.results[COST_TOTAL_ROUTINE_INSPECTION] = cost # Repair and Rehabilitation Cost Calculation - cost = self.database_manager.repair_and_rehabilitation_cost(total_initial_construction_cost) + cost = self.database_manager.repair_and_rehabilitation_cost(total_initial_construction_cost, data) # Update Results Dict self.parent.results[COST_REPAIR_REHAB] = cost # Periodic Maintenance Carbon Emission Cost Calculation (Concrete only) - cost = self.database_manager.periodic_maintainnce_carbon_emission_cost(data) + cost = self.database_manager.periodic_maintainance_carbon_emission_cost(data) # Update Results Dict self.parent.results[COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION] = cost diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py index 1e10518..3167490 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py @@ -17,6 +17,21 @@ def __init__(self, parent): self.init_ui() self._initializing = False + def set_locked(self, locked): + """Enable or disable all input widgets based on lock state""" + self.component_combobox.setEnabled(not locked) + self.add_material_button.setEnabled(not locked) + self.remove_component_button.setEnabled(not locked) + + for row in self.material_rows: + row[KEY_TYPE].setEnabled(not locked) + row[KEY_GRADE].setEnabled(not locked) + row[KEY_QUANTITY].setEnabled(not locked) + row[KEY_UNIT_M3].setEnabled(not locked) + row[KEY_RATE].setEnabled(not locked) + row[KEY_RATE_DATA_SOURCE].setEnabled(not locked) + row['remove_button'].setEnabled(not locked) + def collect_data(self): rows_data = [] for row in self.material_rows: @@ -242,9 +257,14 @@ def add_material_row(self): self.material_rows.append(row_widgets) self.current_material_row_idx += 1 + + if materials: + first_material = materials[0] + self.update_comp_grades(first_material, grade_combo) + self.update_comp_units(first_material, unit_combo_m3) + self.updateGeometry() self.adjustSize() - self._on_type_material_changed(type_material_combo.currentText(), grade_combo, unit_combo_m3) self._on_value_changed() def remove_material_row_by_widgets(self, row_widgets_to_remove): @@ -314,9 +334,11 @@ def __init__(self, database, parent=None): self.setObjectName("central_panel_widget") self.component_widgets = [] self._initializing = True - self.state_changed = False - # To store Id'd of current data + self.state_changed = True self.data_id = [] + self.is_first_visit = True + self.is_locked = False + self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -443,6 +465,30 @@ def __init__(self, database, parent=None): border-color: #A0A0A0; } + QPushButton#lock_button { + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 12px; + color: #3F3E5E; + padding: 2px 2px; + text-align: center; + font-weight: bold; + } + QPushButton#lock_button:hover { + background-color: #F8F8F8; + border-color: #C0C0C0; + } + QPushButton#lock_button[locked="true"] { + background-color: #FFE0E0; + border-color: #FF9999; + color: #CC0000; + } + QPushButton#lock_button[locked="false"] { + background-color: #E0FFE0; + border-color: #99FF99; + color: #00AA00; + } + QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; @@ -487,6 +533,10 @@ def __init__(self, database, parent=None): border: 1px solid #DDDCE0; background-color: #FFFFFF; } + #MaterialGridInput:disabled { + background-color: #F0F0F0; + color: #888888; + } QPushButton#add_material_button, QPushButton#add_component_button { background-color: #FFFFFF; @@ -504,6 +554,11 @@ def __init__(self, database, parent=None): background-color: #E8E8E8; border-color: #A0A0A0; } + QPushButton#add_material_button:disabled, QPushButton#add_component_button:disabled { + background-color: #F0F0F0; + color: #AAAAAA; + border-color: #D0D0D0; + } """) left_panel_vlayout = QVBoxLayout(self) @@ -522,6 +577,20 @@ def __init__(self, database, parent=None): self.scroll_content_layout.setContentsMargins(0, 0, 0, 0) self.scroll_content_layout.setSpacing(0) + lock_hlayout = QHBoxLayout() + lock_hlayout.setContentsMargins(0,2,2,0) + lock_hlayout.setSpacing(0) + lock_hlayout.addStretch() + + self.lock_button = QPushButton("🔓") + self.lock_button.setObjectName("lock_button") + self.lock_button.setFixedSize(24, 24) + self.lock_button.setProperty("locked", "false") + self.lock_button.clicked.connect(self.toggle_lock) + lock_hlayout.addWidget(self.lock_button) + + self.scroll_content_layout.addLayout(lock_hlayout) + self.add_component_button = QPushButton("+ Add Component") self.add_component_button.setObjectName("add_component_button") self.add_component_button.clicked.connect(self.add_component_layout) @@ -547,6 +616,34 @@ def __init__(self, database, parent=None): left_panel_vlayout.addWidget(self.scroll_area) self._initializing = False + def showEvent(self, event): + super().showEvent(event) + if not self.is_first_visit: + self.set_form_locked(True) + else: + self.is_first_visit = False + + def toggle_lock(self): + self.set_form_locked(not self.is_locked) + + def set_form_locked(self, locked): + self.is_locked = locked + + if locked: + self.lock_button.setText("🔒") + self.lock_button.setProperty("locked", "true") + else: + self.lock_button.setText("🔓") + self.lock_button.setProperty("locked", "false") + + self.lock_button.style().unpolish(self.lock_button) + self.lock_button.style().polish(self.lock_button) + + for component_widget in self.component_widgets: + component_widget.set_locked(locked) + + self.add_component_button.setEnabled(not locked) + def add_component_layout(self): new_component = ComponentWidget(self) self.component_widgets.append(new_component) @@ -563,6 +660,9 @@ def add_component_layout(self): self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) self.scroll_content_layout.addLayout(self.button_h_layout) + if self.is_locked: + new_component.set_locked(True) + self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() self.mark_state_changed() @@ -614,6 +714,7 @@ def on_next_clicked(self): if reply == QMessageBox.StandardButton.Yes: self.save_data() self.next.emit(KEY_AUXILIARY) + def expand_scroll_area(self): self.central_widget.layout().invalidate() diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py index 6a7202f..abed294 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py @@ -12,18 +12,17 @@ def __init__(self, parent=None): self.parent_widget = parent self._initializing = True self.data = construction_materials.get(KEY_FOUNDATION) - self.material_rows = [] # To store references to widgets in each material row - self.current_material_row_idx = 1 # Start index for material rows (0 is header) + self.material_rows = [] + self.current_material_row_idx = 1 self.init_ui() self._initializing = False def init_ui(self): - self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout = QVBoxLayout(self) self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) self.component_first_scroll_content_layout.setSpacing(10) - # Component Label and Dropdown with a remove button component_header_layout = QHBoxLayout() component_label = QLabel("Component:") component_label.setContentsMargins(0, 5, 0, 5) @@ -36,7 +35,6 @@ def init_ui(self): self.component_combobox.setContentsMargins(0, 5, 0, 5) component_header_layout.addWidget(self.component_combobox) - # Add a remove button for the component self.remove_component_button = QPushButton("x") self.remove_component_button.setFixedSize(24, 24) self.remove_component_button.setStyleSheet(""" @@ -62,12 +60,10 @@ def init_ui(self): self.component_first_scroll_content_layout.addLayout(component_header_layout) - # --- Material Details Grid Layout --- self.material_grid_layout = QGridLayout() self.material_grid_layout.setHorizontalSpacing(10) self.material_grid_layout.setVerticalSpacing(5) - # Header Row headers = ["Type of Material", "Grade", "Quantity", "Unit", "Rate", "Rate Data Source"] for col, header_text in enumerate(headers): label = QLabel(header_text) @@ -75,23 +71,32 @@ def init_ui(self): label.setObjectName("MaterialGridLabel") self.material_grid_layout.addWidget(label, 0, col) - # Column stretch factors are removed as all input widgets will now have fixed widths. - # This allows the grid to size columns based on the fixed widget sizes and spacing. - self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) - # Add initial two material rows self.add_material_row() self.add_material_row() self.update_comp_material(self.component_combobox.currentText()) - # --- Add Material Button --- self.add_material_button = QPushButton("+ Add Material") self.add_material_button.setObjectName("add_material_button") self.add_material_button.clicked.connect(self.add_material_row) self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) + def set_locked(self, locked): + self.component_combobox.setEnabled(not locked) + self.add_material_button.setEnabled(not locked) + self.remove_component_button.setEnabled(not locked) + + for row in self.material_rows: + row[KEY_TYPE].setEnabled(not locked) + row[KEY_GRADE].setEnabled(not locked) + row[KEY_QUANTITY].setEnabled(not locked) + row[KEY_UNIT_M3].setEnabled(not locked) + row[KEY_RATE].setEnabled(not locked) + row[KEY_RATE_DATA_SOURCE].setEnabled(not locked) + row['remove_button'].setEnabled(not locked) + def collect_data(self): rows_data = [] for row in self.material_rows: @@ -152,15 +157,12 @@ def update_comp_units(self, selected_material, widget): def add_material_row(self): validator = QDoubleValidator() - validator.setRange(0.0, 999999.99, 2) + validator.setRange(0.0, 9999999.999, 3) validator.setBottom(0.0) validator.setNotation(QDoubleValidator.Notation.StandardNotation) row_widgets = {} row_idx = self.current_material_row_idx - - # Set fixed width for all input widgets to 80px, as requested. - # Note: This might make wider text truncate or appear cramped depending on content. fixed_input_width = 80 type_material_combo = QComboBox() @@ -187,8 +189,8 @@ def add_material_row(self): unit_combo_m3 = QComboBox() unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width) # Previously 80, now consistent with others - self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) # Directly add, no extra layout + unit_combo_m3.setFixedWidth(fixed_input_width) + self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) row_widgets[KEY_UNIT_M3] = unit_combo_m3 unit_combo_m3.currentTextChanged.connect(self._on_value_changed) @@ -238,9 +240,18 @@ def add_material_row(self): self.material_rows.append(row_widgets) self.current_material_row_idx += 1 - self.updateGeometry() # Call updateGeometry on self, not on the scroll content widget - self.adjustSize() # Adjust the size of the component widget - self._on_type_material_changed(type_material_combo.currentText(), grade_combo, unit_combo_m3) + + selected_component = self.component_combobox.currentText() + materials = list(self.data.get(selected_component, {}).keys()) + type_material_combo.addItems(materials) + + if materials: + first_material = materials[0] + self.update_comp_grades(first_material, grade_combo) + self.update_comp_units(first_material, unit_combo_m3) + + self.updateGeometry() + self.adjustSize() self._on_value_changed() @@ -251,7 +262,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): row_idx_in_grid = -1 for i, row_dict in enumerate(self.material_rows): if row_dict == row_widgets_to_remove: - row_idx_in_grid = i + 1 # +1 because row 0 is header + row_idx_in_grid = i + 1 break if row_idx_in_grid == -1: @@ -288,10 +299,10 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): self.material_grid_layout.removeItem(layout) self.material_grid_layout.addLayout(layout, r_idx, c_idx) - self.updateGeometry() # Call updateGeometry on self - self.update() # Call update on self + self.updateGeometry() + self.update() self.material_grid_layout.invalidate() - self.adjustSize() # Adjust the size of the component widget + self.adjustSize() self._on_value_changed() class Foundation(QWidget): @@ -301,12 +312,14 @@ class Foundation(QWidget): def __init__(self, database, parent=None): super().__init__() self.database_manager = database - # Track inserted comp_id values for replace-on-save self.data_id = [] self.setObjectName("central_panel_widget") - self.component_widgets = [] # To store references to each ComponentWidget instance + self.component_widgets = [] self._initializing = True - self.state_changed = False + self.state_changed = True + self.is_first_visit = True + self.is_locked = False + self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -323,7 +336,6 @@ def __init__(self, database, parent=None): } QScrollArea { - background-color: transparent; outline: none; } @@ -405,38 +417,59 @@ def __init__(self, database, parent=None): border-color: #606060; } - /* Styling for component_first_widget (the container for the nested scroll area) */ #component_first_widget { background-color: transparent; - margin-top: 10px; /* Add some space above each component block */ + margin-top: 10px; } - #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + #component_first_scroll_content_widget { background-color: #FFFFFF; padding: 10px; - border-radius: 8px; } - /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ QPushButton#nav_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; - min-width: 80px; /* Ensure a minimum width */ + min-width: 80px; } QPushButton#nav_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#nav_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; + } + + QPushButton#lock_button { + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 12px; + color: #3F3E5E; + padding: 2px 2px; + text-align: center; + font-weight: bold; + } + QPushButton#lock_button:hover { + background-color: #F8F8F8; + border-color: #C0C0C0; } - /* Styling for QComboBox */ + QPushButton#lock_button[locked="true"] { + background-color: #FFE0E0; + border-color: #FF9999; + color: #CC0000; + } + QPushButton#lock_button[locked="false"] { + background-color: #E0FFE0; + border-color: #99FF99; + color: #00AA00; + } + QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; @@ -465,7 +498,6 @@ def __init__(self, database, parent=None): background-color: #FDEFEF; } - /* Styling for material grid elements */ #MaterialGridLabel { font-weight: bold; color: #3F3E5E; @@ -482,28 +514,50 @@ def __init__(self, database, parent=None): border: 1px solid #DDDCE0; background-color: #FFFFFF; } - /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ + #MaterialGridInput:disabled { + background-color: #F0F0F0; + color: #888888; + } + QPushButton#add_material_button, QPushButton#add_component_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; } QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; + } + QPushButton#add_material_button:disabled, QPushButton#add_component_button:disabled { + background-color: #F0F0F0; + color: #AAAAAA; + border-color: #D0D0D0; } """) + left_panel_vlayout = QVBoxLayout(self) - left_panel_vlayout.setContentsMargins(0, 0, 0, 0) + left_panel_vlayout.setContentsMargins(0,0,0,0) left_panel_vlayout.setSpacing(0) + lock_hlayout = QHBoxLayout() + lock_hlayout.setContentsMargins(0,2,2,0) + lock_hlayout.setSpacing(0) + lock_hlayout.addStretch() + + self.lock_button = QPushButton("🔓") + self.lock_button.setObjectName("lock_button") + self.lock_button.setFixedSize(24, 24) + self.lock_button.setProperty("locked", "false") + self.lock_button.clicked.connect(self.toggle_lock) + lock_hlayout.addWidget(self.lock_button) + self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) @@ -516,32 +570,57 @@ def __init__(self, database, parent=None): self.scroll_content_layout.setContentsMargins(0,0,0,0) self.scroll_content_layout.setSpacing(0) - # Create the Add Component button and connect it + self.scroll_content_layout.addLayout(lock_hlayout) + self.add_component_button = QPushButton("+ Add Component") self.add_component_button.setObjectName("add_component_button") self.add_component_button.clicked.connect(self.add_component_layout) - # Create the navigation buttons layout self.button_h_layout = QHBoxLayout() self.button_h_layout.setSpacing(10) self.button_h_layout.setContentsMargins(10,10,10,10) - # Adjust these stretch factors to control the position - self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + self.button_h_layout.addStretch(6) next_button = QPushButton("Next") next_button.setObjectName("nav_button") next_button.clicked.connect(self.on_next_clicked) self.button_h_layout.addWidget(next_button) - # Add the initial component layout self.add_component_layout() - # Add initial spacing before the navigation buttons self.scroll_content_layout.addLayout(self.button_h_layout) left_panel_vlayout.addWidget(self.scroll_area) self._initializing = False + def showEvent(self, event): + super().showEvent(event) + if not self.is_first_visit: + self.set_form_locked(True) + else: + self.is_first_visit = False + + def toggle_lock(self): + self.set_form_locked(not self.is_locked) + + def set_form_locked(self, locked): + self.is_locked = locked + + if locked: + self.lock_button.setText("🔒") + self.lock_button.setProperty("locked", "true") + else: + self.lock_button.setText("🔓") + self.lock_button.setProperty("locked", "false") + + self.lock_button.style().unpolish(self.lock_button) + self.lock_button.style().polish(self.lock_button) + + for component_widget in self.component_widgets: + component_widget.set_locked(locked) + + self.add_component_button.setEnabled(not locked) + def collect_data(self): all_data = [] for component_widget in self.component_widgets: @@ -581,21 +660,18 @@ def add_component_layout(self): self.component_widgets.append(new_component) new_component.remove_component_button.clicked.connect(lambda: self.remove_component_layout(new_component)) - # Temporarily remove button_h_layout and add_component_button for insertion if self.scroll_content_layout.indexOf(self.add_component_button) != -1: self.scroll_content_layout.removeWidget(self.add_component_button) if self.scroll_content_layout.indexOf(self.button_h_layout) != -1: self.scroll_content_layout.removeItem(self.button_h_layout) - # Insert the new component self.scroll_content_layout.addWidget(new_component) - - # Re-add the 'Add Component' button self.scroll_content_layout.addWidget(self.add_component_button, alignment=Qt.AlignCenter) - - # Re-add the navigation buttons layout self.scroll_content_layout.addLayout(self.button_h_layout) + if self.is_locked: + new_component.set_locked(True) + self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() self.mark_state_changed() @@ -614,31 +690,4 @@ def expand_scroll_area(self): def close_widget(self): self.closed.emit() - self.setParent(None) - -#----------------Standalone-Test-Code-------------------------------- - -# class MyMainWindow(QMainWindow): -# def __init__(self): -# super().__init__() - -# self.setStyleSheet("border: none") - -# self.central_widget = QWidget() -# self.central_widget.setObjectName("central_widget") -# self.setCentralWidget(self.central_widget) - -# self.main_h_layout = QHBoxLayout(self.central_widget) -# self.main_h_layout.addStretch(1) - -# self.main_h_layout.addWidget(Foundation(), 2) - -# self.setWindowState(Qt.WindowMaximized) - - -# if __name__ == "__main__": -# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) -# app = QApplication(sys.argv) -# window = MyMainWindow() -# window.show() -# sys.exit(app.exec()) \ No newline at end of file + self.setParent(None) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py index 250aef5..a52a468 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py @@ -12,13 +12,26 @@ def __init__(self, parent=None): self.parent_widget = parent self._initializing = True self.data = construction_materials.get(KEY_SUBSTRUCTURE) - self.material_rows = [] self.current_material_row_idx = 1 self.init_ui() self._initializing = False + def set_locked(self, locked): + """Enable or disable all input widgets based on lock state""" + self.component_combobox.setEnabled(not locked) + self.add_material_button.setEnabled(not locked) + self.remove_component_button.setEnabled(not locked) + + for row in self.material_rows: + row[KEY_TYPE].setEnabled(not locked) + row[KEY_GRADE].setEnabled(not locked) + row[KEY_QUANTITY].setEnabled(not locked) + row[KEY_UNIT_M3].setEnabled(not locked) + row[KEY_RATE].setEnabled(not locked) + row[KEY_RATE_DATA_SOURCE].setEnabled(not locked) + row['remove_button'].setEnabled(not locked) def collect_data(self): rows_data = [] @@ -227,9 +240,18 @@ def add_material_row(self): self.material_rows.append(row_widgets) self.current_material_row_idx += 1 + + selected_component = self.component_combobox.currentText() + materials = list(self.data.get(selected_component, {}).keys()) + type_material_combo.addItems(materials) + + if materials: + first_material = materials[0] + self.update_comp_grades(first_material, grade_combo) + self.update_comp_units(first_material, unit_combo_m3) + self.updateGeometry() self.adjustSize() - self._on_type_material_changed(type_material_combo.currentText(), grade_combo, unit_combo_m3) self._on_value_changed() @@ -290,12 +312,14 @@ class SubStructure(QWidget): def __init__(self, database, parent=None): super().__init__() self.database_manager = database - # Track inserted comp_id values for replace-on-save self.data_id = [] self.setObjectName("central_panel_widget") self.component_widgets = [] self._initializing = True - self.state_changed = False + self.state_changed = True + self.is_first_visit = True + self.is_locked = False + self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -312,7 +336,6 @@ def __init__(self, database, parent=None): } QScrollArea { - background-color: transparent; outline: none; } @@ -402,7 +425,6 @@ def __init__(self, database, parent=None): #component_first_scroll_content_widget { background-color: #FFFFFF; padding: 10px; - border-radius: 8px; } @@ -423,6 +445,31 @@ def __init__(self, database, parent=None): background-color: #E8E8E8; border-color: #A0A0A0; } + + QPushButton#lock_button { + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 12px; + color: #3F3E5E; + padding: 2px 2px; + text-align: center; + font-weight: bold; + } + QPushButton#lock_button:hover { + background-color: #F8F8F8; + border-color: #C0C0C0; + } + QPushButton#lock_button[locked="true"] { + background-color: #FFE0E0; + border-color: #FF9999; + color: #CC0000; + } + QPushButton#lock_button[locked="false"] { + background-color: #E0FFE0; + border-color: #99FF99; + color: #00AA00; + } + QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; @@ -467,6 +514,11 @@ def __init__(self, database, parent=None): border: 1px solid #DDDCE0; background-color: #FFFFFF; } + #MaterialGridInput:disabled { + background-color: #F0F0F0; + color: #888888; + } + QPushButton#add_material_button, QPushButton#add_component_button { background-color: #FFFFFF; border: 1px solid #E0E0E0; @@ -483,7 +535,13 @@ def __init__(self, database, parent=None): background-color: #E8E8E8; border-color: #A0A0A0; } + QPushButton#add_material_button:disabled, QPushButton#add_component_button:disabled { + background-color: #F0F0F0; + color: #AAAAAA; + border-color: #D0D0D0; + } """) + left_panel_vlayout = QVBoxLayout(self) left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) @@ -500,6 +558,20 @@ def __init__(self, database, parent=None): self.scroll_content_layout.setContentsMargins(0,0,0,0) self.scroll_content_layout.setSpacing(0) + lock_hlayout = QHBoxLayout() + lock_hlayout.setContentsMargins(0,2,2,0) + lock_hlayout.setSpacing(0) + lock_hlayout.addStretch() + + self.lock_button = QPushButton("🔓") + self.lock_button.setObjectName("lock_button") + self.lock_button.setFixedSize(24, 24) + self.lock_button.setProperty("locked", "false") + self.lock_button.clicked.connect(self.toggle_lock) + lock_hlayout.addWidget(self.lock_button) + + self.scroll_content_layout.addLayout(lock_hlayout) + self.add_component_button = QPushButton("+ Add Component") self.add_component_button.setObjectName("add_component_button") self.add_component_button.clicked.connect(self.add_component_layout) @@ -526,6 +598,34 @@ def __init__(self, database, parent=None): left_panel_vlayout.addWidget(self.scroll_area) self._initializing = False + def showEvent(self, event): + super().showEvent(event) + if not self.is_first_visit: + self.set_form_locked(True) + else: + self.is_first_visit = False + + def toggle_lock(self): + self.set_form_locked(not self.is_locked) + + def set_form_locked(self, locked): + self.is_locked = locked + + if locked: + self.lock_button.setText("🔒") + self.lock_button.setProperty("locked", "true") + else: + self.lock_button.setText("🔓") + self.lock_button.setProperty("locked", "false") + + self.lock_button.style().unpolish(self.lock_button) + self.lock_button.style().polish(self.lock_button) + + for component_widget in self.component_widgets: + component_widget.set_locked(locked) + + self.add_component_button.setEnabled(not locked) + def add_component_layout(self): new_component = ComponentWidget(self) self.component_widgets.append(new_component) @@ -542,6 +642,9 @@ def add_component_layout(self): self.scroll_content_layout.addLayout(self.button_h_layout) + if self.is_locked: + new_component.set_locked(True) + self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() self.mark_state_changed() diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py index f4d95d0..19fe4d6 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py @@ -17,6 +17,21 @@ def __init__(self, parent=None): self.init_ui() self._initializing = False + + def set_locked(self, locked): + """Enable or disable all input widgets based on lock state""" + self.component_combobox.setEnabled(not locked) + self.add_material_button.setEnabled(not locked) + self.remove_component_button.setEnabled(not locked) + + for row in self.material_rows: + row[KEY_TYPE].setEnabled(not locked) + row[KEY_GRADE].setEnabled(not locked) + row[KEY_QUANTITY].setEnabled(not locked) + row[KEY_UNIT_M3].setEnabled(not locked) + row[KEY_RATE].setEnabled(not locked) + row[KEY_RATE_DATA_SOURCE].setEnabled(not locked) + row['remove_button'].setEnabled(not locked) def collect_data(self): rows_data = [] @@ -156,7 +171,7 @@ def add_material_row(self): type_material_combo.setObjectName("MaterialGridInput") type_material_combo.setFixedWidth(fixed_input_width) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) - row_widgets[KEY_TYPE] = type_material_combo # CHANGE 7: Changed key to KEY_TYPE + row_widgets[KEY_TYPE] = type_material_combo grade_combo = QComboBox() grade_combo.setObjectName("MaterialGridInput") @@ -227,9 +242,18 @@ def add_material_row(self): self.material_rows.append(row_widgets) self.current_material_row_idx += 1 + + selected_component = self.component_combobox.currentText() + materials = list(self.data.get(selected_component, {}).keys()) + type_material_combo.addItems(materials) + + if materials: + first_material = materials[0] + self.update_comp_grades(first_material, grade_combo) + self.update_comp_units(first_material, unit_combo_m3) + self.updateGeometry() self.adjustSize() - self._on_type_material_changed(type_material_combo.currentText(), grade_combo, unit_combo_m3) self._on_value_changed() @@ -290,12 +314,14 @@ class SuperStructure(QWidget): def __init__(self, database, parent=None): super().__init__() self.database_manager = database - # Track inserted comp_id values for replace-on-save self.data_id = [] self.setObjectName("central_panel_widget") self.component_widgets = [] self._initializing = True - self.state_changed = False + self.state_changed = True + self.is_first_visit = True + self.is_locked = False + self.setStyleSheet(""" #central_panel_widget { background-color: #F8F8F8; @@ -312,7 +338,6 @@ def __init__(self, database, parent=None): } QScrollArea { - background-color: transparent; outline: none; } @@ -402,7 +427,6 @@ def __init__(self, database, parent=None): #component_first_scroll_content_widget { background-color: #FFFFFF; padding: 10px; - border-radius: 8px; } @@ -423,6 +447,31 @@ def __init__(self, database, parent=None): background-color: #E8E8E8; border-color: #A0A0A0; } + + QPushButton#lock_button { + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 12px; + color: #3F3E5E; + padding: 2px 2px; + text-align: center; + font-weight: bold; + } + QPushButton#lock_button:hover { + background-color: #F8F8F8; + border-color: #C0C0C0; + } + QPushButton#lock_button[locked="true"] { + background-color: #FFE0E0; + border-color: #FF9999; + color: #CC0000; + } + QPushButton#lock_button[locked="false"] { + background-color: #E0FFE0; + border-color: #99FF99; + color: #00AA00; + } + QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; @@ -467,6 +516,11 @@ def __init__(self, database, parent=None): border: 1px solid #DDDCE0; background-color: #FFFFFF; } + #MaterialGridInput:disabled { + background-color: #F0F0F0; + color: #888888; + } + QPushButton#add_material_button, QPushButton#add_component_button { background-color: #FFFFFF; border: 1px solid #E0E0E0; @@ -483,7 +537,13 @@ def __init__(self, database, parent=None): background-color: #E8E8E8; border-color: #A0A0A0; } + QPushButton#add_material_button:disabled, QPushButton#add_component_button:disabled { + background-color: #F0F0F0; + color: #AAAAAA; + border-color: #D0D0D0; + } """) + left_panel_vlayout = QVBoxLayout(self) left_panel_vlayout.setContentsMargins(0, 0, 0, 0) left_panel_vlayout.setSpacing(0) @@ -500,6 +560,20 @@ def __init__(self, database, parent=None): self.scroll_content_layout.setContentsMargins(0,0,0,0) self.scroll_content_layout.setSpacing(0) + lock_hlayout = QHBoxLayout() + lock_hlayout.setContentsMargins(0,2,2,0) + lock_hlayout.setSpacing(0) + lock_hlayout.addStretch() + + self.lock_button = QPushButton("🔓") + self.lock_button.setObjectName("lock_button") + self.lock_button.setFixedSize(24, 24) + self.lock_button.setProperty("locked", "false") + self.lock_button.clicked.connect(self.toggle_lock) + lock_hlayout.addWidget(self.lock_button) + + self.scroll_content_layout.addLayout(lock_hlayout) + self.add_component_button = QPushButton("+ Add Component") self.add_component_button.setObjectName("add_component_button") self.add_component_button.clicked.connect(self.add_component_layout) @@ -526,6 +600,34 @@ def __init__(self, database, parent=None): left_panel_vlayout.addWidget(self.scroll_area) self._initializing = False + def showEvent(self, event): + super().showEvent(event) + if not self.is_first_visit: + self.set_form_locked(True) + else: + self.is_first_visit = False + + def toggle_lock(self): + self.set_form_locked(not self.is_locked) + + def set_form_locked(self, locked): + self.is_locked = locked + + if locked: + self.lock_button.setText("🔒") + self.lock_button.setProperty("locked", "true") + else: + self.lock_button.setText("🔓") + self.lock_button.setProperty("locked", "false") + + self.lock_button.style().unpolish(self.lock_button) + self.lock_button.style().polish(self.lock_button) + + for component_widget in self.component_widgets: + component_widget.set_locked(locked) + + self.add_component_button.setEnabled(not locked) + def add_component_layout(self): new_component = ComponentWidget(self) self.component_widgets.append(new_component) @@ -542,6 +644,9 @@ def add_component_layout(self): self.scroll_content_layout.addLayout(self.button_h_layout) + if self.is_locked: + new_component.set_locked(True) + self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() self.mark_state_changed() diff --git a/src/osbridgelcca/desktop_app/widgets/title_bar.py b/src/osbridgelcca/desktop_app/widgets/title_bar.py index a19aefe..2df9a01 100644 --- a/src/osbridgelcca/desktop_app/widgets/title_bar.py +++ b/src/osbridgelcca/desktop_app/widgets/title_bar.py @@ -57,7 +57,7 @@ def __init__(self, parent=None): self.layout.addWidget(self.icon_label) # Middle: Title (centered) - self.title_label = QLabel("My Custom App") + self.title_label = QLabel("Life Cycle Cost Analysis") self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.title_label.setStyleSheet("font-weight: bold;") self.layout.addWidget(self.title_label, 1) # Add stretch factor of 1 to center the title diff --git a/src/osbridgelcca/desktop_app/widgets/utils/data.py b/src/osbridgelcca/desktop_app/widgets/utils/data.py index 2cd282a..9e92cc0 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/data.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/data.py @@ -25,7 +25,7 @@ KEY_ROADROUGHNESS = "road_roughness" KEY_ROAD_RISE_AND_FALL = "road_rise_and_fall" KEY_TYPE_OF_ROAD = "type_of_road" -KEY_ANNUAL_INCREASE = "annual_increase" +KEY_ACCIDENT_CAT = "annual_increase" KEY_EMBODIED_CARBON_ENERGY = "embodied_carbon_energy" @@ -47,17 +47,6 @@ construction_materials = { KEY_FOUNDATION: { - "Pile": { - "Steel Rebar": { - KEY_GRADE: ["Fe415", "Fe500", "Fe550"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] - }, - "Reinforced Cement Concrete": { - KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", - "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] - } - }, "Excavation": { "Rock": { KEY_GRADE: [], @@ -108,6 +97,17 @@ KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] }, }, + "Pile": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + } + }, "Pile Cap": { "Steel Rebar": { KEY_GRADE: ["Fe415", "Fe500", "Fe550"], @@ -118,7 +118,7 @@ "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] } - } + } }, KEY_SUBSTRUCTURE: { @@ -278,8 +278,14 @@ KEY_BRIDGE_TRAFFIC: { KEY_LANES: { KEY_OPTIONS: [ - "2", - "4" + "Single Lane Roads", + "Intermediate Lane Roads", + "Two Lane Roads", + "Four Lane Divided Roads", + "Six Lane Divided Roads", + "Four Lane Divided Expressways", + "Six Lane Divided Expressways", + "Eight Lane Divided Urban Expressways" ], }, @@ -297,14 +303,12 @@ KEY_TYPE_OF_ROAD: { KEY_OPTIONS: [ "Urban Road", - "Rural Road", - "Highway", - "Expressway" + "Rural Road" ], }, - KEY_ANNUAL_INCREASE: { - KEY_OPTIONS: ["8", "9", "10", "12"], + KEY_ACCIDENT_CAT: { + KEY_OPTIONS: ["Minor Injury", "Major Injury", "Fatal"], }, } diff --git a/src/osbridgelcca/desktop_app/widgets/utils/database.py b/src/osbridgelcca/desktop_app/widgets/utils/database.py index 5167fa4..ba02371 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/database.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/database.py @@ -22,7 +22,9 @@ def __init__(self, db_path: str = "widgets/utils/structure_works.db", recreate: self.db_path = db_path self.conn = None self.create_database(recreate=recreate) - self.carbon_cost = 6.3936 + self.discount_rate = 0 + self.design_life = 0 + self.analysis_period = 0 def create_database(self, recreate: bool = True): """ @@ -234,6 +236,7 @@ def replace_structure_work_rows(self, work_type: str, rows_data: List[Dict], old if old_comp_ids: for comp_id in old_comp_ids: self.delete_structure_work(comp_id) + self.delete_component(comp_id) return self.input_data_row(work_type, rows_data) @@ -246,7 +249,7 @@ def delete_structure_work(self, comp_id: int): def delete_component(self, component_id: int): """Delete a specific component by its ID""" cursor = self.conn.cursor() - cursor.execute('DELETE FROM component WHERE id = ?', (component_id,)) + cursor.execute('DELETE FROM component WHERE comp_id = ?', (component_id,)) self.conn.commit() def get_all_structure_works(self) -> List[Tuple]: @@ -432,7 +435,7 @@ def close(self): #========================Calculations======================== - # 1. Called on next from Auxiliary Cost Data.py + #(OK) 1. Called on next from Auxiliary Cost Data.py def calculate_total_initial_cost(self) -> float: data = self.get_all_materials_info() total_cost = 0.0 @@ -445,7 +448,7 @@ def calculate_total_initial_cost(self) -> float: print("\nTotal Initial Construction Cost:", total_cost) return total_cost - # 3. Called on next from financial_data.py + #(OK) 3. Called on next from financial_data.py def calculate_time_cost(self, data: List, total_init_construct_cost: float) -> float: # Insert financial data into the database self._insert_financial_data(data) @@ -458,35 +461,21 @@ def calculate_time_cost(self, data: List, total_init_construct_cost: float) -> f print("\nTime Cost:", time_cost_component.calculate_cost()) return time_cost_component.calculate_cost() - # 2. Called on next from carbon_emission_cost_data.py + #(OK) 2. Called on next from carbon_emission_cost_data.py def carbon_emission_cost(self, carbon_cost: float) -> float: # Updated Carbon cost self.carbon_cost = carbon_cost - unit_conversions = { - "cum": 2549.25, - "kg": 1.0, - "mt": 1000.0, - "rmt": 1.0, - "sqm": 1.0, - "ltr": 1.0 - } - # Get carbon emission data from the database data = self.get_carbon_emission_data() total_carbon_emission_cost= 0.0 for item in data: - total_component = 0.0 qty = float(item.get(KEY_QUANTITY)) - unit = item.get(KEY_UNIT_M3) emission_factor = float(item.get(KEY_CARBON_EMISSION_FACTOR)) - # convert quantity to kg - qty = qty * unit_conversions.get(unit.lower()) - total_component += qty - carbon_emission_cost = (total_component * emission_factor) * carbon_cost; + carbon_emission_cost = (qty * emission_factor) * carbon_cost; total_carbon_emission_cost += carbon_emission_cost - print(f"Carbon Emission Cost for {item.get(KEY_TYPE)}:", total_carbon_emission_cost) + print(f"Carbon Emission Cost for {item.get(KEY_TYPE)}:", carbon_emission_cost) print("\nTotal Carbon Emission Cost:", total_carbon_emission_cost) return total_carbon_emission_cost @@ -615,16 +604,16 @@ def periodic_maintainance_cost(self, data: List, total_initial_cost) -> float: maintenance_cost_rate = PeriodicMaintenanceCost( maintenance_cost_rate=float(data[0]), construction_cost=total_initial_cost, - discount_rate=0.0425, + discount_rate=self.discount_rate, period=float(data[3]), - design_life=50.0 + design_life=self.design_life ) print("\nPeriodic Maintenance Cost:", maintenance_cost_rate.calculate_cost()) # INR return maintenance_cost_rate.calculate_cost() # 7. Periodic Maintenance Carbon Emission Cost Calculation (Concrete only) - def periodic_maintainnce_carbon_emission_cost(self, data: List): + def periodic_maintainance_carbon_emission_cost(self, data: List): unit_conversions = { "cum": 2549.25, "kg": 1.0, @@ -647,37 +636,32 @@ def periodic_maintainnce_carbon_emission_cost(self, data: List): # convert quantity to kg qty = qty * unit_conversions.get(unit.lower()) maintenance_concrete_emission_factor = item.get(KEY_CARBON_EMISSION_FACTOR) - maintenance_concrete_kg += qty + maintenance_concrete_kg += qty - maintenance_carbon_cost = 6.3936 - maintenance_discount_rate = 0.0425 + maintenance_carbon_cost = self.carbon_cost maintenance_period = float(data[3]) - maintenance_design_life = 50.0 periodic_maintenance_concrete_carbon_component = PeriodicMaintenanceCarbonCost( material_quantity=maintenance_concrete_kg, carbon_emission_factor=maintenance_concrete_emission_factor, carbon_cost=maintenance_carbon_cost, - discount_rate=maintenance_discount_rate, + discount_rate=self.discount_rate, period=maintenance_period, - design_life=maintenance_design_life + design_life=self.design_life ) print("\nPeriodic Maintenance Carbon Emission Cost (Concrete only):", periodic_maintenance_concrete_carbon_component.calculate_cost()) # INR return periodic_maintenance_concrete_carbon_component.calculate_cost() # 8. Annual Routine Inspection Cost Calculation - def routine_inspection_cost(self, total_initial_construction_cost: float) -> float: - - inspection_discount_rate = 0.0425 - inspection_design_life = 50.0 + def routine_inspection_cost(self, data: List, total_initial_construction_cost: float) -> float: - inspection_rate = 0.01 - inspection_period = 1.0 + inspection_rate = float(data[1]) + inspection_period = float(data[4]) inspection_component = RoutineInspectionCost( inspection_cost_rate=inspection_rate, # Use inspection_rate as inspection cost rate construction_cost=total_initial_construction_cost, # Always use total_initial_construction_cost - discount_rate=inspection_discount_rate, - design_life=inspection_design_life, + discount_rate=self.discount_rate, + design_life=self.design_life, period=inspection_period # always annual ) total_routine_inspection_cost = inspection_component.calculate_cost() @@ -685,57 +669,74 @@ def routine_inspection_cost(self, total_initial_construction_cost: float) -> flo return total_routine_inspection_cost # 9. Repair and Rehabilitation Cost Calculation - def repair_and_rehabilitation_cost(self, total_initial_construction_cost: float) -> float: - repair_period = 30.0 - repair_cost_rate = 0.10 - - discount_rate = 0.0425 - design_life = 50.0 + def repair_and_rehabilitation_cost(self, total_initial_construction_cost: float, data: List) -> float: + repair_period = data[5] + repair_cost_rate = data[2] repair_component = RepairAndRehabilitationCost( repair_cost_rate=repair_cost_rate, construction_cost=total_initial_construction_cost, - discount_rate=discount_rate, + discount_rate=self.discount_rate, period=repair_period, - design_life=design_life + design_life=self.design_life ) print("\nRepair and Rehabilitation Cost:", repair_component.calculate_cost()) # INR return repair_component.calculate_cost() # 10. Demolition and Disposal Cost Calculation def demolition_and_disposal_cost(self, data: List, total_initial_construction_cost: float) -> float: - demolition_discount_rate = 0.0425 - demolition_design_life = 50.0 - demolition_rate = float(float(data[0])/100) # e.g., 0.05 for 5% + demolition_rate = float(data[0]) demolition_component = DemolitionCost( demolition_rate=demolition_rate, construction_cost=total_initial_construction_cost, - discount_rate=demolition_discount_rate, - design_life=demolition_design_life + discount_rate=self.discount_rate, + design_life=self.design_life ) print("\nDemolition and Disposal Cost:", demolition_component.calculate_cost()) # INR return demolition_component.calculate_cost() # 11. Recycling Cost Calculation def recycling_cost(self, data: List) -> float: - scrap_value = float(data[1]) - recycling_design_life = 50.0 - scrap_rate = 0.98 - - discount_rate = 0.0425 - user_input_steel_quantity_mt = 0.0 + # To convert to MT + unit_conversions = { + "kg": 1000.0, + "mt": 1.0 + } - recycling_component = RecyclingCost( - scrap_value=scrap_value, - quantity=user_input_steel_quantity_mt, - scrap_rate=scrap_rate, - discount_rate=discount_rate, - design_life=recycling_design_life + steel_rebar_quantity = 0 + struct_steel_quantity = 0 + # Get carbon emission data from the database + data = self.get_carbon_emission_data() + for item in data: + material = item.get(KEY_TYPE) + if material == "Steel Rebar": + steel_rebar_quantity += float(item.get(KEY_QUANTITY)) * unit_conversions.get(item.get(KEY_UNIT_M3)) + elif material == "Structural Steel": + struct_steel_quantity += float(item.get(KEY_QUANTITY)) * unit_conversions.get(item.get(KEY_UNIT_M3)) + + # Structural Steel-----------recycling-cost----------------------------------------- + ss_recycling_component = RecyclingCost( + scrap_value=float(data[1]), + quantity=struct_steel_quantity, + scrap_rate=float(data[2]), + discount_rate=self.discount_rate, + design_life=self.design_life + ) + print("\nRecycling Cost (Structural Steel):", ss_recycling_component.calculate_cost()) + + # Steel-rebar-----------------recycling-cost----------------------------------------- + sr_recycling_component = RecyclingCost( + scrap_value=float(data[3]), + quantity=steel_rebar_quantity, + scrap_rate=float(data[4]), + discount_rate=self.discount_rate, + design_life=self.design_life ) - print("\nRecycling Cost (user-input steel only):", recycling_component.calculate_cost()) # INR - return recycling_component.calculate_cost() + print("\nRecycling Cost (Steel Rebar):", sr_recycling_component.calculate_cost()) + + return sr_recycling_component.calculate_cost() + ss_recycling_component.calculate_cost() # 12. Reconstruction Cost Calculation def reconstruction_cost(self, initial_construction_cost: float, @@ -751,15 +752,10 @@ def reconstruction_cost(self, initial_construction_cost: float, reconstruction_time_cost = time_cost reconstruction_roaduser_cost = roaduser_cost reconstruction_rerouting_carbon_cost = rerouting_carbon_cost - reconstruction_design_life = 50.0 reconstruction_result = 0 - analysis_period = 75.0 - - design_life = 50.0 - discount_rate = 0.0425 - if analysis_period > design_life: + if self.analysis_period > self.design_life: reconstruction_component = ReconstructionCost( demolition_cost=demolition_cost, reconstruction_cost=reconstruction_cost, @@ -767,8 +763,8 @@ def reconstruction_cost(self, initial_construction_cost: float, reconstruction_time_cost=reconstruction_time_cost, reconstruction_roaduser_cost=reconstruction_roaduser_cost, reconstruction_rerouting_carbon_cost=reconstruction_rerouting_carbon_cost, - design_life=reconstruction_design_life, - discount_rate=discount_rate + design_life=self.design_life, + discount_rate=self.discount_rate ) reconstruction_result = reconstruction_component.calculate_cost() else: From 366860c44b8fb412a79a1e6862d3f67143d45f2b Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Tue, 25 Nov 2025 21:43:29 +0530 Subject: [PATCH 18/22] -Updated Calculations and ui --- .../desktop_app/bash.exe.stackdump | 28 + src/osbridgelcca/desktop_app/main_template.py | 14 - .../widgets/bridge_and_traffic_data.py | 78 +- .../carbon_emission_cost_data.py | 350 +++++--- .../carbon_emission_data.py | 214 ++--- .../widgets/demolition_and_recycling_data.py | 160 ++-- .../desktop_app/widgets/financial_data.py | 113 +-- .../widgets/maintenance_repair_data.py | 301 ++++--- .../auxiliary_works_widget.py | 32 +- .../structure_works_data/foundation_widget.py | 32 +- .../sub_structure_widget.py | 27 +- .../super_structure_widget.py | 27 +- .../desktop_app/widgets/utils/a.py | 4 + .../widgets/utils/cost_component.py | 496 +++++------ .../desktop_app/widgets/utils/data.py | 176 +++- .../desktop_app/widgets/utils/database.py | 776 +++++++++++------- 16 files changed, 1671 insertions(+), 1157 deletions(-) create mode 100644 src/osbridgelcca/desktop_app/bash.exe.stackdump create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/a.py diff --git a/src/osbridgelcca/desktop_app/bash.exe.stackdump b/src/osbridgelcca/desktop_app/bash.exe.stackdump new file mode 100644 index 0000000..66ca9bf --- /dev/null +++ b/src/osbridgelcca/desktop_app/bash.exe.stackdump @@ -0,0 +1,28 @@ +Stack trace: +Frame Function Args +0007FFFF9F00 00021006118E (00021028DEE8, 000210272B3E, 0007FFFF9F00, 0007FFFF8E00) msys-2.0.dll+0x2118E +0007FFFF9F00 0002100469BA (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x69BA +0007FFFF9F00 0002100469F2 (00021028DF99, 0007FFFF9DB8, 0007FFFF9F00, 000000000000) msys-2.0.dll+0x69F2 +0007FFFF9F00 00021006A41E (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x2A41E +0007FFFF9F00 00021006A545 (0007FFFF9F10, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x2A545 +0001004F94B7 00021006B9A5 (0007FFFF9F10, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x2B9A5 +End of stack trace +Loaded modules: +000100400000 bash.exe +7FFC33540000 ntdll.dll +7FFC328E0000 KERNEL32.DLL +7FFC30A90000 KERNELBASE.dll +7FFC319A0000 USER32.dll +7FFC309A0000 win32u.dll +000210040000 msys-2.0.dll +7FFC33420000 GDI32.dll +7FFC31270000 gdi32full.dll +7FFC30E50000 msvcp_win.dll +7FFC30F00000 ucrtbase.dll +7FFC318E0000 advapi32.dll +7FFC32AC0000 msvcrt.dll +7FFC31570000 sechost.dll +7FFC32B70000 RPCRT4.dll +7FFC2FE20000 CRYPTBASE.DLL +7FFC31050000 bcryptPrimitives.dll +7FFC333D0000 IMM32.DLL diff --git a/src/osbridgelcca/desktop_app/main_template.py b/src/osbridgelcca/desktop_app/main_template.py index d97d975..1db4933 100644 --- a/src/osbridgelcca/desktop_app/main_template.py +++ b/src/osbridgelcca/desktop_app/main_template.py @@ -36,20 +36,6 @@ def setupUi(self, MainWindow): self.database_manager = DatabaseManager() # To check if tab widget is there or no self.tabs_active = False - self.results = { - COST_TOTAL_INIT_CONST: None, - COST_TOTAL_INIT_CARBON_EMISSION: None, - COST_TIME: None, - COST_TOTAL_ROAD_USER: None, - COST_ADDITIONAL_CARBON_EMISSION: None, - COST_PERIODIC_MAINTAINANCE: None, - COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION: None, - COST_TOTAL_ROUTINE_INSPECTION: None, - COST_REPAIR_REHAB: None, - COST_DEMOLITION_DISPOSAL: None, - COST_RECYCLING: None, - COST_RECONSTRUCTION: None - } # Contains name of widget and its index in tabs self.active_tab_widgets = {} self.widget_map = { diff --git a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py index aef648c..7ff9da5 100644 --- a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py +++ b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py @@ -2,7 +2,7 @@ from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) -from PySide6.QtGui import QIcon +from PySide6.QtGui import QIcon, QIntValidator from .utils.data import * import sys import os @@ -16,7 +16,7 @@ def __init__(self, database, parent=None): self.parent = parent self.database_manager = database self.data = bridge_traffic_data.get(KEY_BRIDGE_TRAFFIC) - self.traffic_widgets = [] + self.widgets = [] self.text_box_width = 200 self.setStyleSheet(""" @@ -204,10 +204,10 @@ def __init__(self, database, parent=None): grid_layout.addWidget(label, 0, 0, 1, 1) valuer_combo = QComboBox(self.general_widget) + self.widgets.append(valuer_combo) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") valuer_combo.addItems(self.data[KEY_LANES][KEY_OPTIONS]) - self.traffic_widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 0, 1, 1, 1) info_icon = QLabel(" ") @@ -220,6 +220,7 @@ def __init__(self, database, parent=None): label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 1, 0, 1, 1) input_widget = QLineEdit(self.general_widget) + self.widgets.append(input_widget) input_widget.setFixedWidth(self.text_box_width) input_widget.setStyleSheet(""" QLineEdit { @@ -228,7 +229,6 @@ def __init__(self, database, parent=None): padding: 3px 10px; } """) - self.traffic_widgets.append(input_widget) grid_layout.addWidget(input_widget, 1, 1, 1, 1) info_icon = QLabel("(km)") info_icon.setStyleSheet("color: grey; font-size: 14px;") @@ -240,6 +240,7 @@ def __init__(self, database, parent=None): label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 2, 0, 1, 1) input_widget = QLineEdit(self.general_widget) + self.widgets.append(input_widget) input_widget.setFixedWidth(self.text_box_width) input_widget.setStyleSheet(""" QLineEdit { @@ -248,7 +249,6 @@ def __init__(self, database, parent=None): padding: 3px 10px; } """) - self.traffic_widgets.append(input_widget) grid_layout.addWidget(input_widget, 2, 1, 1, 1) info_icon = QLabel("(min)") info_icon.setStyleSheet("color: grey; font-size: 14px;") @@ -261,11 +261,11 @@ def __init__(self, database, parent=None): grid_layout.addWidget(label, 3, 0, 1, 1) valuer_combo = QComboBox(self.general_widget) + self.widgets.append(valuer_combo) valuer_combo.setFixedWidth(self.text_box_width) valuer_combo.setPlaceholderText("Select") valuer_combo.addItems(self.data[KEY_ROADROUGHNESS][KEY_OPTIONS] + ["Custom"]) valuer_combo.currentIndexChanged.connect(lambda index,combo=valuer_combo: self.custom_combo_input(index, combo)) - self.traffic_widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 3, 1, 1, 1) info_icon = QLabel("(mm/km)") @@ -283,7 +283,7 @@ def __init__(self, database, parent=None): valuer_combo.setPlaceholderText("Select") valuer_combo.addItems(self.data[KEY_ROAD_RISE_AND_FALL][KEY_OPTIONS] + ["Custom"]) valuer_combo.currentIndexChanged.connect(lambda index,combo=valuer_combo: self.custom_combo_input(index, combo)) - self.traffic_widgets.append(valuer_combo) + self.widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 4, 1, 1, 1) info_icon = QLabel("(m/km)") @@ -301,7 +301,7 @@ def __init__(self, database, parent=None): valuer_combo.setPlaceholderText("Select") valuer_combo.addItems(self.data[KEY_ROAD_RISE_AND_FALL][KEY_OPTIONS] + ["Custom"]) valuer_combo.currentIndexChanged.connect(lambda index,combo=valuer_combo: self.custom_combo_input(index, combo)) - self.traffic_widgets.append(valuer_combo) + self.widgets.append(valuer_combo) grid_layout.addWidget(valuer_combo, 5, 1, 1, 1) info_icon = QLabel("(m/km)") @@ -319,7 +319,7 @@ def __init__(self, database, parent=None): valuer_combo.setPlaceholderText("Select") valuer_combo.addItems(self.data[KEY_TYPE_OF_ROAD][KEY_OPTIONS]) grid_layout.addWidget(valuer_combo, 6, 1, 1, 1) - self.traffic_widgets.append(valuer_combo) + self.widgets.append(valuer_combo) info_icon = QLabel(" ") info_icon.setStyleSheet("color: grey; font-size: 14px;") @@ -339,7 +339,7 @@ def __init__(self, database, parent=None): padding: 3px 10px; } """) - self.traffic_widgets.append(input_widget) + self.widgets.append(input_widget) grid_layout.addWidget(input_widget, 7, 1, 1, 1) info_icon = QLabel("(accidents/million km)") info_icon.setStyleSheet("color: grey; font-size: 14px;") @@ -370,7 +370,7 @@ def __init__(self, database, parent=None): } """) - per_dist_label = QLabel("% Accident Distribution") + per_dist_label = QLabel("Percentage Accident Distribution") per_dist_label.setAlignment(Qt.AlignCenter) per_dist_label.setStyleSheet(""" QLabel { @@ -394,7 +394,6 @@ def __init__(self, database, parent=None): v_label.setAlignment(Qt.AlignCenter) v_label.setStyleSheet("background-color: #FFFFFF; border: 1px solid #FFFFFF; border-radius: 10px; padding: 10px 10px 10px 1px;") v0_input = QLineEdit() - self.traffic_widgets.append(v0_input) v0_input.setFixedWidth(self.text_box_width*2) v0_input.setStyleSheet(""" QLineEdit { @@ -450,7 +449,7 @@ def __init__(self, database, parent=None): } """) - per_dist_label = QLabel("% Accident Distribution") + per_dist_label = QLabel("Percentage Accident Distribution") per_dist_label.setAlignment(Qt.AlignCenter) per_dist_label.setStyleSheet(""" QLabel { @@ -473,6 +472,7 @@ def __init__(self, database, parent=None): "MCV", "HCV" ] + self.average_daily_traffic = [] for i, vehicle in enumerate(vehicles): v_label = QLabel(f"{vehicle}:") @@ -480,7 +480,8 @@ def __init__(self, database, parent=None): v_label.setAlignment(Qt.AlignCenter) v_label.setStyleSheet("background-color: #FFFFFF; border: 1px solid #FFFFFF; border-radius: 10px; padding: 10px 10px 10px 1px;") v0_input = QLineEdit() - self.traffic_widgets.append(v0_input) + v0_input.setValidator(QIntValidator(v0_input)) + self.average_daily_traffic.append(v0_input) v0_input.setFixedWidth(self.text_box_width) v0_input.setStyleSheet(""" QLineEdit { @@ -491,7 +492,6 @@ def __init__(self, database, parent=None): } """) v1_input = QLineEdit() - self.traffic_widgets.append(v1_input) v1_input.setFixedWidth(self.text_box_width) v1_input.setStyleSheet(""" QLineEdit { @@ -555,23 +555,37 @@ def custom_combo_input(self, index, combo): combo.lineEdit().setPlaceholderText("Type here...") def collect_data(self): - data = [] - for widget in self.traffic_widgets: - if isinstance(widget, QComboBox): - value = widget.currentText() - elif isinstance(widget, QLineEdit): - value = widget.text() if widget.text() != "" else "0" - data.append(value) - print("Collected Data from UI:",data) - - # calculate Road User Cost Calculation - road_user_data = self.database_manager.calculate_irc_road_cost(data) - # Update Results Dict - self.parent.results[COST_TOTAL_ROAD_USER] = road_user_data - - # Calculate additional carbon emission costs - cost = self.database_manager.additional_carbon_emission_cost(data) - self.parent.results[COST_ADDITIONAL_CARBON_EMISSION] = cost + from pprint import pprint + traffic_data = { + KEY_TWO_WHEELER: 0 if not self.average_daily_traffic[0].text() else int(self.average_daily_traffic[0].text()), + KEY_SMALL_CARS: 0 if not self.average_daily_traffic[0].text() else int(self.average_daily_traffic[1].text()), + KEY_BIG_CARS: 0 if not self.average_daily_traffic[0].text() else int(self.average_daily_traffic[2].text()), + KEY_ORDINARY_BUS: 0 if not self.average_daily_traffic[0].text() else int(self.average_daily_traffic[3].text()), + KEY_DELUXE_BUS: 0 if not self.average_daily_traffic[0].text() else int(self.average_daily_traffic[3].text()), + KEY_LCV: 0 if not self.average_daily_traffic[0].text() else int(self.average_daily_traffic[4].text()), + KEY_HCV: 0 if not self.average_daily_traffic[0].text() else int(self.average_daily_traffic[5].text()), + KEY_MCV: 0 if not self.average_daily_traffic[0].text() else int(self.average_daily_traffic[6].text()) + } + # Collect other data + data = { + KEY_ALTER_ROAD_CARRIAGEWAY: self.widgets[0].currentText(), + KEY_ADDIT_REROUTING_DISTANCE: 0.0 if not self.widgets[1].text() else float(self.widgets[1].text()), + KEY_ADDIT_TRAVEL_TIME: 0.0 if not self.widgets[2].text() else float(self.widgets[2].text()), + KEY_ROAD_ROUGHNESS: self.widgets[3].currentText(), + KEY_ROAD_RISE: self.widgets[4].currentText(), + KEY_ROAD_FALL: self.widgets[5].currentText(), + KEY_ROAD_TYPE: self.widgets[6].currentText(), + KEY_CRASH_RATE: 0.0 if not self.widgets[7].text() else float(self.widgets[7].text()) + } + print("\nCollected Traffic Composition Data from UI:") + pprint(traffic_data) + + # Save UI Data to Backend + self.database_manager.traffic_data = data + self.database_manager.daily_average_traffic_data = traffic_data + + # Carbon Emission due to Rerouting during Initial Construction + self.database_manager.init_carbon_emission_rerouting() def close_widget(self): self.closed.emit() diff --git a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py index 2cb4e86..67534c1 100644 --- a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py +++ b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_cost_data.py @@ -1,7 +1,7 @@ from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal -from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) -from PySide6.QtGui import QIcon +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QComboBox) +from PySide6.QtGui import QIcon, QDoubleValidator from ..utils.data import * import sys @@ -14,6 +14,9 @@ def __init__(self, database, parent=None): self.parent = parent self.widget = [] self.database_manager = database + + # Store references to K.Ricke widgets for showing/hiding + self.k_ricke_widgets = [] self.setStyleSheet(""" #central_panel_widget { @@ -112,25 +115,71 @@ def __init__(self, database, parent=None): border-color: #606060; } - /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ QPushButton#nav_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; - min-width: 80px; /* Ensure a minimum width */ + min-width: 80px; } QPushButton#nav_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#nav_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; + } + + QComboBox { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background-color: #FFFFFF; + } + QComboBox::drop-down { + border: none; + padding-right: 5px; + } + QComboBox::down-arrow { + image: url(resources/country_arrow.png); + width: 30px; + height: 30px; + } + QComboBox QAbstractItemView { + border: 1px solid #DDDCE0; + border-radius: 5px; + background-color: #FFFFFF; + outline: none; + } + QComboBox QAbstractItemView::item:selected { + background-color: #FDEFEF; + color: #000000; + } + QComboBox QAbstractItemView::item:hover { + background-color: #FDEFEF; + } + QComboBox:disabled { + background-color: #F0F0F0; + color: #888888; + } + + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + background-color: #FFFFFF; + } + QLineEdit:focus { + border: 1px solid #DDDCE0; + background-color: #FFFFFF; + } + QLineEdit:disabled { + background-color: #F0F0F0; + color: #888888; } - /* (Removed: QComboBox and material grid element CSS) */ """) self.setObjectName("central_panel_widget") @@ -156,84 +205,136 @@ def __init__(self, database, parent=None): self.general_layout.setContentsMargins(10, 20, 10, 10) self.general_layout.setSpacing(10) - grid_layout = QGridLayout() - grid_layout.setHorizontalSpacing(10) - grid_layout.setVerticalSpacing(20) + self.grid_layout = QGridLayout() + self.grid_layout.setHorizontalSpacing(10) + self.grid_layout.setVerticalSpacing(20) - field_width = 200 # More compact width for input fields + field_width = 200 - # Shared Socioeconomic Pathway Type (first row, word wrap enabled) - label = QLabel("Shared Socioeconomic Pathway\nType") - label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) - grid_layout.addWidget(label, 0, 0, 1, 1) - input_widget = QLineEdit(self.general_widget) - self.widget.append(input_widget) - input_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) - input_widget.setFixedWidth(field_width) - input_widget.setText("SSP2") # Set default text - input_widget.setStyleSheet(""" - QLineEdit { - border: 1px solid #DDDCE0; - border-radius: 10px; - padding: 3px 10px; - } - """) - grid_layout.addWidget(input_widget, 0, 1, 1, 1, alignment=Qt.AlignLeft) + # Validator for numeric inputs + validator = QDoubleValidator() + validator.setRange(0.0, 9999999.999, 4) + validator.setBottom(0.0) + validator.setNotation(QDoubleValidator.Notation.StandardNotation) - # Representative Concentration Pathway Type - label = QLabel("Representative Concentration Pathway\nType") + # 1. Source + label = QLabel("Source") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) - grid_layout.addWidget(label, 1, 0, 1, 1) - input_widget = QLineEdit(self.general_widget) - self.widget.append(input_widget) - input_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) - input_widget.setFixedWidth(field_width) - input_widget.setText("RCP60") # Set default text - input_widget.setStyleSheet(""" - QLineEdit { - border: 1px solid #DDDCE0; - border-radius: 10px; - padding: 3px 10px; - } - """) - grid_layout.addWidget(input_widget, 1, 1, 1, 1, alignment=Qt.AlignLeft) - - # Social Cost of Carbon (SCC) - label = QLabel("Social Cost of Carbon") # Removed "\n(SCC)" - label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) - grid_layout.addWidget(label, 2, 0, 1, 1) + self.grid_layout.addWidget(label, 0, 0, 1, 1) + + self.source_combo = QComboBox(self.general_widget) + self.widget.append(self.source_combo) + self.source_combo.setFixedWidth(field_width) + self.source_combo.addItems(KEY_SCC_OPTIONS) + self.source_combo.currentTextChanged.connect(self.on_source_changed) + self.grid_layout.addWidget(self.source_combo, 0, 1, 1, 1, alignment=Qt.AlignLeft) + + # 2. Social Cost of Carbon (SCC) + scc_label = QLabel("Social Cost of Carbon") + scc_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + self.grid_layout.addWidget(scc_label, 1, 0, 1, 1) # Create a QWidget with a horizontal layout for the SCC input and label scc_widget = QWidget(self.general_widget) scc_h_layout = QHBoxLayout(scc_widget) scc_h_layout.setContentsMargins(0,0,0,0) scc_h_layout.setSpacing(10) - input_widget = QLineEdit() - self.widget.append(input_widget) - input_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) - input_widget.setFixedWidth(field_width) # Adjusted width for SCC input to match the image - input_widget.setText("6.3936") # Set default text - input_widget.setStyleSheet(""" - QLineEdit { - border: 1px solid #DDDCE0; - border-radius: 10px; - padding: 3px 10px; - } - """) - scc_h_layout.addWidget(input_widget) - - scc_suggested_label = QLabel("(INR/kgCO₂e)") - scc_suggested_label.setStyleSheet("color: #707070; font-size: 11px;") - scc_h_layout.addWidget(scc_suggested_label) - - scc_suggested_label = QLabel(" Suggested") - scc_suggested_label.setStyleSheet("color: #B3AEAE; font-size: 10px;") - scc_h_layout.addWidget(scc_suggested_label) - scc_h_layout.addStretch(1) # Add stretch to push content to the left - - grid_layout.addWidget(scc_widget, 2, 1, 1, 1, alignment=Qt.AlignLeft) + + self.scc_input = QLineEdit() + self.widget.append(self.scc_input) + self.scc_input.setAlignment(Qt.AlignmentFlag.AlignLeft) + self.scc_input.setFixedWidth(field_width) + self.scc_input.setText("6.3936") + self.scc_input.setValidator(validator) + scc_h_layout.addWidget(self.scc_input) + + scc_unit_label = QLabel("(INR/kgCO₂e)") + scc_unit_label.setStyleSheet("color: #707070; font-size: 11px;") + scc_h_layout.addWidget(scc_unit_label) + + self.scc_suggested_label = QLabel("Suggested") + self.scc_suggested_label.setStyleSheet("color: #B3AEAE; font-size: 10px;") + scc_h_layout.addWidget(self.scc_suggested_label) + scc_h_layout.addStretch(1) + + self.grid_layout.addWidget(scc_widget, 1, 1, 1, 1, alignment=Qt.AlignLeft) - self.general_layout.addLayout(grid_layout) + # 3. K.Ricke et al. specific fields (initially hidden) + current_row = 2 + + # SSP + ssp_label = QLabel(SCC_SSP) + ssp_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + self.grid_layout.addWidget(ssp_label, current_row, 0, 1, 1) + self.k_ricke_widgets.append(ssp_label) + + self.ssp_combo = QComboBox(self.general_widget) + self.widget.append(self.ssp_combo) + self.ssp_combo.setFixedWidth(field_width) + self.ssp_combo.addItems(SCC_SSP_OPTIONS) + self.grid_layout.addWidget(self.ssp_combo, current_row, 1, 1, 1, alignment=Qt.AlignLeft) + self.k_ricke_widgets.append(self.ssp_combo) + current_row += 1 + + # RCP + rcp_label = QLabel(SCC_RCP) + rcp_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + self.grid_layout.addWidget(rcp_label, current_row, 0, 1, 1) + self.k_ricke_widgets.append(rcp_label) + + self.rcp_combo = QComboBox(self.general_widget) + self.widget.append(self.rcp_combo) + self.rcp_combo.setFixedWidth(field_width) + self.rcp_combo.addItems(SCC_RCP_OPTIONS) + self.grid_layout.addWidget(self.rcp_combo, current_row, 1, 1, 1, alignment=Qt.AlignLeft) + self.k_ricke_widgets.append(self.rcp_combo) + current_row += 1 + + # Climate + climate_label = QLabel(SCC_CLIMATE) + climate_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + self.grid_layout.addWidget(climate_label, current_row, 0, 1, 1) + self.k_ricke_widgets.append(climate_label) + + self.climate_combo = QComboBox(self.general_widget) + self.widget.append(self.climate_combo) + self.climate_combo.setFixedWidth(field_width) + self.climate_combo.addItems(SSC_CLIMATE_OPTIONS) + self.grid_layout.addWidget(self.climate_combo, current_row, 1, 1, 1, alignment=Qt.AlignLeft) + self.k_ricke_widgets.append(self.climate_combo) + current_row += 1 + + # Run + run_label = QLabel(SCC_RUN) + run_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + self.grid_layout.addWidget(run_label, current_row, 0, 1, 1) + self.k_ricke_widgets.append(run_label) + + self.run_combo = QComboBox(self.general_widget) + self.widget.append(self.run_combo) + self.run_combo.setFixedWidth(field_width) + self.run_combo.addItems(SCC_RUN_OPTIONS) + self.grid_layout.addWidget(self.run_combo, current_row, 1, 1, 1, alignment=Qt.AlignLeft) + self.k_ricke_widgets.append(self.run_combo) + current_row += 1 + + # USD to INR Conversion + usd_inr_label = QLabel("USD to INR Conversion") + usd_inr_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + self.grid_layout.addWidget(usd_inr_label, current_row, 0, 1, 1) + self.k_ricke_widgets.append(usd_inr_label) + + self.usd_inr_input = QLineEdit() + self.widget.append(self.usd_inr_input) + self.usd_inr_input.setAlignment(Qt.AlignmentFlag.AlignLeft) + self.usd_inr_input.setFixedWidth(field_width) + self.usd_inr_input.setPlaceholderText("Enter conversion rate") + self.usd_inr_input.setValidator(validator) + self.usd_inr_input.setText("88") + self.grid_layout.addWidget(self.usd_inr_input, current_row, 1, 1, 1, alignment=Qt.AlignLeft) + self.k_ricke_widgets.append(self.usd_inr_input) + + self.general_layout.addLayout(self.grid_layout) self.general_layout.addStretch(1) self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) @@ -242,8 +343,7 @@ def __init__(self, database, parent=None): self.button_h_layout.setSpacing(10) self.button_h_layout.setContentsMargins(10,10,10,10) - # Adjust these stretch factors to control the position - self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + self.button_h_layout.addStretch(6) back_button = QPushButton("Back") back_button.setObjectName("nav_button") @@ -256,54 +356,60 @@ def __init__(self, database, parent=None): next_button.clicked.connect(lambda: self.next.emit(KEY_CARBON_EMISSION_COST)) self.button_h_layout.addWidget(next_button) - # Add initial spacing before the navigation buttons self.scroll_content_layout.addLayout(self.button_h_layout) - # --- Add a corner spacer to the scroll_content_layout --- self.button_h_layout.addSpacerItem(QSpacerItem(20, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.scroll_content_layout.addSpacerItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) left_panel_vlayout.addWidget(self.scroll_area) + + # Initialize the UI state based on default source + self.on_source_changed(self.source_combo.currentText()) + + def on_source_changed(self, source): + """Handle source selection changes""" + if source == SCC_NITI_Aayog: + # Set default value and disable input + self.scc_input.setText("6.3936") + self.scc_input.setEnabled(False) + self.scc_suggested_label.show() + # Hide K.Ricke fields + for widget in self.k_ricke_widgets: + widget.hide() + + elif source == SCC_CUSTOM: + # Clear value and enable input + self.scc_input.clear() + self.scc_input.setEnabled(True) + self.scc_suggested_label.hide() + # Hide K.Ricke fields + for widget in self.k_ricke_widgets: + widget.hide() + + elif source == SCC_K_Ricke_et_al: + # Disable SCC input (will be calculated) + self.scc_input.setEnabled(False) + self.scc_suggested_label.hide() + # Show K.Ricke fields + for widget in self.k_ricke_widgets: + widget.show() def collect_data(self): - data = [] - for widget in self.widget: - data.append(widget.text()) - print("Collected Data from UI:",data) + from pprint import pprint + data = { + KEY_SOURCE: self.source_combo.currentText(), + KEY_SCC: float(self.scc_input.text()), + KEY_USD_T_INR: float(self.usd_inr_input.text()) if self.usd_inr_input.text() else 0.0 + } + print("\nCollected Data from Carbon Emission Cost UI:") + pprint(data) + + # Save UI Data to Backend + self.database_manager.carbon_emission_cost_data = data - # calculate Carbon Emission Cost - carbon_cost = float(self.widget[2].text()) # Social Cost of Carbon input - carbon_emission_cost = self.database_manager.carbon_emission_cost(carbon_cost) - # Update Results Dict - self.parent.results[COST_TOTAL_INIT_CARBON_EMISSION] = carbon_emission_cost + # Carbon Emission Cost + # self.database_manager.carbon_emission_cost() def close_widget(self): self.closed.emit() - self.setParent(None) - -#----------------Standalone-Test-Code-------------------------------- - -# class MyMainWindow(QMainWindow): -# def __init__(self): -# super().__init__() - -# self.setStyleSheet("border: none") - -# self.central_widget = QWidget() -# self.central_widget.setObjectName("central_widget") -# self.setCentralWidget(self.central_widget) - -# self.main_h_layout = QHBoxLayout(self.central_widget) -# self.main_h_layout.addStretch(1) - -# self.main_h_layout.addWidget(CarbonEmissionCostData(), 2) - -# self.setWindowState(Qt.WindowMaximized) - - -# if __name__ == "__main__": -# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) -# app = QApplication(sys.argv) -# window = MyMainWindow() -# window.show() -# sys.exit(app.exec()) + self.setParent(None) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py index 2a41901..0bc1f90 100644 --- a/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py +++ b/src/osbridgelcca/desktop_app/widgets/carbon_emission_data/carbon_emission_data.py @@ -10,36 +10,32 @@ def __init__(self, data, parent=None): super().__init__(parent) self.widgets = [] self.data = data - self.material_rows = [] # To store references to widgets in each material row - self.current_material_row_idx = 1 # Start index for material rows (0 is header) + self.material_rows = [] + self.current_material_row_idx = 1 self.init_ui() def init_ui(self): - self.component_first_scroll_content_layout = QVBoxLayout(self) # Set QVBoxLayout directly on self + self.component_first_scroll_content_layout = QVBoxLayout(self) self.component_first_scroll_content_layout.setContentsMargins(10, 10, 10, 10) self.component_first_scroll_content_layout.setSpacing(10) # --- Material Details Grid Layout --- self.material_grid_layout = QGridLayout() self.material_grid_layout.setHorizontalSpacing(10) - self.material_grid_layout.setVerticalSpacing(5) + self.material_grid_layout.setVerticalSpacing(10) # Changed from 5 to 10 - # Header Row - Updated headers for clarity (no specific unit in header now) + # Header Row headers = ["Type of Material and Grade", "Quantity", "Unit", "Embodied Carbon Energy", "Carbon Emission Factor"] for col, header_text in enumerate(headers): label = QLabel(header_text) label.setAlignment(Qt.AlignCenter) label.setObjectName("MaterialGridLabel") - if header_text=="Embodied Carbon Energy": - self.material_grid_layout.addWidget(label, 0, col, alignment=Qt.AlignmentFlag.AlignRight) - else: - - self.material_grid_layout.addWidget(label, 0, col, alignment=Qt.AlignmentFlag.AlignCenter) + self.material_grid_layout.addWidget(label, 0, col, alignment=Qt.AlignmentFlag.AlignCenter) self.component_first_scroll_content_layout.addLayout(self.material_grid_layout) - # Add initial two material rows + # Add initial material rows for item in self.data: self.widgets.append(self.add_material_row(item)) @@ -47,16 +43,15 @@ def init_ui(self): self.add_material_button = QPushButton("+ Add Material") self.add_material_button.setObjectName("add_material_button") self.add_material_button.clicked.connect(self.add_material_row) + + # Add spacing before the button + self.component_first_scroll_content_layout.addSpacing(10) self.component_first_scroll_content_layout.addWidget(self.add_material_button, alignment=Qt.AlignCenter) def add_material_row(self, item=[]): row_idx = self.current_material_row_idx row_widgets = [] - # Set fixed width for input widgets. - fixed_input_width_combo = 80 - fixed_input_width_line_edit = 80 - # Type of Material (Column 0) if item: type_material = QComboBox() @@ -65,8 +60,9 @@ def add_material_row(self, item=[]): type_material = QLineEdit() type_material.setPlaceholderText("Material...") type_material.setObjectName("MaterialGridInput") - type_material.setFixedWidth(fixed_input_width_combo) - self.material_grid_layout.addWidget(type_material, row_idx, 0, alignment=Qt.AlignmentFlag.AlignHCenter) + type_material.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + type_material.setMinimumWidth(200) + self.material_grid_layout.addWidget(type_material, row_idx, 0) # Quantity (Column 1) quantity_edit = QLineEdit() @@ -74,8 +70,10 @@ def add_material_row(self, item=[]): quantity_edit.setText(str(item[2])) quantity_edit.setReadOnly(True) quantity_edit.setObjectName("MaterialGridInput") - quantity_edit.setFixedWidth(fixed_input_width_line_edit) - self.material_grid_layout.addWidget(quantity_edit, row_idx, 1, alignment=Qt.AlignmentFlag.AlignHCenter) + quantity_edit.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + quantity_edit.setMinimumWidth(100) + quantity_edit.setMaximumWidth(100) + self.material_grid_layout.addWidget(quantity_edit, row_idx, 1) # Unit (Column 2) if item: @@ -85,66 +83,68 @@ def add_material_row(self, item=[]): unit_combo = QLineEdit() unit_combo.setPlaceholderText("Unit...") unit_combo.setObjectName("MaterialGridInput") - unit_combo.setFixedWidth(fixed_input_width_combo) - self.material_grid_layout.addWidget(unit_combo, row_idx, 2, alignment=Qt.AlignmentFlag.AlignHCenter) + unit_combo.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + unit_combo.setMinimumWidth(100) + unit_combo.setMaximumWidth(100) + self.material_grid_layout.addWidget(unit_combo, row_idx, 2) - # Build row_widgets array in correct order: [unit, quantity, type, grade, embodied_carbon, carbon_emission] + # Build row_widgets array if item: row_widgets = [item[1], item[2], item[3], item[4]] else: row_widgets = [unit_combo, quantity_edit, type_material, None] - # --- Embodied Carbon Energy (LineEdit + QLabel for unit text) - Column 3 --- + # --- Embodied Carbon Energy - Column 3 --- embodied_carbon_layout = QHBoxLayout() - embodied_carbon_layout.addStretch() embodied_carbon_layout.setContentsMargins(0, 0, 0, 0) embodied_carbon_layout.setSpacing(5) embodied_carbon_edit = QLineEdit() embodied_carbon_edit.setPlaceholderText("0.00") embodied_carbon_edit.setObjectName("MaterialGridInput") - embodied_carbon_edit.setFixedWidth(fixed_input_width_combo) + embodied_carbon_edit.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + embodied_carbon_edit.setMinimumWidth(100) + embodied_carbon_edit.setMaximumWidth(100) embodied_carbon_layout.addWidget(embodied_carbon_edit) embodied_carbon_unit_label = QLabel("(MJ/kg)") embodied_carbon_unit_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) embodied_carbon_unit_label.setStyleSheet("color: #3F3E5E; font-size: 11px;") + embodied_carbon_unit_label.setFixedWidth(60) embodied_carbon_layout.addWidget(embodied_carbon_unit_label) - self.material_grid_layout.addLayout(embodied_carbon_layout, row_idx, 3, alignment=Qt.AlignmentFlag.AlignHCenter) + self.material_grid_layout.addLayout(embodied_carbon_layout, row_idx, 3) - # Add embodied carbon edit to row_widgets (index 4) row_widgets.append(embodied_carbon_edit) - # --- Carbon Emission Factor (LineEdit + QLabel for unit text) - Column 4 --- + # --- Carbon Emission Factor - Column 4 --- carbon_emission_layout = QHBoxLayout() carbon_emission_layout.setContentsMargins(0, 0, 0, 0) carbon_emission_layout.setSpacing(5) - carbon_emission_layout.addStretch() carbon_emission_edit = QLineEdit() carbon_emission_edit.setPlaceholderText("0.00") carbon_emission_edit.setObjectName("MaterialGridInput") - carbon_emission_edit.setFixedWidth(fixed_input_width_combo) + carbon_emission_edit.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + carbon_emission_edit.setMinimumWidth(100) + carbon_emission_edit.setMaximumWidth(100) carbon_emission_layout.addWidget(carbon_emission_edit) - # Create the carbon emission unit label if item: carbon_emission_unit_label = QLabel("kg CO2e/" + str(item[1])) else: carbon_emission_unit_label = QLabel("kg CO2e/kg") carbon_emission_unit_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) carbon_emission_unit_label.setStyleSheet("color: #3F3E5E; font-size: 11px;") + carbon_emission_unit_label.setFixedWidth(80) carbon_emission_layout.addWidget(carbon_emission_unit_label) - carbon_emission_layout.addStretch() self.material_grid_layout.addLayout(carbon_emission_layout, row_idx, 4) - # Add carbon emission edit to row_widgets (index 5) row_widgets.append(carbon_emission_edit) - # Only connect if it's a custom row (unit_combo is QLineEdit, not QComboBox) - if not item: # This means it's a custom material row + # Connect unit changes to update carbon emission label + if not item: def update_carbon_unit(text): unit_text = text.strip() if text.strip() else "kg" carbon_emission_unit_label.setText(f"kg CO2e/{unit_text}") @@ -157,7 +157,6 @@ def update_carbon_unit(text): self.adjustSize() if not item: - # append widget to self.widget self.widgets.append(row_widgets) return row_widgets @@ -169,7 +168,7 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): row_idx_in_grid = -1 for i, row_dict in enumerate(self.material_rows): if row_dict == row_widgets_to_remove: - row_idx_in_grid = i + 1 # +1 because row 0 is header + row_idx_in_grid = i + 1 break if row_idx_in_grid == -1: @@ -184,12 +183,11 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): widget.deleteLater() elif item.layout(): layout = item.layout() - # Iterate and delete widgets within the layout while layout.count(): sub_item = layout.takeAt(0) if sub_item.widget(): sub_item.widget().deleteLater() - self.material_grid_layout.removeItem(layout) # Remove the layout itself + self.material_grid_layout.removeItem(layout) self.material_rows.remove(row_widgets_to_remove) self.current_material_row_idx -= 1 @@ -197,33 +195,32 @@ def remove_material_row_by_widgets(self, row_widgets_to_remove): # Re-arrange remaining rows for r_idx in range(row_idx_in_grid, self.current_material_row_idx + 1): for c_idx in range(self.material_grid_layout.columnCount()): - item = self.material_grid_layout.itemAtPosition(r_idx + 1, c_idx) # Look at the row below + item = self.material_grid_layout.itemAtPosition(r_idx + 1, c_idx) if item: if item.widget(): widget = item.widget() self.material_grid_layout.removeWidget(widget) - self.material_grid_layout.addWidget(widget, r_idx, c_idx, alignment=Qt.AlignmentFlag.AlignHCenter) # Re-add with original alignment + self.material_grid_layout.addWidget(widget, r_idx, c_idx) elif item.layout(): layout = item.layout() self.material_grid_layout.removeItem(layout) - self.material_grid_layout.addLayout(layout, r_idx, c_idx, alignment=Qt.AlignmentFlag.AlignHCenter) # Re-add with original alignment + self.material_grid_layout.addLayout(layout, r_idx, c_idx) self.updateGeometry() self.update() self.material_grid_layout.invalidate() self.adjustSize() - def collect_data(self): p = [] for row in self.widgets: data = { - KEY_TYPE: row[2].text() if isinstance(row[2], QLineEdit) else row[2], - KEY_GRADE: row[3].text() if isinstance(row[3], QLineEdit) else "", - KEY_QUANTITY: row[1].text() if isinstance(row[1], QLineEdit) else row[1], - KEY_UNIT_M3: row[0].text() if isinstance(row[0], QLineEdit) else row[0], - KEY_EMBODIED_CARBON_ENERGY: row[4].text() if row[4].text() else "0", - KEY_CARBON_EMISSION_FACTOR: row[5].text() if row[4].text() else "0", + KEY_TYPE: row[2] if not isinstance(row[2], QLineEdit) else row[2].text(), + KEY_GRADE: row[3] if not isinstance(row[3], QLineEdit) else row[3].text(), + KEY_QUANTITY: float(row[1]) if not isinstance(row[1], QLineEdit) else (0.0 if not row[1].text() else float(row[1].text())), + KEY_UNIT_M3: row[0] if not isinstance(row[0], QLineEdit) else ("kg" if not row[1].text() else row[1].text()), + KEY_EMBODIED_CARBON_ENERGY: 0.0 if not row[4].text() else float(row[4].text()), + KEY_CARBON_EMISSION_FACTOR: 0.0 if not row[4].text() else float(row[5].text()), } p.append(data) return p @@ -233,12 +230,14 @@ class CarbonEmissionData(QWidget): closed = Signal() next = Signal(str) back = Signal(str) + def __init__(self, database, parent=None): super().__init__() self.database_manager = database self.material_store = self.database_manager.get_unique_materials_and_grades() - print(self.material_store) - self.component_widgets = [] # To store references to each ComponentWidget instance + from pprint import pprint + pprint(self.material_store) + self.component_widgets = [] self.setStyleSheet(""" #central_panel_widget { @@ -256,7 +255,6 @@ def __init__(self, database, parent=None): } QScrollArea { - background-color: transparent; outline: none; } @@ -338,38 +336,35 @@ def __init__(self, database, parent=None): border-color: #606060; } - /* Styling for component_first_widget (the container for the nested scroll area) */ #component_first_widget { background-color: transparent; - margin-top: 10px; /* Add some space above each component block */ + margin-top: 10px; } - #component_first_scroll_content_widget { /* This now applies directly to ComponentWidget itself */ + #component_first_scroll_content_widget { background-color: #FFFFFF; padding: 10px; - border-radius: 8px; } - /* Updated Styling for navigation buttons to match the Add Material/Component buttons */ QPushButton#nav_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; - min-width: 80px; /* Ensure a minimum width */ + min-width: 80px; } QPushButton#nav_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#nav_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; } - /* Styling for QComboBox */ + QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; @@ -398,7 +393,6 @@ def __init__(self, database, parent=None): background-color: #FDEFEF; } - /* Styling for material grid elements */ #MaterialGridLabel { font-weight: bold; color: #3F3E5E; @@ -415,24 +409,23 @@ def __init__(self, database, parent=None): border: 1px solid #DDDCE0; background-color: #FFFFFF; } - /* IMPROVED CSS FOR ADD MATERIAL/COMPONENT BUTTONS */ + QPushButton#add_material_button, QPushButton#add_component_button { - background-color: #FFFFFF; /* White background */ - border: 1px solid #E0E0E0; /* Light grey border */ - border-radius: 8px; /* Slightly more rounded corners */ - color: #3F3E5E; /* Dark text color */ - padding: 6px 15px; /* Increased padding */ + background-color: #FFFFFF; + border: 1px solid #E0E0E0; + border-radius: 8px; + color: #3F3E5E; + padding: 6px 15px; text-align: center; } QPushButton#add_material_button:hover, QPushButton#add_component_button:hover { - background-color: #F8F8F8; /* Very subtle light grey on hover */ - border-color: #C0C0C0; /* Darker border on hover */ + background-color: #F8F8F8; + border-color: #C0C0C0; } QPushButton#add_material_button:pressed, QPushButton#add_component_button:pressed { - background-color: #E8E8E8; /* Darker grey on pressed */ - border-color: #A0A0A0; /* Even darker border */ + background-color: #E8E8E8; + border-color: #A0A0A0; } - /* END IMPROVED CSS */ """) self.setObjectName("central_panel_widget") @@ -449,16 +442,15 @@ def __init__(self, database, parent=None): self.scroll_area.setWidget(scroll_content_widget) self.scroll_content_layout = QVBoxLayout(scroll_content_widget) - self.scroll_content_layout.setContentsMargins(0,0,0,0) - self.scroll_content_layout.setSpacing(0) + self.scroll_content_layout.setContentsMargins(15, 15, 15, 15) # Added margins + self.scroll_content_layout.setSpacing(15) # Increased spacing # Create the navigation buttons layout self.button_h_layout = QHBoxLayout() self.button_h_layout.setSpacing(10) - self.button_h_layout.setContentsMargins(10,10,10,10) + self.button_h_layout.setContentsMargins(0, 10, 0, 0) # Top margin only - # Adjust these stretch factors to control the position - self.button_h_layout.addStretch(6) # Larger stretch on the left to push it more right + self.button_h_layout.addStretch() back_button = QPushButton("Back") back_button.setObjectName("nav_button") @@ -471,31 +463,26 @@ def __init__(self, database, parent=None): next_button.clicked.connect(lambda: self.next.emit(KEY_CARBON_EMISSION)) self.button_h_layout.addWidget(next_button) - # Add the initial component layout self.add_component_layout() - - # Add initial spacing before the navigation buttons + # Add navigation buttons self.scroll_content_layout.addLayout(self.button_h_layout) - # --- Add a corner spacer to the scroll_content_layout --- - self.button_h_layout.addSpacerItem(QSpacerItem(20, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) + # Add vertical spacer at the end self.scroll_content_layout.addSpacerItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)) left_panel_vlayout.addWidget(self.scroll_area) - def add_component_layout(self): new_component = ComponentWidget(data=self.material_store, parent=self) self.component_widgets.append(new_component) - # Temporarily remove button_h_layout and the vertical spacer for insertion - # Find the vertical spacer and remove it if it exists + # Remove spacer and button layout temporarily vertical_spacer_item = None for i in range(self.scroll_content_layout.count()): item = self.scroll_content_layout.itemAt(i) - if isinstance(item, QSpacerItem) and item.sizeHint().width() == 0: # This identifies the vertical spacer + if isinstance(item, QSpacerItem) and item.sizeHint().width() == 0: vertical_spacer_item = self.scroll_content_layout.takeAt(i) break @@ -508,14 +495,13 @@ def add_component_layout(self): # Re-add the navigation buttons layout self.scroll_content_layout.addLayout(self.button_h_layout) - # Re-add the vertical spacer if it was found + # Re-add the vertical spacer if vertical_spacer_item: self.scroll_content_layout.addItem(vertical_spacer_item) self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() - def remove_component_layout(self, component_to_remove): if component_to_remove in self.component_widgets: self.scroll_content_layout.removeWidget(component_to_remove) @@ -524,7 +510,6 @@ def remove_component_layout(self, component_to_remove): self.scroll_area.widget().updateGeometry() self.scroll_area.widget().adjustSize() - def expand_scroll_area(self): self.central_widget.layout().invalidate() @@ -533,34 +518,9 @@ def close_widget(self): self.setParent(None) def collect_data(self): + from pprint import pprint data = self.component_widgets[0].collect_data() - print("Collected Data from UI:",data) - self.database_manager.insert_carbon_emission_data(data) - - -#----------------Standalone-Test-Code-------------------------------- - -# class MyMainWindow(QMainWindow): -# def __init__(self): -# super().__init__() - -# self.setStyleSheet("border: none") - -# self.central_widget = QWidget() -# self.central_widget.setObjectName("central_widget") -# self.setCentralWidget(self.central_widget) - -# self.main_h_layout = QHBoxLayout(self.central_widget) -# self.main_h_layout.addStretch(1) - -# self.main_h_layout.addWidget(CarbonEmissionData(), 2) - -# self.setWindowState(Qt.WindowMaximized) - - -# if __name__ == "__main__": -# QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) -# app = QApplication(sys.argv) -# window = MyMainWindow() -# window.show() -# sys.exit(app.exec()) \ No newline at end of file + print("\nCollected Data from Carbon Emission UI:") + pprint(data) + # Insert Carbon Emission Data + self.database_manager.insert_carbon_emission_data(data) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py b/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py index 01d4bac..e924cfd 100644 --- a/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py +++ b/src/osbridgelcca/desktop_app/widgets/demolition_and_recycling_data.py @@ -163,8 +163,8 @@ def __init__(self, database, parent=None): field_width = 200 # More compact width for input fields - # 1. Demolition Cost rate as percentage to total construction cost - label = QLabel("Demolition Cost rate as\npercentage to total construction cost") + # 1. Demolition and Disposal Rate + label = QLabel("Demolition and Disposal Cost\n(Percentage of Initial Construction Cost)") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 0, 0, 1, 1) demolition_widget = QWidget(self.general_widget) @@ -173,7 +173,7 @@ def __init__(self, database, parent=None): demolition_layout.setSpacing(10) demolition_input = QLineEdit() self.widgets.append(demolition_input) - demolition_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + demolition_input.setAlignment(Qt.AlignmentFlag.AlignLeft) demolition_input.setFixedWidth(field_width) demolition_input.setText("10") demolition_input.setStyleSheet(""" @@ -191,8 +191,8 @@ def __init__(self, database, parent=None): demolition_layout.addStretch(1) grid_layout.addWidget(demolition_widget, 0, 1, 1, 1, alignment=Qt.AlignLeft) - # 2. Scrap Value of Structural Steel - label = QLabel("Scrap Value of Structural Steel") + # 2. Structural Steel Scrap Rate + label = QLabel("Structural Steel Scrap Rate") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 1, 0, 1, 1) scrap_value_widget = QWidget(self.general_widget) @@ -201,7 +201,7 @@ def __init__(self, database, parent=None): scrap_value_layout.setSpacing(10) scrap_value_input = QLineEdit() self.widgets.append(scrap_value_input) - scrap_value_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + scrap_value_input.setAlignment(Qt.AlignmentFlag.AlignLeft) scrap_value_input.setFixedWidth(field_width) scrap_value_input.setText("26000") scrap_value_input.setStyleSheet(""" @@ -219,8 +219,8 @@ def __init__(self, database, parent=None): scrap_value_layout.addStretch(1) grid_layout.addWidget(scrap_value_widget, 1, 1, 1, 1, alignment=Qt.AlignLeft) - # 3. Structural Steel Scrap - label = QLabel("Structural Steel Scrap") + # 3. Structural Steel Recylability + label = QLabel("Structural Steel Recylability") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 2, 0, 1, 1) steel_scrap_widget = QWidget(self.general_widget) @@ -229,7 +229,7 @@ def __init__(self, database, parent=None): steel_scrap_layout.setSpacing(10) steel_scrap_input = QLineEdit() self.widgets.append(steel_scrap_input) - steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignLeft) steel_scrap_input.setFixedWidth(field_width) steel_scrap_input.setText("95") steel_scrap_input.setStyleSheet(""" @@ -247,8 +247,8 @@ def __init__(self, database, parent=None): steel_scrap_layout.addStretch(1) grid_layout.addWidget(steel_scrap_widget, 2, 1, 1, 1, alignment=Qt.AlignLeft) - # 4. Scrap Value of Steel Rebar - label = QLabel("Scrap Value of Steel Rebar") + # 4. Steel Rebar Scrap Rate + label = QLabel("Steel Rebar Scrap Rate") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 3, 0, 1, 1) scrap_value_widget = QWidget(self.general_widget) @@ -257,7 +257,7 @@ def __init__(self, database, parent=None): scrap_value_layout.setSpacing(10) scrap_value_input = QLineEdit() self.widgets.append(scrap_value_input) - scrap_value_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + scrap_value_input.setAlignment(Qt.AlignmentFlag.AlignLeft) scrap_value_input.setFixedWidth(field_width) scrap_value_input.setText("32500") scrap_value_input.setStyleSheet(""" @@ -275,8 +275,8 @@ def __init__(self, database, parent=None): scrap_value_layout.addStretch(1) grid_layout.addWidget(scrap_value_widget, 3, 1, 1, 1, alignment=Qt.AlignLeft) - # 4. Steel Rebar Scrap - label = QLabel("Steel Rebar Scrap") + # 4. Steel Rebar Recylability + label = QLabel("Steel Rebar Recylability") label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid_layout.addWidget(label, 4, 0, 1, 1) steel_scrap_widget = QWidget(self.general_widget) @@ -285,7 +285,7 @@ def __init__(self, database, parent=None): steel_scrap_layout.setSpacing(10) steel_scrap_input = QLineEdit() self.widgets.append(steel_scrap_input) - steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignCenter) + steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignLeft) steel_scrap_input.setFixedWidth(field_width) steel_scrap_input.setText("75") steel_scrap_input.setStyleSheet(""" @@ -303,6 +303,62 @@ def __init__(self, database, parent=None): steel_scrap_layout.addStretch(1) grid_layout.addWidget(steel_scrap_widget, 4, 1, 1, 1, alignment=Qt.AlignLeft) + # 5. Pre Stressed Tendons Scrap Rate + label = QLabel("Pre Stressed Tendons Scrap Rate") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 5, 0, 1, 1) + steel_scrap_widget = QWidget(self.general_widget) + steel_scrap_layout = QHBoxLayout(steel_scrap_widget) + steel_scrap_layout.setContentsMargins(0,0,0,0) + steel_scrap_layout.setSpacing(10) + steel_scrap_input = QLineEdit() + self.widgets.append(steel_scrap_input) + steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignLeft) + steel_scrap_input.setFixedWidth(field_width) + steel_scrap_input.setText("32500") + steel_scrap_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + steel_scrap_layout.addWidget(steel_scrap_input) + steel_scrap_layout.addWidget(QLabel("(INR/MT)")) + suggested_label3 = QLabel("Suggested") + suggested_label3.setStyleSheet("color: #B3AEAE; font-size: 10px;") + steel_scrap_layout.addWidget(suggested_label3) + steel_scrap_layout.addStretch(1) + grid_layout.addWidget(steel_scrap_widget, 5, 1, 1, 1, alignment=Qt.AlignLeft) + + # 6. Pre Stressed Tendons Recylability + label = QLabel("Pre Stressed Tendons Recylability") + label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + grid_layout.addWidget(label, 6, 0, 1, 1) + steel_scrap_widget = QWidget(self.general_widget) + steel_scrap_layout = QHBoxLayout(steel_scrap_widget) + steel_scrap_layout.setContentsMargins(0,0,0,0) + steel_scrap_layout.setSpacing(10) + steel_scrap_input = QLineEdit() + self.widgets.append(steel_scrap_input) + steel_scrap_input.setAlignment(Qt.AlignmentFlag.AlignLeft) + steel_scrap_input.setFixedWidth(field_width) + steel_scrap_input.setText("60") + steel_scrap_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + steel_scrap_layout.addWidget(steel_scrap_input) + steel_scrap_layout.addWidget(QLabel("(%)")) + suggested_label3 = QLabel("Suggested") + suggested_label3.setStyleSheet("color: #B3AEAE; font-size: 10px;") + steel_scrap_layout.addWidget(suggested_label3) + steel_scrap_layout.addStretch(1) + grid_layout.addWidget(steel_scrap_widget, 6, 1, 1, 1, alignment=Qt.AlignLeft) + self.general_layout.addLayout(grid_layout) self.general_layout.addStretch(1) self.scroll_content_layout.addWidget(self.general_widget, alignment=Qt.AlignLeft) @@ -340,50 +396,38 @@ def close_widget(self): def collect_data(self): - data = [] - for widget in self.widgets: - print(f"Collecting data from widget: {widget}") - if isinstance(widget, QComboBox): - value = widget.currentText() - elif isinstance(widget, QLineEdit): - value = widget.text() - data.append(value) - - data[0] = float(data[0])/100 - data[2] = float(data[2])/100 + from pprint import pprint + data = { + KEY_DEMOLITION_DISPOSAL_COST: 0.0 if not self.widgets[0].text() else float(self.widgets[0].text())/100, + KEY_STRUCT_STEEL_SCRAP_RATE: 0.0 if not self.widgets[1].text() else float(self.widgets[1].text()), + KEY_STRUCT_STEEL_RECYLABILITY: 0.0 if not self.widgets[2].text() else float(self.widgets[2].text())/100, + KEY_STEEL_REBAR_SCRAP_RATE: 0.0 if not self.widgets[3].text() else float(self.widgets[3].text()), + KEY_STEEL_REBAR_RECYLABILITY: 0.0 if not self.widgets[4].text() else float(self.widgets[4].text())/100, + KEY_PS_TENDONS_SCRAP_RATE: 0.0 if not self.widgets[5].text() else float(self.widgets[5].text()), + KEY_PS_TENDONS_RECYLABILITY: 0.0 if not self.widgets[6].text() else float(self.widgets[6].text())/100 + } + + print("\nCollected Data from Demolition & Recycling UI:") + pprint(data) + + # Save UI Data to Backend + self.database_manager.demolition_and_recycling_data = data + + # Demolition and Disposal Cost + self.database_manager.demolition_and_disposal_cost() + + # Demolition and Disposal related Carbon Emission + self.database_manager.demolition_disposal_carbon_emission_cost() + + # Carbon Emission due to Rerouting during Demolition and Disposal + self.database_manager.demolition_disposal_rerouting_carbon_emission_cost() + + # Recycling Cost + self.database_manager.recycling_cost() + - print("Collected Data from UI:",data) - - # calculate demolition and disposal cost - total_initial_construction_cost = self.parent.results.get(COST_TOTAL_INIT_CONST) - cost = self.database_manager.demolition_and_disposal_cost(data, total_initial_construction_cost) - # Update Results Dict - self.parent.results[COST_DEMOLITION_DISPOSAL] = cost - - # recycling cost - cost = self.database_manager.recycling_cost(data) - # Update Results Dict - self.parent.results[COST_RECYCLING] = cost - - # reconstruction cost - initial_construction_cost = self.parent.results[COST_TOTAL_INIT_CONST] - demolition_cost = self.parent.results[COST_DEMOLITION_DISPOSAL] - carbon_emission_cost = self.parent.results[COST_TOTAL_INIT_CARBON_EMISSION] - time_cost = self.parent.results[COST_TIME] - roaduser_cost = self.parent.results[COST_TOTAL_ROAD_USER] - rerouting_carbon_cost = self.parent.results[COST_ADDITIONAL_CARBON_EMISSION] - - cost = self.database_manager.reconstruction_cost(initial_construction_cost, - demolition_cost, - carbon_emission_cost, - time_cost, - roaduser_cost, - rerouting_carbon_cost - ) - # Update Results Dict - self.parent.results[COST_RECONSTRUCTION] = cost - - print(f"Results:\n{self.parent.results}") + print("Results:\n") + pprint(self.database_manager.results) #----------------Standalone-Test-Code-------------------------------- diff --git a/src/osbridgelcca/desktop_app/widgets/financial_data.py b/src/osbridgelcca/desktop_app/widgets/financial_data.py index 3c7e7fa..d3721f2 100644 --- a/src/osbridgelcca/desktop_app/widgets/financial_data.py +++ b/src/osbridgelcca/desktop_app/widgets/financial_data.py @@ -1,7 +1,7 @@ from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) -from PySide6.QtGui import QIcon +from PySide6.QtGui import QIcon, QIntValidator from .utils.data import * import sys import os @@ -168,7 +168,7 @@ def __init__(self, database, parent=None): label1_layout = QHBoxLayout(label1_widget) label1_layout.setContentsMargins(0, 0, 0, 0) label1_layout.setSpacing(4) - label1 = QLabel("Real Discount Rate") + label1 = QLabel("Discount Rate (Inflation Adjusted)") label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) label1_layout.addWidget(label1) info_icon = QLabel() @@ -179,7 +179,7 @@ def __init__(self, database, parent=None): self.widgets.append(input1) input1.setAlignment(Qt.AlignmentFlag.AlignLeft) input1.setFixedWidth(field_width) - input1.setText("4.2500") + input1.setText("6.70") input1.setStyleSheet(""" QLineEdit { border: 1px solid #DDDCE0; @@ -194,14 +194,14 @@ def __init__(self, database, parent=None): grid_layout.addWidget(unit1, 0, 2, alignment=Qt.AlignVCenter) grid_layout.addWidget(suggested1, 0, 3, alignment=Qt.AlignVCenter) - # 2. Interest Rate - label2 = QLabel("Interest Rate") + # 2. Inflation Rate + label2 = QLabel("Inflation Rate") label2.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input2 = QLineEdit() self.widgets.append(input2) input2.setAlignment(Qt.AlignmentFlag.AlignLeft) input2.setFixedWidth(field_width) - input2.setText("10") + input2.setText("5.15") input2.setStyleSheet(input1.styleSheet()) unit2 = QLabel("(%)") suggested2 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") @@ -210,7 +210,23 @@ def __init__(self, database, parent=None): grid_layout.addWidget(unit2, 1, 2, alignment=Qt.AlignVCenter) grid_layout.addWidget(suggested2, 1, 3, alignment=Qt.AlignVCenter) - # 3. Investment Ratio + # 3. Interest Rate + label2 = QLabel("Interest Rate") + label2.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + input2 = QLineEdit() + self.widgets.append(input2) + input2.setAlignment(Qt.AlignmentFlag.AlignLeft) + input2.setFixedWidth(field_width) + input2.setText("7.75") + input2.setStyleSheet(input1.styleSheet()) + unit2 = QLabel("(%)") + suggested2 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label2, 2, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input2, 2, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit2, 2, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(suggested2, 2, 3, alignment=Qt.AlignVCenter) + + # 4. Investment Ratio label3 = QLabel("Investment Ratio") label3.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input3 = QLineEdit() @@ -220,15 +236,16 @@ def __init__(self, database, parent=None): input3.setText("0.5000") input3.setStyleSheet(input1.styleSheet()) suggested3 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") - grid_layout.addWidget(label3, 2, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(input3, 2, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(QWidget(), 2, 2) # Empty cell for unit - grid_layout.addWidget(suggested3, 2, 3, alignment=Qt.AlignVCenter) + grid_layout.addWidget(label3, 3, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input3, 3, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(QWidget(), 3, 2) # Empty cell for unit + grid_layout.addWidget(suggested3, 3, 3, alignment=Qt.AlignVCenter) - # 4. Duration of Study + # 5. Design Life label4 = QLabel("Design Life") label4.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input4 = QLineEdit() + input4.setValidator(QIntValidator(input4)) self.widgets.append(input4) input4.setAlignment(Qt.AlignmentFlag.AlignLeft) input4.setFixedWidth(field_width) @@ -236,12 +253,12 @@ def __init__(self, database, parent=None): input4.setStyleSheet(input1.styleSheet()) unit4 = QLabel("(years)") suggested4 = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") - grid_layout.addWidget(label4, 3, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(input4, 3, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(unit4, 3, 2, alignment=Qt.AlignVCenter) - grid_layout.addWidget(suggested4, 3, 3, alignment=Qt.AlignVCenter) + grid_layout.addWidget(label4, 4, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input4, 4, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit4, 4, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(suggested4, 4, 3, alignment=Qt.AlignVCenter) - # 5. Time for construction of Base Project + # 6. Time for construction of Base Project label5 = QLabel("Time for construction of Base Project") label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input5 = QLineEdit() @@ -251,25 +268,27 @@ def __init__(self, database, parent=None): input5.setText("") input5.setStyleSheet(input1.styleSheet()) unit5 = QLabel("(years)") - grid_layout.addWidget(label5, 4, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(input5, 4, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(unit5, 4, 2, alignment=Qt.AlignVCenter) - grid_layout.addWidget(QWidget(), 4, 3) # Empty cell for suggested + grid_layout.addWidget(label5, 5, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input5, 5, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit5, 5, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(QWidget(), 5, 3) # Empty cell for suggested - # 6. Analysis Period + # 7. Analysis Period label5 = QLabel("Analysis Period") label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) input5 = QLineEdit() + input5.setValidator(QIntValidator(input5)) self.widgets.append(input5) input5.setAlignment(Qt.AlignmentFlag.AlignLeft) input5.setFixedWidth(field_width) input5.setText("50") input5.setStyleSheet(input1.styleSheet()) unit5 = QLabel("(years)") - grid_layout.addWidget(label5, 5, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(input5, 5, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(unit5, 5, 2, alignment=Qt.AlignVCenter) - grid_layout.addWidget(QWidget(), 5, 3) # Empty cell for suggested + suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label5, 6, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(input5, 6, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(unit5, 6, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(suggested, 6, 3) # Empty cell for suggested self.general_layout.addLayout(grid_layout) self.general_layout.addStretch(1) @@ -309,29 +328,25 @@ def close_widget(self): self.setParent(None) def collect_data(self): - data = [] - for widget in self.widgets: - if isinstance(widget, QComboBox): - value = widget.currentText() - elif isinstance(widget, QLineEdit): - value = widget.text() if widget.text() != "" else "0" - data.append(value) - # percentage - data[0] = float(data[0])/100 - data[1] = float(data[1])/100 - self.database_manager.analysis_period = float(data[5]) - - # save discount_rate and design_life - self.database_manager.discount_rate = data[0] - self.database_manager.design_life = float(data[3]) - - print("Collected Data from UI:",data) - - # calculate time cost - total_initial_construction_cost = self.parent.results.get(COST_TOTAL_INIT_CONST) - time_cost = self.database_manager.calculate_time_cost(data, total_initial_construction_cost) - # Update Results Dict - self.parent.results[COST_TIME] = time_cost + from pprint import pprint + data = { + KEY_DISCOUNT_RATE_IA: 0.0 if not self.widgets[0].text() else float(self.widgets[0].text())/100, + KEY_INFLATION_RATE: 0.0 if not self.widgets[1].text() else float(self.widgets[1].text())/100, + KEY_INTEREST_RATE: 0.0 if not self.widgets[2].text() else float(self.widgets[2].text())/100, + KEY_INVESTMENT_RATIO: 0.0 if not self.widgets[3].text() else float(self.widgets[3].text()), + KEY_DESIGN_LIFE: 0 if not self.widgets[4].text() else int(self.widgets[4].text()), + KEY_CONSTR_TIME: 0.0 if not self.widgets[5].text() else float(self.widgets[5].text()), + KEY_ANALYSIS_PERIOD: 0 if not self.widgets[6].text() else int(self.widgets[6].text()), + } + + print("\nCollected Data from Financial UI:") + pprint(data) + + # Save UI Data to Backend + self.database_manager.financial_data = data + + # calculate Time Cost + self.database_manager.calculate_time_cost() #----------------Standalone-Test-Code-------------------------------- diff --git a/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py b/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py index 3991934..eae54be 100644 --- a/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py +++ b/src/osbridgelcca/desktop_app/widgets/maintenance_repair_data.py @@ -1,7 +1,7 @@ from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QCoreApplication, Qt, QSize, Signal -from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy, QFrame) -from PySide6.QtGui import QIcon +from PySide6.QtWidgets import (QHBoxLayout, QPushButton, QLineEdit, QComboBox, QGridLayout, QWidget, QLabel, QVBoxLayout, QScrollArea, QSpacerItem, QSizePolicy) +from PySide6.QtGui import QIcon, QIntValidator from .utils.data import * import sys import os @@ -23,7 +23,7 @@ def __init__(self, database, parent=None): super().__init__() self.parent = parent self.database_manager = database - self.widget = [] + self.widgets = [] self.component_widgets = [] @@ -145,6 +145,8 @@ def __init__(self, database, parent=None): /* (Removed: QComboBox and material grid element CSS) */ """) + + self.setObjectName("central_panel_widget") left_panel_vlayout = QVBoxLayout(self) left_panel_vlayout.setContentsMargins(0, 0, 0, 0) @@ -174,33 +176,11 @@ def __init__(self, database, parent=None): field_width = 200 - # 1. Periodic Maintenance Cost rate as percentage to total construction cost - label1 = QLabel("Periodic Maintenance Cost rate as\npercentage to total construction\ncost") - label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) - pmc_input = QLineEdit() - self.widget.append(pmc_input) - pmc_input.setAlignment(Qt.AlignmentFlag.AlignTop) - pmc_input.setFixedWidth(field_width) - pmc_input.setText("0.55") - pmc_input.setStyleSheet(""" - QLineEdit { - border: 1px solid #DDDCE0; - border-radius: 10px; - padding: 3px 10px; - } - """) - pmc_unit = QLabel("(%)") - pmc_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") - grid_layout.addWidget(label1, 0, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(pmc_input, 0, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(pmc_unit, 0, 2, alignment=Qt.AlignVCenter) - grid_layout.addWidget(pmc_suggested, 0, 3, alignment=Qt.AlignVCenter) - - # 2. Annual Routine Inspection cost rate as percentage of total construction cost - label2 = QLabel("Annual Routine Inspection cost rate\nas percentage of total construction\ncost") + # 1. Annual Routine Inspection cost rate as percentage of total construction cost + label2 = QLabel("Routine Inspection Cost Rate\n(Percentage of Initial Construction Cost)") label2.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) ari_input = QLineEdit() - self.widget.append(ari_input) + self.widgets.append(ari_input) ari_input.setAlignment(Qt.AlignmentFlag.AlignTop) ari_input.setFixedWidth(field_width) ari_input.setText("1") @@ -213,38 +193,62 @@ def __init__(self, database, parent=None): """) ari_unit = QLabel("(%)") ari_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") - grid_layout.addWidget(label2, 1, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(ari_input, 1, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(ari_unit, 1, 2, alignment=Qt.AlignVCenter) - grid_layout.addWidget(ari_suggested, 1, 3, alignment=Qt.AlignVCenter) + grid_layout.addWidget(label2, 0, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(ari_input, 0, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(ari_unit, 0, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(ari_suggested, 0, 3, alignment=Qt.AlignVCenter) - # 3. Repair and Rehabilitation cost rate as percentage of total construction cost - label3 = QLabel("Repair and Rehabilitation cost rate as\npercentage of total construction cost") - label3.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) - rr_input = QLineEdit() - self.widget.append(rr_input) - rr_input.setAlignment(Qt.AlignmentFlag.AlignTop) - rr_input.setFixedWidth(field_width) - rr_input.setText("10") - rr_input.setStyleSheet(""" + # 2. Routine Inspection Frequency + label5 = QLabel("Routine Inspection Frequency") + label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + fri_input = QLineEdit() + fri_input.setValidator(QIntValidator(fri_input)) + self.widgets.append(fri_input) + fri_input.setAlignment(Qt.AlignmentFlag.AlignTop) + fri_input.setFixedWidth(field_width) + fri_input.setText("1") + fri_input.setStyleSheet(""" QLineEdit { border: 1px solid #DDDCE0; border-radius: 10px; padding: 3px 10px; } """) - rr_unit = QLabel("(%)") - rr_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") - grid_layout.addWidget(label3, 2, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(rr_input, 2, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(rr_unit, 2, 2, alignment=Qt.AlignVCenter) - grid_layout.addWidget(rr_suggested, 2, 3, alignment=Qt.AlignVCenter) + fri_unit = QLabel("(years)") + fri_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label5, 1, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_input, 1, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_unit, 1, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_suggested, 1, 3, alignment=Qt.AlignVCenter) + + # 3. Periodic Maintenance Cost Rate + label1 = QLabel("Periodic Maintenance Cost\n(Percentage of Initial Construction Cost)") + label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + pmc_input = QLineEdit() + self.widgets.append(pmc_input) + pmc_input.setAlignment(Qt.AlignmentFlag.AlignTop) + pmc_input.setFixedWidth(field_width) + pmc_input.setText("0.55") + pmc_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + pmc_unit = QLabel("(%)") + pmc_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label1, 2, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_input, 2, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_unit, 2, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_suggested, 2, 3, alignment=Qt.AlignVCenter) - # 4. Frequency of Periodic Maintenance - label4 = QLabel("Frequency of Periodic Maintenance") + # 4. Periodic Maintenance Frequency + label4 = QLabel("Periodic Maintenance Frequency") label4.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) fpm_input = QLineEdit() - self.widget.append(fpm_input) + fri_input.setValidator(QIntValidator(fri_input)) + self.widgets.append(fpm_input) fpm_input.setAlignment(Qt.AlignmentFlag.AlignTop) fpm_input.setFixedWidth(field_width) fpm_input.setText("5") @@ -262,14 +266,37 @@ def __init__(self, database, parent=None): grid_layout.addWidget(fpm_unit, 3, 2, alignment=Qt.AlignVCenter) grid_layout.addWidget(fpm_suggested, 3, 3, alignment=Qt.AlignVCenter) - # 5. Frequency of Routine Inspection - label5 = QLabel("Frequency of Routine Inspection") + # 5. Major Inspection Cost Rate + label1 = QLabel("Major Inspection Cost\n(Percentage of Initial Construction Cost)") + label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + pmc_input = QLineEdit() + self.widgets.append(pmc_input) + pmc_input.setAlignment(Qt.AlignmentFlag.AlignTop) + pmc_input.setFixedWidth(field_width) + pmc_input.setText("0.5") + pmc_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + pmc_unit = QLabel("(%)") + pmc_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label1, 4, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_input, 4, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_unit, 4, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_suggested, 4, 3, alignment=Qt.AlignVCenter) + + # 6. Major Inspection Frequency + label5 = QLabel("Major Inspection Frequency") label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) fri_input = QLineEdit() - self.widget.append(fri_input) + fri_input.setValidator(QIntValidator(fri_input)) + self.widgets.append(fri_input) fri_input.setAlignment(Qt.AlignmentFlag.AlignTop) fri_input.setFixedWidth(field_width) - fri_input.setText("1") + fri_input.setText("5") fri_input.setStyleSheet(""" QLineEdit { border: 1px solid #DDDCE0; @@ -278,19 +305,44 @@ def __init__(self, database, parent=None): } """) fri_unit = QLabel("(years)") - fri_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") - grid_layout.addWidget(label5, 4, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(fri_input, 4, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(fri_unit, 4, 2, alignment=Qt.AlignVCenter) - grid_layout.addWidget(fri_suggested, 4, 3, alignment=Qt.AlignVCenter) + pmc_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label5, 5, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_input, 5, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_unit, 5, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_suggested, 5, 3, alignment=Qt.AlignVCenter) - # 6. Frequency of Major Repairs - label5 = QLabel("Frequency of Major Repairs") + # 5. Major Repair Cost Rate + label1 = QLabel("Major Repair Cost\n(Percentage of Initial Construction Cost)") + label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + pmc_input = QLineEdit() + self.widgets.append(pmc_input) + pmc_input.setAlignment(Qt.AlignmentFlag.AlignTop) + pmc_input.setFixedWidth(field_width) + pmc_input.setText("10") + pmc_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + pmc_unit = QLabel("(%)") + pmc_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label1, 6, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_input, 6, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_unit, 6, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_suggested, 6, 3, alignment=Qt.AlignVCenter) + + + # 6. Major Repairs Frequency + label5 = QLabel("Major Repair Frequency") label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) fri_input = QLineEdit() - self.widget.append(fri_input) + fri_input.setValidator(QIntValidator(fri_input)) + self.widgets.append(fri_input) fri_input.setAlignment(Qt.AlignmentFlag.AlignTop) fri_input.setFixedWidth(field_width) + fri_input.setText("20") fri_input.setStyleSheet(""" QLineEdit { border: 1px solid #DDDCE0; @@ -299,10 +351,56 @@ def __init__(self, database, parent=None): } """) fri_unit = QLabel("(years)") - grid_layout.addWidget(label5, 5, 0, alignment=Qt.AlignVCenter) - grid_layout.addWidget(fri_input, 5, 1, alignment=Qt.AlignVCenter) - grid_layout.addWidget(fri_unit, 5, 2, alignment=Qt.AlignVCenter) - grid_layout.addWidget(QLabel(), 5, 3, alignment=Qt.AlignVCenter) + pmc_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label5, 7, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_input, 7, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_unit, 7, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_suggested, 7, 3, alignment=Qt.AlignVCenter) + + # 7. Replacement cost of bearing and expansion joint + label3 = QLabel("Replacement cost of bearing and expansion joint\n(Percentage of Initial Construction Cost of the Superstructure)") + label3.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + rr_input = QLineEdit() + self.widgets.append(rr_input) + rr_input.setAlignment(Qt.AlignmentFlag.AlignTop) + rr_input.setFixedWidth(field_width) + rr_input.setText("12.5") + rr_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + rr_unit = QLabel("(%)") + rr_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label3, 8, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(rr_input, 8, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(rr_unit, 8, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(rr_suggested, 8, 3, alignment=Qt.AlignVCenter) + + # 8. Frequency of Repair cost of bearing and expansion joints + label5 = QLabel("Frequency of Repair cost of bearing and expansion joints") + label5.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + fri_input = QLineEdit() + fri_input.setValidator(QIntValidator(fri_input)) + self.widgets.append(fri_input) + fri_input.setAlignment(Qt.AlignmentFlag.AlignTop) + fri_input.setFixedWidth(field_width) + fri_input.setText("25") + fri_input.setStyleSheet(""" + QLineEdit { + border: 1px solid #DDDCE0; + border-radius: 10px; + padding: 3px 10px; + } + """) + fri_unit = QLabel("(years)") + pmc_suggested = QLabel("Suggested", parent=self.general_widget, styleSheet="color: #B3AEAE; font-size: 10px;") + grid_layout.addWidget(label5, 9, 0, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_input, 9, 1, alignment=Qt.AlignVCenter) + grid_layout.addWidget(fri_unit, 9, 2, alignment=Qt.AlignVCenter) + grid_layout.addWidget(pmc_suggested, 9, 3, alignment=Qt.AlignVCenter) self.general_layout.addLayout(grid_layout) self.general_layout.addStretch(1) @@ -343,40 +441,51 @@ def close_widget(self): def collect_data(self): - data = [] - for widget in self.widget: - if isinstance(widget, QComboBox): - value = widget.currentText() - elif isinstance(widget, QLineEdit): - value = widget.text() if widget.text() != "" else "0" - data.append(value) - - data[0] = float(data[0])/100 - data[1] = float(data[1])/100 - data[2] = float(data[2])/100 + from pprint import pprint + data = { + KEY_ROUTINE_INSP_COST: 0.0 if not self.widgets[0].text() else float(self.widgets[0].text())/100, + KEY_ROUTINE_INSP_FREQ: 0 if not self.widgets[1].text() else int(self.widgets[1].text()), + KEY_PERIODIC_MAINT_COST: 0.0 if not self.widgets[2].text() else float(self.widgets[2].text())/100, + KEY_PERIODIC_MAINT_FREQ: 0 if not self.widgets[3].text() else int(self.widgets[3].text()), + KEY_MAJOR_INSP_COST: 0.0 if not self.widgets[4].text() else float(self.widgets[4].text())/100, + KEY_MAJOR_INSP_FREQ: 0 if not self.widgets[5].text() else int(self.widgets[5].text()), + KEY_MAJOR_REPAIR_COST: 0.0 if not self.widgets[6].text() else float(self.widgets[6].text())/100, + KEY_MAJOR_REPAIR_FREQ: 0 if not self.widgets[7].text() else int(self.widgets[7].text()), + KEY_BEARING_EXP_JOINT_REPAIR_COST: 0.0 if not self.widgets[8].text() else float(self.widgets[8].text())/100, + KEY_BEARING_EXP_JOINT_REPAIR_FREQ: 0 if not self.widgets[9].text() else int(self.widgets[9].text()) + } + print("\nCollected Data from Maintainance UI:") + pprint(data) + + # Save UI Data to Backend + self.database_manager.maintainance_and_repair_data = data - print("Collected Data from UI:",data) + # Routine Inspection Cost Calculation + self.database_manager.routine_inspection_cost() # Periodic Maintenance Cost Calculation - total_initial_construction_cost = self.parent.results.get(COST_TOTAL_INIT_CONST) - cost = self.database_manager.periodic_maintainance_cost(data, total_initial_construction_cost) - # Update Results Dict - self.parent.results[COST_PERIODIC_MAINTAINANCE] = cost + self.database_manager.periodic_maintainance_cost() + + # Periodic Maintenance Carbon Emission Cost Calculation + self.database_manager.periodic_maintainance_carbon_emission_cost() + + # Major Inspection Cost + self.database_manager.major_inspection_cost() + + # Major Repair Cost + self.database_manager.major_repair_cost() + + # Major Repair Related Carbon Emisson Cost + self.database_manager.major_repair_related_carbon_emission_cost() - # Routine Inspection Cost Calculation - cost = self.database_manager.routine_inspection_cost(data, total_initial_construction_cost) - # Update Results Dict - self.parent.results[COST_TOTAL_ROUTINE_INSPECTION] = cost - - # Repair and Rehabilitation Cost Calculation - cost = self.database_manager.repair_and_rehabilitation_cost(total_initial_construction_cost, data) - # Update Results Dict - self.parent.results[COST_REPAIR_REHAB] = cost - - # Periodic Maintenance Carbon Emission Cost Calculation (Concrete only) - cost = self.database_manager.periodic_maintainance_carbon_emission_cost(data) - # Update Results Dict - self.parent.results[COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION] = cost + # 9. Replacement cost of Bearing and Expansion Joints + self.database_manager.bearing_expansion_joint_replacement_cost() + + # Carbon Emission due to rerouting during Major Repairs + self.database_manager.carbon_emission_rerouting_during_major_repairs() + + # Carbon Emission due to rerouting during Replacement + self.database_manager.carbon_emission_rerouting_during_replacement() #----------------Standalone-Test-Code-------------------------------- diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py index 3167490..91bfd68 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/auxiliary_works_widget.py @@ -51,7 +51,7 @@ def collect_data(self): KEY_RATE_DATA_SOURCE: rate_data_source } rows_data.append(row_dict) - return rows_data + return rows_data def _on_value_changed(self, *_args): if self._initializing: @@ -178,14 +178,14 @@ def add_material_row(self): type_material_combo = QComboBox() type_material_combo.addItems(materials) type_material_combo.setObjectName("MaterialGridInput") - type_material_combo.setFixedWidth(fixed_input_width) + type_material_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) row_widgets[KEY_TYPE] = type_material_combo # Grade ComboBox grade_combo = QComboBox() grade_combo.setObjectName("MaterialGridInput") - grade_combo.setFixedWidth(fixed_input_width) + grade_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) row_widgets[KEY_GRADE] = grade_combo grade_combo.currentTextChanged.connect(self._on_value_changed) @@ -195,7 +195,7 @@ def add_material_row(self): quantity_edit.setValidator(validator) quantity_edit.setPlaceholderText("0") quantity_edit.setObjectName("MaterialGridInput") - quantity_edit.setFixedWidth(fixed_input_width) + quantity_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) row_widgets[KEY_QUANTITY] = quantity_edit quantity_edit.textChanged.connect(self._on_value_changed) @@ -203,7 +203,7 @@ def add_material_row(self): # Unit unit_combo_m3 = QComboBox() unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width) + unit_combo_m3.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) row_widgets[KEY_UNIT_M3] = unit_combo_m3 unit_combo_m3.currentTextChanged.connect(self._on_value_changed) @@ -213,7 +213,7 @@ def add_material_row(self): rate_edit.setValidator(validator) rate_edit.setPlaceholderText("0.00") rate_edit.setObjectName("MaterialGridInput") - rate_edit.setFixedWidth(fixed_input_width) + rate_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) row_widgets[KEY_RATE] = rate_edit rate_edit.textChanged.connect(self._on_value_changed) @@ -221,7 +221,7 @@ def add_material_row(self): # Rate Data Source rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") - rate_data_source_edit.setFixedWidth(fixed_input_width) + rate_data_source_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit rate_data_source_edit.textChanged.connect(self._on_value_changed) @@ -466,7 +466,7 @@ def __init__(self, database, parent=None): } QPushButton#lock_button { - background-color: #FFFFFF; + background: transparent; border: 1px solid #E0E0E0; border-radius: 12px; color: #3F3E5E; @@ -475,17 +475,17 @@ def __init__(self, database, parent=None): font-weight: bold; } QPushButton#lock_button:hover { - background-color: #F8F8F8; + background: transparent; border-color: #C0C0C0; } QPushButton#lock_button[locked="true"] { - background-color: #FFE0E0; + background: transparent; border-color: #FF9999; color: #CC0000; } QPushButton#lock_button[locked="false"] { - background-color: #E0FFE0; - border-color: #99FF99; + background: transparent; + border-color: #45913E; color: #00AA00; } @@ -689,17 +689,17 @@ def mark_state_changed(self): self.state_changed = True def save_data(self): + from pprint import pprint data = self.collect_data() - print("\nCollected Data from UI:",data) + print("\nCollected Data from Auxiliary Works UI:") + pprint(data) if self.data_id: self.data_id = self.database_manager.replace_structure_work_rows(KEY_AUXILIARY, data, self.data_id) else: self.data_id = self.database_manager.input_data_row(KEY_AUXILIARY, data) # calculating total initial cost - total_init_cost = self.database_manager.calculate_total_initial_cost() - # Update Results Dict - self.parent.results[COST_TOTAL_INIT_CONST] = float(total_init_cost) + self.database_manager.calculate_total_initial_cost() self.state_changed = False def on_next_clicked(self): diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py index abed294..fd40006 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/foundation_widget.py @@ -167,13 +167,13 @@ def add_material_row(self): type_material_combo = QComboBox() type_material_combo.setObjectName("MaterialGridInput") - type_material_combo.setFixedWidth(fixed_input_width) + type_material_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) row_widgets[KEY_TYPE] = type_material_combo grade_combo = QComboBox() grade_combo.setObjectName("MaterialGridInput") - grade_combo.setFixedWidth(fixed_input_width) + grade_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) row_widgets[KEY_GRADE] = grade_combo grade_combo.currentTextChanged.connect(self._on_value_changed) @@ -182,14 +182,14 @@ def add_material_row(self): quantity_edit.setValidator(validator) quantity_edit.setPlaceholderText("0") quantity_edit.setObjectName("MaterialGridInput") - quantity_edit.setFixedWidth(fixed_input_width) + quantity_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) row_widgets[KEY_QUANTITY] = quantity_edit quantity_edit.textChanged.connect(self._on_value_changed) unit_combo_m3 = QComboBox() unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width) + unit_combo_m3.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) row_widgets[KEY_UNIT_M3] = unit_combo_m3 unit_combo_m3.currentTextChanged.connect(self._on_value_changed) @@ -198,14 +198,14 @@ def add_material_row(self): rate_edit.setValidator(validator) rate_edit.setPlaceholderText("0.00") rate_edit.setObjectName("MaterialGridInput") - rate_edit.setFixedWidth(fixed_input_width) + rate_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) row_widgets[KEY_RATE] = rate_edit rate_edit.textChanged.connect(self._on_value_changed) rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") - rate_data_source_edit.setFixedWidth(fixed_input_width) + rate_data_source_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit rate_data_source_edit.textChanged.connect(self._on_value_changed) @@ -447,7 +447,7 @@ def __init__(self, database, parent=None): } QPushButton#lock_button { - background-color: #FFFFFF; + background: transparent; border: 1px solid #E0E0E0; border-radius: 12px; color: #3F3E5E; @@ -456,24 +456,27 @@ def __init__(self, database, parent=None): font-weight: bold; } QPushButton#lock_button:hover { - background-color: #F8F8F8; + background: transparent; border-color: #C0C0C0; } QPushButton#lock_button[locked="true"] { - background-color: #FFE0E0; + background: transparent; border-color: #FF9999; color: #CC0000; } QPushButton#lock_button[locked="false"] { - background-color: #E0FFE0; - border-color: #99FF99; + background: transparent; + border-color: #45913E; color: #00AA00; } - + QLineEdit { + text-align: center; + } QComboBox { border: 1px solid #DDDCE0; border-radius: 10px; padding: 3px 10px; + text-align: center; } QComboBox::drop-down { border: none; @@ -634,8 +637,11 @@ def mark_state_changed(self): self.state_changed = True def save_data(self): + from pprint import pprint data = self.collect_data() - print("\nCollected Data from UI:",data) + print("\nCollected Data from Foundation UI:") + pprint(data) + if self.data_id: self.data_id = self.database_manager.replace_structure_work_rows(KEY_FOUNDATION, data, self.data_id) else: diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py index a52a468..a08b142 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/sub_structure_widget.py @@ -168,13 +168,13 @@ def add_material_row(self): type_material_combo = QComboBox() type_material_combo.setObjectName("MaterialGridInput") - type_material_combo.setFixedWidth(fixed_input_width) + type_material_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) row_widgets[KEY_TYPE] = type_material_combo grade_combo = QComboBox() grade_combo.setObjectName("MaterialGridInput") - grade_combo.setFixedWidth(fixed_input_width) + grade_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) row_widgets[KEY_GRADE] = grade_combo grade_combo.currentTextChanged.connect(self._on_value_changed) @@ -183,14 +183,14 @@ def add_material_row(self): quantity_edit.setValidator(validator) quantity_edit.setPlaceholderText("0") quantity_edit.setObjectName("MaterialGridInput") - quantity_edit.setFixedWidth(fixed_input_width) + quantity_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) row_widgets[KEY_QUANTITY] = quantity_edit quantity_edit.textChanged.connect(self._on_value_changed) unit_combo_m3 = QComboBox() unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width) + unit_combo_m3.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) row_widgets[KEY_UNIT_M3] = unit_combo_m3 unit_combo_m3.currentTextChanged.connect(self._on_value_changed) @@ -199,14 +199,14 @@ def add_material_row(self): rate_edit.setValidator(validator) rate_edit.setPlaceholderText("0.00") rate_edit.setObjectName("MaterialGridInput") - rate_edit.setFixedWidth(fixed_input_width) + rate_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) row_widgets[KEY_RATE] = rate_edit rate_edit.textChanged.connect(self._on_value_changed) rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") - rate_data_source_edit.setFixedWidth(fixed_input_width) + rate_data_source_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit rate_data_source_edit.textChanged.connect(self._on_value_changed) @@ -447,7 +447,7 @@ def __init__(self, database, parent=None): } QPushButton#lock_button { - background-color: #FFFFFF; + background: transparent; border: 1px solid #E0E0E0; border-radius: 12px; color: #3F3E5E; @@ -456,17 +456,17 @@ def __init__(self, database, parent=None): font-weight: bold; } QPushButton#lock_button:hover { - background-color: #F8F8F8; + background: transparent; border-color: #C0C0C0; } QPushButton#lock_button[locked="true"] { - background-color: #FFE0E0; + background: transparent; border-color: #FF9999; color: #CC0000; } QPushButton#lock_button[locked="false"] { - background-color: #E0FFE0; - border-color: #99FF99; + background: transparent; + border-color: #45913E; color: #00AA00; } @@ -671,8 +671,11 @@ def mark_state_changed(self): self.state_changed = True def save_data(self): + from pprint import pprint data = self.collect_data() - print("\nCollected Data from UI:",data) + print("\nCollected Data from Sub-Structure UI:") + pprint(data) + if self.data_id: self.data_id = self.database_manager.replace_structure_work_rows(KEY_SUBSTRUCTURE, data, self.data_id) else: diff --git a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py index 19fe4d6..c9a375b 100644 --- a/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py +++ b/src/osbridgelcca/desktop_app/widgets/structure_works_data/super_structure_widget.py @@ -169,13 +169,13 @@ def add_material_row(self): type_material_combo = QComboBox() type_material_combo.setObjectName("MaterialGridInput") - type_material_combo.setFixedWidth(fixed_input_width) + type_material_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(type_material_combo, row_idx, 0) row_widgets[KEY_TYPE] = type_material_combo grade_combo = QComboBox() grade_combo.setObjectName("MaterialGridInput") - grade_combo.setFixedWidth(fixed_input_width) + grade_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(grade_combo, row_idx, 1) row_widgets[KEY_GRADE] = grade_combo grade_combo.currentTextChanged.connect(self._on_value_changed) @@ -184,14 +184,14 @@ def add_material_row(self): quantity_edit.setValidator(validator) quantity_edit.setPlaceholderText("0") quantity_edit.setObjectName("MaterialGridInput") - quantity_edit.setFixedWidth(fixed_input_width) + quantity_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(quantity_edit, row_idx, 2) row_widgets[KEY_QUANTITY] = quantity_edit quantity_edit.textChanged.connect(self._on_value_changed) unit_combo_m3 = QComboBox() unit_combo_m3.setObjectName("MaterialGridInput") - unit_combo_m3.setFixedWidth(fixed_input_width) + unit_combo_m3.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(unit_combo_m3, row_idx, 3) row_widgets[KEY_UNIT_M3] = unit_combo_m3 unit_combo_m3.currentTextChanged.connect(self._on_value_changed) @@ -200,14 +200,14 @@ def add_material_row(self): rate_edit.setValidator(validator) rate_edit.setPlaceholderText("0.00") rate_edit.setObjectName("MaterialGridInput") - rate_edit.setFixedWidth(fixed_input_width) + rate_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(rate_edit, row_idx, 4) row_widgets[KEY_RATE] = rate_edit rate_edit.textChanged.connect(self._on_value_changed) rate_data_source_edit = QLineEdit() rate_data_source_edit.setObjectName("MaterialGridInput") - rate_data_source_edit.setFixedWidth(fixed_input_width) + rate_data_source_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.material_grid_layout.addWidget(rate_data_source_edit, row_idx, 5) row_widgets[KEY_RATE_DATA_SOURCE] = rate_data_source_edit rate_data_source_edit.textChanged.connect(self._on_value_changed) @@ -449,7 +449,7 @@ def __init__(self, database, parent=None): } QPushButton#lock_button { - background-color: #FFFFFF; + background: transparent; border: 1px solid #E0E0E0; border-radius: 12px; color: #3F3E5E; @@ -458,17 +458,17 @@ def __init__(self, database, parent=None): font-weight: bold; } QPushButton#lock_button:hover { - background-color: #F8F8F8; + background: transparent; border-color: #C0C0C0; } QPushButton#lock_button[locked="true"] { - background-color: #FFE0E0; + background: transparent; border-color: #FF9999; color: #CC0000; } QPushButton#lock_button[locked="false"] { - background-color: #E0FFE0; - border-color: #99FF99; + background: transparent; + border-color: #45913E; color: #00AA00; } @@ -673,8 +673,11 @@ def mark_state_changed(self): self.state_changed = True def save_data(self): + from pprint import pprint data = self.collect_data() - print("\nCollected Data from UI:",data) + print("\nCollected Data from Super-Structure UI:") + pprint(data) + if self.data_id: self.data_id = self.database_manager.replace_structure_work_rows(KEY_SUPERSTRUCTURE, data, self.data_id) else: diff --git a/src/osbridgelcca/desktop_app/widgets/utils/a.py b/src/osbridgelcca/desktop_app/widgets/utils/a.py new file mode 100644 index 0000000..c7f5c7f --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/a.py @@ -0,0 +1,4 @@ +i=0.00515 +r=0.0067 +a=sum(((1 + i) / (1 + r)) ** period for period in range(1, 50, 1)) +print(a) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/utils/cost_component.py b/src/osbridgelcca/desktop_app/widgets/utils/cost_component.py index 48310b9..1396690 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/cost_component.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/cost_component.py @@ -1,252 +1,255 @@ -# 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, present_worth_factor): -# """ -# 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 present_worth_factor: Discounting factor for future costs (present_worth_factor) -# """ -# self.amount = amount -# self.category = category -# self.is_initial = is_initial -# self.is_recurring = is_recurring -# self.present_worth_factor = present_worth_factor - -# @abstractmethod -# def calculate_cost(self): -# """Abstract method to be implemented by subclasses for cost calculation.""" -# pass - - -# class InitialConstructionCost(CostComponent): -# """Covers material, labor, and equipment costs for bridge construction.""" - -# 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 - - -# class InitialCarbonEmissionCost(CostComponent): -# """Calculates initial carbon emissions from material production and transport.""" +from abc import ABC, abstractmethod +import os +import sqlite3 +from typing import Optional, List, Dict, Any +from dataclasses import dataclass, field -# 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 +class CostComponent(ABC): + """Abstract Base Class for different cost components in Life Cycle Cost Analysis.""" -# def calculate_cost(self): -# return (self.material_quantity * self.carbon_emission_factor) * self.carbon_cost * self.present_worth_factor + def __init__(self, amount, category, is_initial, is_recurring, present_worth_factor): + """ + 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 present_worth_factor: Discounting factor for future costs (present_worth_factor) + """ + self.amount = amount + self.category = category + self.is_initial = is_initial + self.is_recurring = is_recurring + self.present_worth_factor = present_worth_factor -# class TimeCost(CostComponent): -# """Calculates economic losses due to construction delays.""" + @abstractmethod + def calculate_cost(self): + """Abstract method to be implemented by subclasses for cost calculation.""" + pass -# 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 +class InitialConstructionCost: + """Calculation for all Structural works data""" + def __init__(self): + self.present_worth_factor = 1.0 -# def calculate_cost(self): -# return self.construction_cost * self.interest_rate * self.time * self.investment_ratio * self.present_worth_factor + def calculate_cost(self, quantity: float, rate: float) -> float: + return quantity * rate * self.present_worth_factor +class InitialCarbonEmissionCost: + """Calculates initial carbon emissions from material production and transport.""" + def __init__(self): + self.present_worth_factor = 1.00 -# class RoadUserCost(CostComponent): -# """Evaluates economic impact on road users due to delays and detours.""" + def calculate_cost(self, material_quantity: float, + carbon_emission_factor: float, + carbon_cost: float) -> float: + return material_quantity * carbon_emission_factor * carbon_cost * self.present_worth_factor -# 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 +class TimeCost: + """Calculates economic losses due to construction delays.""" + def __init__(self): + self.present_worth_factor = 1.00 -# def calculate_cost(self): -# return self.vehicles_affected * self.vehicle_operation_cost * self.construction_time * self.present_worth_factor + def calculate_cost(self, construction_cost:float, + interest_rate:float, + time:float, + investment_ratio:float) -> float: + return construction_cost * interest_rate * time * investment_ratio * self.present_worth_factor +class RoutineInspectionCost: + """Annual cost of inspections for structural integrity.""" -# class AdditionalCarbonEmissionCost(CostComponent): -# """Accounts for increased emissions from detoured traffic during bridge work.""" + def __init__(self): + self.present_worth_factor = 1.00 -# 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, inflation_rate:float, + init_construction_cost:float, + cost:float, + frequency:float, + discount_rate:float, + design_life:float) -> float: + + print(type(frequency),type(design_life)) + + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(frequency, design_life, frequency)) + return init_construction_cost * self.present_worth_factor * cost -# def calculate_cost(self): -# return self.vehicles_affected * self.reroute_distance * self.co2_emission_per_km * self.carbon_cost * self.present_worth_factor +class PeriodicMaintenanceCost: + """Includes expenses for routine maintenance activities.""" + def __init__(self): + self.present_worth_factor = 1.00 -# class PeriodicMaintenanceCost(CostComponent): -# """Includes expenses for routine maintenance activities.""" + def calculate_cost(self, inflation_rate:float, + init_construction_cost:float, + cost:float, + frequency:float, + discount_rate:float, + design_life:float) -> float: + + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(frequency, design_life, frequency)) + return init_construction_cost * self.present_worth_factor * cost -# 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)) -# 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 +class MajorInspectionCost: -# def calculate_cost(self): -# return self.maintenance_cost_rate * self.construction_cost * self.present_worth_factor + def __init__(self): + self.present_worth_factor = 1.00 + def calculate_cost(self, inflation_rate:float, + init_construction_cost:float, + cost:float, + frequency:float, + discount_rate:float, + design_life:float) -> float: + + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(frequency, design_life, frequency)) + return init_construction_cost * self.present_worth_factor * cost -# class PeriodicMaintenanceCarbonCost(CostComponent): -# """Calculates emissions from maintenance activities.""" +class MajorRepairCost: -# 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)) -# 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 __init__(self): + self.present_worth_factor = 1.00 -# def calculate_cost(self): -# return self.material_quantity * self.carbon_emission_factor * self.carbon_cost * self.present_worth_factor + def calculate_cost(self, inflation_rate:float, + init_construction_cost:float, + cost:float, + frequency:float, + discount_rate:float, + design_life:float) -> float: + + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(frequency, design_life, frequency)) + return init_construction_cost * self.present_worth_factor * cost + +class BearingAndExpansionJointReplacementCost: + def __init__(self): + self.present_worth_factor = 1.00 -# class RoutineInspectionCost(CostComponent): -# """Annual cost of inspections for structural integrity.""" + def calculate_cost(self, inflation_rate:float, + total_superstructure_cost:float, + cost:float, + frequency:float, + discount_rate:float, + design_life:float) -> float: + + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(frequency, design_life, frequency)) + return total_superstructure_cost * self.present_worth_factor * cost -# def __init__(self, quantity, rate, discount_rate, design_life): -# pwf = sum(1 / ((1 + discount_rate) ** i) for i in range(1, design_life + 1)) -# 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 +class PeriodicMaintenanceCarbonCost: + """Calculates emissions from maintenance activities.""" -# def calculate_cost(self): -# return self.quantity * self.rate * self.present_worth_factor + def __init__(self): + self.present_worth_factor = 1.00 + def calculate_cost(self, inflation_rate:float, + total_carbon_emission_cost:float, + cost:float, + frequency:float, + discount_rate:float, + design_life:float) -> float: + + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(frequency, design_life, frequency)) + return total_carbon_emission_cost * self.present_worth_factor * cost -# class RepairAndRehabilitationCost(CostComponent): -# """Covers major structural repairs and retrofitting.""" +class MajorRepairRelCarbonEmissionCost: -# 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)) -# cost = repair_cost_rate * construction_cost * pwf -# super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) + def __init__(self): + self.present_worth_factor = 1.00 -# def calculate_cost(self): -# return self.amount + def calculate_cost(self, inflation_rate:float, + total_carbon_emission_cost:float, + cost:float, + frequency:float, + discount_rate:float, + design_life:float) -> float: + + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(frequency, design_life, frequency)) + return total_carbon_emission_cost * self.present_worth_factor * cost + +class CarbonEmissionDueToRerouting: + + def __init__(self): + self.present_worth_factor = 1.00 + + def calculate_cost(self, additional_rerouting_dist:float, + duration_major_repair:float, + working_days_month:int, + total_traffic:float, + scc:float, + co2_emission_per_km:float, + repair_freq:float, + inflation_rate:float, + discount_rate:float, + design_life:float) -> float: + + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(repair_freq, design_life, repair_freq)) + return total_traffic * duration_major_repair * working_days_month * scc * co2_emission_per_km * self.present_worth_factor * additional_rerouting_dist +class DemolitionCost: + """Costs incurred at the end of bridge life for demolition and disposal.""" -# class DemolitionCost(CostComponent): -# """Costs incurred at the end of bridge life for demolition and disposal.""" + def __init__(self): + self.present_worth_factor = 1.00 -# 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) + def calculate_cost(self, init_constr_cost:float, + demolition_disposal_cost:float, + analysis_period:float, + inflation_rate:float, + discount_rate:float) -> float: -# def calculate_cost(self): -# return self.amount + self.present_worth_factor = ((1 + inflation_rate) / (1 + discount_rate)) ** analysis_period + return init_constr_cost * self.present_worth_factor * demolition_disposal_cost +class DemolitionCarbonCost: -# class RecyclingCost(CostComponent): -# """Accounts for material salvage and repurposing costs.""" + def __init__(self): + self.present_worth_factor = 1.00 -# 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) + def calculate_cost(self, init_carbon_emission_cost:float, + demolition_disposal_cost:float, + analysis_period:float, + inflation_rate:float, + discount_rate:float) -> float: + + self.present_worth_factor = ((1 + inflation_rate) / (1 + discount_rate)) ** analysis_period + return init_carbon_emission_cost * self.present_worth_factor * demolition_disposal_cost -# def calculate_cost(self): -# return self.amount -# # ------------------------------------------------------------------------------------------------------------ +class DemolitionCarbonReroutingCost: -from abc import ABC, abstractmethod -import os -import sqlite3 -from typing import Optional, List, Dict, Any -from dataclasses import dataclass, field + def __init__(self): + self.present_worth_factor = 1.00 -class CostComponent(ABC): - """Abstract Base Class for different cost components in Life Cycle Cost Analysis.""" + def calculate_cost(self, additional_rerouting_dist:float, + init_constr_cost:float, + demolition_disposal_time:float, + working_days_month:int, + scc:float, + co2_emission_per_km:float, + design_life:float, + analysis_period:float, + inflation_rate:float, + discount_rate:float) -> float: - def __init__(self, amount, category, is_initial, is_recurring, present_worth_factor): - """ - Initialize a generic cost component. + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(analysis_period, design_life, analysis_period)) + return init_constr_cost * self.present_worth_factor * demolition_disposal_time * working_days_month * scc * co2_emission_per_km * additional_rerouting_dist - :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 present_worth_factor: Discounting factor for future costs (present_worth_factor) - """ - self.amount = amount - self.category = category - self.is_initial = is_initial - self.is_recurring = is_recurring - self.present_worth_factor = present_worth_factor - - @abstractmethod - def calculate_cost(self): - """Abstract method to be implemented by subclasses for cost calculation.""" - pass - - -class InitialConstructionCost(CostComponent): - """Covers material, labor, and equipment costs for bridge construction.""" - - 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 - - -class InitialCarbonEmissionCost(CostComponent): - """Calculates initial carbon emissions from material production and transport.""" +class RecyclingCost: + """Accounts for material salvage and repurposing costs.""" - def __init__(self, material_quantity, carbon_emission_factor, carbon_cost): - amount = (material_quantity * carbon_emission_factor) * carbon_cost - super().__init__(amount=amount, 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 __init__(self): + self.present_worth_factor = 1.00 - def calculate_cost(self): - return (self.material_quantity * self.carbon_emission_factor) * self.carbon_cost * self.present_worth_factor - -class TimeCost(CostComponent): - """Calculates economic losses due to construction delays.""" + def calculate_cost(self, total_material_cost:float, + material_scrap_rate:float, + material_recyclability:float, + design_life:float, + analysis_period:float, + inflation_rate:float, + discount_rate:float) -> float: - 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 - + self.present_worth_factor = sum(((1 + inflation_rate) / (1 + discount_rate)) ** period for period in range(analysis_period, design_life, analysis_period)) + return material_scrap_rate * material_recyclability * total_material_cost * self.present_worth_factor class RoadUserCost(CostComponent): """Evaluates economic impact on road users due to delays and detours.""" @@ -276,56 +279,6 @@ def __init__(self, vehicles_affected, reroute_distance, co2_emission_per_km, car def calculate_cost(self): return self.vehicles_affected * self.reroute_distance * self.co2_emission_per_km * self.carbon_cost * self.present_worth_factor - -class PeriodicMaintenanceCost(CostComponent): - """Includes expenses for routine maintenance activities.""" - - def __init__(self, maintenance_cost_rate, construction_cost, discount_rate=0, period=1, design_life=1): - pwf = sum(1 / ((1 + discount_rate) ** (i * period)) for i in range(1, int(design_life / period))) - 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 - - -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))) - 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 - - -class RoutineInspectionCost(CostComponent): - """Annual cost of inspections for structural integrity.""" - - def __init__(self, inspection_cost_rate, construction_cost, discount_rate=0, design_life=1, period=1): - pwf = ((1 + discount_rate) ** (design_life - 1)) / (discount_rate * (1 + discount_rate) ** design_life) - cost = inspection_cost_rate * construction_cost * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=True, present_worth_factor=pwf) - self.inspection_cost_rate = inspection_cost_rate - self.construction_cost = construction_cost - self.period = period - self.discount_rate = discount_rate - self.design_life = design_life - - def calculate_cost(self): - return self.inspection_cost_rate * self.construction_cost * self.present_worth_factor - - class RepairAndRehabilitationCost(CostComponent): """Covers major structural repairs and retrofitting.""" @@ -338,39 +291,6 @@ def calculate_cost(self): return self.amount -class DemolitionCost(CostComponent): - """Costs incurred at the end of bridge life for demolition and disposal.""" - - def __init__(self, demolition_rate, construction_cost=0, discount_rate=0, design_life=1): - 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) - self.demolition_rate = demolition_rate - self.construction_cost = construction_cost - self.discount_rate = discount_rate - self.design_life = design_life - - def calculate_cost(self): - return self.amount - - -class RecyclingCost(CostComponent): - """Accounts for material salvage and repurposing costs.""" - - def __init__(self, scrap_value, quantity=0, scrap_rate=1.0, discount_rate=0, design_life=1): - pwf = 1 / ((1 + discount_rate) ** design_life) - cost = scrap_value * quantity * scrap_rate * pwf - super().__init__(amount=cost, category="Economic", is_initial=False, is_recurring=False, present_worth_factor=pwf) - self.scrap_value = scrap_value - self.quantity = quantity - self.scrap_rate = scrap_rate - self.discount_rate = discount_rate - self.design_life = design_life - - def calculate_cost(self): - return self.amount - - class ReconstructionCost(CostComponent): """Accounts for partial or complete reconstruction of the bridge due to structural failures or obsolescence.""" @@ -384,8 +304,6 @@ def calculate_cost(self): - - @dataclass class UserInputs: user_materials: List[Dict[str, Any]] = field(default_factory=lambda: [ diff --git a/src/osbridgelcca/desktop_app/widgets/utils/data.py b/src/osbridgelcca/desktop_app/widgets/utils/data.py index 9e92cc0..daa5506 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/data.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/data.py @@ -1,3 +1,4 @@ +# Keys for All Type of Tabs KEY_STRUCTURE_WORKS_DATA = "Structure Works Data" KEY_FOUNDATION = "Foundation" KEY_SUBSTRUCTURE = "Sub-Structure" @@ -10,6 +11,7 @@ KEY_MAINTAINANCE_REPAIR = "Maintenance and Repair" KEY_DEMOLITION_RECYCLE = "Demolition and Recycling" +# Keys for structural works data KEY_GRADE = "grade" KEY_TYPE = "type" KEY_QUANTITY = "quantity" @@ -20,6 +22,24 @@ KEY_UNITS = "units" KEY_OPTIONS = "options" +# Keys for Financial Widget +KEY_DISCOUNT_RATE_IA = "Discount Rate(Inflation Adjusted)" +KEY_INTEREST_RATE = "Interest Rate" +KEY_INFLATION_RATE = "Inflation Rate" +KEY_INVESTMENT_RATIO = "Investment Ratio" +KEY_DESIGN_LIFE = "Design Life" +KEY_CONSTR_TIME = "Time for Construction of Base Project" +KEY_ANALYSIS_PERIOD = "Analysis Period" + +# Key Bridge and Traffic +KEY_TWO_WHEELER = "Two Wheeler" +KEY_SMALL_CARS = "Small Cars" +KEY_BIG_CARS = "Big Cars" +KEY_ORDINARY_BUS = "Ordinary Buses" +KEY_DELUXE_BUS = "Deluxe Buses" +KEY_LCV = "LCV" +KEY_HCV = "HCV" +KEY_MCV = "MCV" KEY_LANES = "lanes" KEY_ROADROUGHNESS = "road_roughness" @@ -27,24 +47,86 @@ KEY_TYPE_OF_ROAD = "type_of_road" KEY_ACCIDENT_CAT = "annual_increase" - +# Key Carbon Emission KEY_EMBODIED_CARBON_ENERGY = "embodied_carbon_energy" KEY_CARBON_EMISSION_FACTOR = "carbon_emission_factor" # Result Dictionary COST_TOTAL_INIT_CONST = "Total Initial Construction Cost" +COST_TOTAL_SUPERSTRUCTURE = "Total SuperStructure Cost" COST_TOTAL_INIT_CARBON_EMISSION = "Total Initial Carbon Emission Cost" COST_TIME = "Time Cost" +COST_CARBON_EMISSION_REROUTING_INIT = "Carbon Emission due to Rerouting during Initial Construction" COST_TOTAL_ROAD_USER = "Total Road User Cost" COST_ADDITIONAL_CARBON_EMISSION = "Additional Carbon Emission Cost" COST_PERIODIC_MAINTAINANCE = "Periodic Maintenance Cost" +COST_MAJOR_INSPECTION = "Major Inspection Cost" +COST_MAJOR_REPAIR = "Major Repair Cost" COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION = "Periodic Maintenance Carbon Emission Cost" +COST_MAJOR_REPAIR_RELATED_CARBON_EMISSION = "Major Repair Related Carbon Emisson Cost" +COST_CARBON_EMISSION_RR_DURING_MAJOR_REPAIR = "Carbon Emission due to rerouting during Major Repairs" +COST_CARBON_EMISSION_RR_DURING_REPLACEMENT = "Carbon Emission due to rerouting during Replacement" +COST_DEMOLITION_DISPOSAL_CARBON = "Demolition and Disposal related Carbon Emission" +COST_DEMOLITION_DISPOSAL_CARBON_REROUTING = "Carbon Emission due to Rerouting during Demolition and Disposal" COST_TOTAL_ROUTINE_INSPECTION = "Total Routine Inspection Cost" COST_REPAIR_REHAB = "Repair and Rehabilitation Cost" COST_DEMOLITION_DISPOSAL = "Demolition and Disposal Cost" COST_RECYCLING = "Recycling Cost" COST_RECONSTRUCTION = "Reconstruction Cost" +# Keys for Carbon Emission Cost Data +KEY_SCC = "Social Cost of Carbon" +KEY_SOURCE = "Source" +KEY_USD_T_INR = "USD to INR Conversion Factor" +SCC_NITI_Aayog = "NITI Aayog" +SCC_K_Ricke_et_al = "Social Cost of Carbon (K.Ricke et. al.)" +SCC_CUSTOM = "Custom" +KEY_SCC_OPTIONS = [SCC_NITI_Aayog, SCC_K_Ricke_et_al, SCC_CUSTOM] +SCC_SSP = "Shared Socioeconomic Pathway(SSP)" +SCC_RCP = "Representative Concentration Pathway(RCP)" +SCC_CLIMATE = "Climate" +SCC_RUN = "Run" +SCC_K_Ricke_OPTIONS = [SCC_SSP, SCC_RCP, SCC_CLIMATE, SCC_RUN, "USD to INR Conversion"] +SCC_SSP_OPTIONS = ["SSP1", "SSP2", "SSP3", "SSP4", "SSP5"] +SCC_RCP_OPTIONS = ["RCP4.5", "RCP6.0", "RCP8.5"] +SSC_CLIMATE_OPTIONS = ["Expected", "Uncertain"] +SCC_RUN_OPTIONS = ["bhm_lr", "bhm_richpoor_lr", "bhm_richpoor_sr", "bhm_sr", "djo"] + +# Maintainance and Repair +KEY_ROUTINE_INSP_COST = "Routine Inspection Cost Rate" +KEY_ROUTINE_INSP_FREQ = "Routine Inspection Frequency" + +KEY_PERIODIC_MAINT_COST = "Routine Maintainance Cost" +KEY_PERIODIC_MAINT_FREQ = "Routine Maintainance Frequency" + +KEY_MAJOR_INSP_COST = "Major Inspection Cost" +KEY_MAJOR_INSP_FREQ = "Major Inspection Frequency" +KEY_MAJOR_REPAIR_COST = "Major Repair Cost" +KEY_MAJOR_REPAIR_FREQ = "Major Repair Frequency" + +KEY_BEARING_EXP_JOINT_REPAIR_COST = "Repair cost of bearing and expansion joints" +KEY_BEARING_EXP_JOINT_REPAIR_FREQ = "Frequency of Repair cost of bearing and expansion joints" + +# Demolition and Disposal +KEY_DEMOLITION_DISPOSAL_COST = "Demolition and Disposal Cost" +KEY_STRUCT_STEEL_SCRAP_RATE = "Structural Steel Scrap Rate" +KEY_STRUCT_STEEL_RECYLABILITY = "Structural Steel Recylability" +KEY_STEEL_REBAR_SCRAP_RATE = "Steel Rebar Scrap Rate" +KEY_STEEL_REBAR_RECYLABILITY = "Steel Rebar Recylability" +KEY_PS_TENDONS_SCRAP_RATE = "Pre Stressed Tendons Scrap Rate" +KEY_PS_TENDONS_RECYLABILITY = "Pre Stressed Tendons Recylability" + +# Bridge and Traffic Data +KEY_ALTER_ROAD_CARRIAGEWAY = "Alternate Road Carriageway" +KEY_ADDIT_REROUTING_DISTANCE = "Additional Rerouting Distance" +KEY_ADDIT_TRAVEL_TIME = "Additonal Travel Time" +KEY_ROAD_ROUGHNESS = "Road Roughness" +KEY_ROAD_RISE = "Road Rise" +KEY_ROAD_FALL = "Road Fall" +KEY_ROAD_TYPE = "Type of Road" +KEY_CRASH_RATE = "Crash Rate" + + construction_materials = { KEY_FOUNDATION: { "Excavation": { @@ -100,23 +182,23 @@ "Pile": { "Steel Rebar": { KEY_GRADE: ["Fe415", "Fe500", "Fe550"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Reinforced Cement Concrete": { KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["cum", "kg"] } }, "Pile Cap": { "Steel Rebar": { KEY_GRADE: ["Fe415", "Fe500", "Fe550"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Reinforced Cement Concrete": { KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["cum", "kg"] } } }, @@ -125,12 +207,12 @@ "Pier": { "Steel Rebar": { KEY_GRADE: ["Fe415", "Fe500", "Fe550"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Reinforced Cement Concrete": { KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["cum", "kg"] }, "Paint": { KEY_GRADE: ["Epoxy", "Oil Paint", "Primer"], @@ -140,12 +222,12 @@ "Pier Cap": { "Steel Rebar": { KEY_GRADE: ["Fe415", "Fe500", "Fe550"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Reinforced Cement Concrete": { KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["cum", "kg"] }, "Paint": { KEY_GRADE: ["Epoxy", "Oil Paint", "Primer"], @@ -153,7 +235,24 @@ }, "Steel Anchor Rods": { KEY_GRADE: ["E250", "E350"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] + } + }, + "Pedestal": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["kg", "MT"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg"] + } + }, + "Bearing": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["kg", "MT"] } } }, @@ -162,17 +261,17 @@ "Girder": { "Steel Rebar": { KEY_GRADE: ["Fe415", "Fe500", "Fe550"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Reinforced Cement Concrete": { KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["cum", "kg"] }, "Pre-stressed Cement Concrete": { KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["cum", "kg"] }, "Tendons": { KEY_GRADE: [], @@ -180,7 +279,7 @@ }, "Structural Steel": { KEY_GRADE: ["E250", "E350"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Shear Connectors": { KEY_GRADE: ["E250", "E350"], @@ -191,25 +290,42 @@ KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] } }, - "Deck Slab": { - "Steel Rebar": { - KEY_GRADE: ["Fe415", "Fe500", "Fe550"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + "Deck Slab": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["kg", "MT"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg"] + } }, - "Reinforced Cement Concrete": { - KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", - "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + "Diaphragm": { + "Steel Rebar": { + KEY_GRADE: ["Fe415", "Fe500", "Fe550"], + KEY_UNITS: ["kg", "MT"] + }, + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg"] + } + }, + "Cross Bracings": { + "Reinforced Cement Concrete": { + KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", + "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], + KEY_UNITS: ["cum", "kg"] + } } - } - }, KEY_AUXILIARY: { "Bearings": { "Structural Steel": { KEY_GRADE: ["E250", "E350"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Rubber": { KEY_GRADE: [], @@ -220,15 +336,15 @@ "Reinforced Cement Concrete": { KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["cum", "kg"] }, "Structural Steel": { KEY_GRADE: ["E250", "E350"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Steel Rebar": { KEY_GRADE: ["Fe415", "Fe500", "Fe550"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "Paint": { KEY_GRADE: ["Epoxy", "Oil Paint", "Primer", "Anti-Corrosive Paint"], @@ -243,11 +359,11 @@ "Reinforced Cement Concrete": { KEY_GRADE: ["M10", "M15", "M20", "M25", "M30", "M35", "M40", "M45", "M50", "M55", "M60", "M65", "M70", "M75", "M80", "M85", "M90", "M95", "M100"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["cum", "kg"] }, "Structural Steel": { KEY_GRADE: ["E250", "E350"], - KEY_UNITS: ["cum", "kg", "MT", "rmt", "sqm", "ltr"] + KEY_UNITS: ["kg", "MT"] }, "FRP": { KEY_GRADE: [], diff --git a/src/osbridgelcca/desktop_app/widgets/utils/database.py b/src/osbridgelcca/desktop_app/widgets/utils/database.py index ba02371..d6bbb83 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/database.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/database.py @@ -1,11 +1,13 @@ import sqlite3 from typing import List, Dict, Tuple from .data import * -from .cost_component import ( InitialConstructionCost, TimeCost, RoadUserCost, - AdditionalCarbonEmissionCost, PeriodicMaintenanceCost, - RoutineInspectionCost, RepairAndRehabilitationCost, - DemolitionCost, RecyclingCost, ReconstructionCost, - PeriodicMaintenanceCarbonCost +from .cost_component import ( BearingAndExpansionJointReplacementCost, CarbonEmissionDueToRerouting, DemolitionCarbonCost, DemolitionCarbonReroutingCost, + InitialConstructionCost, MajorInspectionCost, MajorRepairCost, + MajorRepairRelCarbonEmissionCost, TimeCost, RoadUserCost, + AdditionalCarbonEmissionCost, PeriodicMaintenanceCost, + RoutineInspectionCost, RepairAndRehabilitationCost, + DemolitionCost, RecyclingCost, ReconstructionCost, + PeriodicMaintenanceCarbonCost ) class DatabaseManager: @@ -22,9 +24,49 @@ def __init__(self, db_path: str = "widgets/utils/structure_works.db", recreate: self.db_path = db_path self.conn = None self.create_database(recreate=recreate) - self.discount_rate = 0 - self.design_life = 0 - self.analysis_period = 0 + + # Data from UI + self.financial_data = {} + self.carbon_emission_cost_data = {} + self.daily_average_traffic_data = {} + self.maintainance_and_repair_data = {} + self.demolition_and_recycling_data = {} + self.traffic_data = {} + + # Total Costs + self.results = { + COST_TOTAL_INIT_CONST: 7724184.66, + COST_TOTAL_SUPERSTRUCTURE: None, + COST_TOTAL_INIT_CARBON_EMISSION: 1217668.46, + COST_TIME: None, + COST_CARBON_EMISSION_REROUTING_INIT: None, + COST_TOTAL_ROUTINE_INSPECTION: None, + COST_PERIODIC_MAINTAINANCE: None, + COST_MAJOR_INSPECTION: None, + COST_MAJOR_REPAIR: None, + COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION: None, + COST_MAJOR_REPAIR_RELATED_CARBON_EMISSION: None, + COST_CARBON_EMISSION_RR_DURING_MAJOR_REPAIR: None, + COST_DEMOLITION_DISPOSAL: None, + COST_DEMOLITION_DISPOSAL_CARBON: None, + COST_DEMOLITION_DISPOSAL_CARBON_REROUTING: None, + COST_RECYCLING: None, + + COST_TOTAL_ROAD_USER: None, + COST_ADDITIONAL_CARBON_EMISSION: None, + COST_REPAIR_REHAB: None, + COST_RECONSTRUCTION: None + } + + # Some Constants + self.CO2_EMISSION_PER_KM = 0.1213 + self.WORKING_DAYS_IN_MONTH = 26 + # Duration of Major Repairs (Month) + self.DURATION_MAJOR_REPAIRS = 3 + # Duration of Replacement (Month) + self.DURATION_REPLACEMENT = 2/self.WORKING_DAYS_IN_MONTH + # Duration of Demolition and Disposal (Month) + self.DURATION_DEMOLITION_DISPOSAL = 2 def create_database(self, recreate: bool = True): """ @@ -251,68 +293,42 @@ def delete_component(self, component_id: int): cursor = self.conn.cursor() cursor.execute('DELETE FROM component WHERE comp_id = ?', (component_id,)) self.conn.commit() - - def get_all_structure_works(self) -> List[Tuple]: - """Get summary of all structure works""" - cursor = self.conn.cursor() - cursor.execute(''' - SELECT sw.comp_id, sw.type, sw.component_type, COUNT(c.id) as component_count - FROM struct_works_data sw - LEFT JOIN component c ON sw.comp_id = c.comp_id - GROUP BY sw.comp_id - ORDER BY sw.type, sw.comp_id - ''') - return cursor.fetchall() - - def get_components_by_comp_id(self, comp_id: int) -> List[Tuple]: - """Get all component rows for a specific comp_id""" - cursor = self.conn.cursor() - cursor.execute(''' - SELECT id, comp_id, type_material, grade, quantity, unit, rate, rate_data_source - FROM component - WHERE comp_id = ? - ORDER BY id - ''', (comp_id,)) - return cursor.fetchall() - def _insert_financial_data(self, data: List[float]) -> int: + def get_all_materials_info(self) -> List[Dict]: """ - Insert a new row into the financial_data table - - Args: - data: List containing [real_discount_rate, interest_rate, investment_ratio, duration_of_study] - in the same sequence as the table columns + Retrieve all material types, grades, and quantities from the component table Returns: - id: The ID of the newly created financial data row - - Raises: - ValueError: If the data list doesn't contain exactly 4 elements + List of dictionaries containing material information with keys: + - type_material: The type of material + - grade: The grade of the material + - quantity: The quantity used + - unit: The unit of measurement """ - if len(data) != 5: - raise ValueError("Data list must contain exactly 4 elements: [real_discount_rate, interest_rate, investment_ratio, duration_of_study]") - cursor = self.conn.cursor() - - cursor.execute(''' - INSERT INTO financial_data ( - real_discount_rate, - interest_rate, - investment_ratio, - duration_of_study, - time_of_project - ) - VALUES (?, ?, ?, ?,?) - ''', data) + SELECT type_material, grade, quantity, unit, rate + FROM component + ORDER BY type_material, grade + ''') - financial_data_id = cursor.lastrowid - self.conn.commit() - return financial_data_id - - def get_all_materials_info(self) -> List[Dict]: + results = [] + for row in cursor.fetchall(): + material_info = { + KEY_TYPE: row[0], + KEY_GRADE: row[1], + KEY_QUANTITY: row[2], + KEY_UNIT_M3: row[3], + KEY_RATE: row[4] + } + results.append(material_info) + + return results + + def get_all_superstructures_data(self) -> List[Dict]: """ - Retrieve all unique material types, grades, and quantities from the component table + Retrieve all material types, grades, and quantities from the component table + only for superstructure Returns: List of dictionaries containing material information with keys: @@ -323,24 +339,27 @@ def get_all_materials_info(self) -> List[Dict]: """ cursor = self.conn.cursor() cursor.execute(''' - SELECT DISTINCT type_material, grade, quantity, unit, rate + SELECT comp_id, type_material, grade, quantity, unit, rate FROM component - ORDER BY type_material, grade - ''') + WHERE comp_id IN + (SELECT comp_id + FROM struct_works_data + WHERE type = ?); + ''', (KEY_SUPERSTRUCTURE,)) results = [] for row in cursor.fetchall(): material_info = { - 'type_material': row[0], - 'grade': row[1], - 'quantity': row[2], - 'unit': row[3], - 'rate': row[4] + KEY_TYPE: row[1], + KEY_GRADE: row[2], + KEY_QUANTITY: row[3], + KEY_UNIT_M3: row[4], + KEY_RATE: row[5] } results.append(material_info) return results - + def get_unique_materials_and_grades(self) -> List[List[str]]: """ Retrieve all unique material and grade pairs from the component table @@ -359,7 +378,10 @@ def get_unique_materials_and_grades(self) -> List[List[str]]: output =[[row[0], row[1], row[2], row[3]] for row in cursor.fetchall()] p = [] for item in output: - s = item[0] + " (" + item[1] + ")" + if item[1]: + s = item[0] + " (" + item[1] + ")" + else: + s = item[0] p.append([s, item[2], item[3], item[0], item[1]]) return p @@ -391,7 +413,6 @@ def insert_carbon_emission_data(self, data_list): data.get(KEY_UNIT_M3, ''), float(data.get(KEY_CARBON_EMISSION_FACTOR, 0)), float(data.get(KEY_EMBODIED_CARBON_ENERGY, 0)) - ) records.append(record) # Execute batch insert @@ -435,118 +456,420 @@ def close(self): #========================Calculations======================== - #(OK) 1. Called on next from Auxiliary Cost Data.py + #=================Initial-Stage-Cost-Start=================== + # 1. Initial Cost Calculation def calculate_total_initial_cost(self) -> float: + obj = InitialConstructionCost() data = self.get_all_materials_info() total_cost = 0.0 for item in data: - component = InitialConstructionCost( - quantity=item["quantity"], - rate=item["rate"] + cost = obj.calculate_cost( + quantity=item.get(KEY_QUANTITY), + rate=item.get(KEY_RATE) ) - total_cost += component.calculate_cost() - print("\nTotal Initial Construction Cost:", total_cost) + print(f"\nMaterial={item.get(KEY_TYPE)}\nQuantity={item.get(KEY_QUANTITY)}\nRate={item.get(KEY_RATE)}\nCost={cost}") + total_cost += cost + + print("\n1.Total Initial Construction Cost:", total_cost) + + # Store Total Initial cost + self.results[COST_TOTAL_INIT_CONST] = total_cost return total_cost - - #(OK) 3. Called on next from financial_data.py - def calculate_time_cost(self, data: List, total_init_construct_cost: float) -> float: - # Insert financial data into the database - self._insert_financial_data(data) - time_cost_component = TimeCost( - construction_cost=total_init_construct_cost, - interest_rate=float(data[1]), - time=float(data[4]), - investment_ratio=float(data[2]) - ) - print("\nTime Cost:", time_cost_component.calculate_cost()) - return time_cost_component.calculate_cost() - #(OK) 2. Called on next from carbon_emission_cost_data.py - def carbon_emission_cost(self, carbon_cost: float) -> float: - # Updated Carbon cost - self.carbon_cost = carbon_cost + # 2. Initial Carbon Emission Cost + def carbon_emission_cost(self) -> float: # Get carbon emission data from the database data = self.get_carbon_emission_data() + # SCC + if self.carbon_emission_cost_data.get(KEY_SOURCE) == SCC_K_Ricke_et_al: + SCC = self.carbon_emission_cost_data.get(KEY_SCC) * self.carbon_emission_cost_data.get(KEY_USD_T_INR) + else: + SCC = self.carbon_emission_cost_data.get(KEY_SCC) + + print(f"\nSCC considered is.\nSource={self.carbon_emission_cost_data.get(KEY_SOURCE)}\nSCC={SCC}") + total_carbon_emission_cost= 0.0 for item in data: qty = float(item.get(KEY_QUANTITY)) emission_factor = float(item.get(KEY_CARBON_EMISSION_FACTOR)) - carbon_emission_cost = (qty * emission_factor) * carbon_cost; + carbon_emission_cost = (qty * emission_factor) * SCC total_carbon_emission_cost += carbon_emission_cost print(f"Carbon Emission Cost for {item.get(KEY_TYPE)}:", carbon_emission_cost) - print("\nTotal Carbon Emission Cost:", total_carbon_emission_cost) + print("\n2.Total Carbon Emission Cost:", total_carbon_emission_cost) + self.results[COST_TOTAL_INIT_CARBON_EMISSION] = total_carbon_emission_cost return total_carbon_emission_cost + + # 3. Time Cost + def calculate_time_cost(self) -> float: + + component = TimeCost() + cost = component.calculate_cost( + construction_cost=self.results.get(COST_TOTAL_INIT_CONST), + interest_rate=self.financial_data.get(KEY_INTEREST_RATE), + time=self.financial_data.get(KEY_CONSTR_TIME), + investment_ratio=self.financial_data.get(KEY_INVESTMENT_RATIO) + ) + print("\n3.Time Cost: ", cost) + # Save the time cost + self.results[COST_TIME] = cost + return cost + + # Helper function to get total traffic + def _get_total_traffic(self)->float: + total_traffic = 0 + for count in self.daily_average_traffic_data.values(): + total_traffic += count + return total_traffic + + # 5. Carbon Emission due to Rerouting during Initial Construction + def init_carbon_emission_rerouting(self) -> float: + + total_traffic = self._get_total_traffic() + rerouting_dist = self.traffic_data.get(KEY_ADDIT_REROUTING_DISTANCE) + + init_const_time = self.financial_data.get(KEY_CONSTR_TIME) + SCC = self.carbon_emission_cost_data.get(KEY_SCC) + cost = total_traffic * init_const_time * 12 * self.WORKING_DAYS_IN_MONTH * SCC * self.CO2_EMISSION_PER_KM * rerouting_dist + + self.results[COST_CARBON_EMISSION_REROUTING_INIT] = cost + print(f"\n5.Carbon Emission due to Rerouting during Initial Construction. {cost}") + return cost + + #=================Initial-Stage-Cost-End=================== + + #=================Use-Stage-Cost-Start===================== + # 1. Routine Inspection Cost Calculation + def routine_inspection_cost(self) -> float: + + component = RoutineInspectionCost() + + cost = component.calculate_cost( + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + init_construction_cost=self.results.get(COST_TOTAL_INIT_CONST), + cost=self.maintainance_and_repair_data.get(KEY_ROUTINE_INSP_COST), + frequency=self.maintainance_and_repair_data.get(KEY_ROUTINE_INSP_FREQ), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_TOTAL_ROUTINE_INSPECTION] = cost + print("\n1.Routine Inspection Cost: ", cost) + return cost + + + # 2. Periodic Maintainance Cost + def periodic_maintainance_cost(self) -> float: + + component = PeriodicMaintenanceCost() + + cost = component.calculate_cost( + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + init_construction_cost=self.results.get(COST_TOTAL_INIT_CONST), + cost=self.maintainance_and_repair_data.get(KEY_PERIODIC_MAINT_COST), + frequency=self.maintainance_and_repair_data.get(KEY_PERIODIC_MAINT_FREQ), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_PERIODIC_MAINTAINANCE] = cost + print("\n2.Periodic Maintenance Cost: ", cost) + return cost + + # 3. Periodic Maintenance Carbon Emission Cost Calculation + def periodic_maintainance_carbon_emission_cost(self): + + component = PeriodicMaintenanceCarbonCost() + + cost = component.calculate_cost( + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + total_carbon_emission_cost=self.results.get(COST_TOTAL_INIT_CARBON_EMISSION), + cost=self.maintainance_and_repair_data.get(KEY_PERIODIC_MAINT_COST), + frequency=self.maintainance_and_repair_data.get(KEY_PERIODIC_MAINT_FREQ), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_PERIODIC_MAINTAINANCE_CARBON_EMISSION] = cost + print("\n3.Periodic Maintenance Carbon Emission Cost:", cost) + return cost + + # 4. Major Inspection Cost + def major_inspection_cost(self) -> float: + + component = MajorInspectionCost() + + cost = component.calculate_cost( + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + init_construction_cost=self.results.get(COST_TOTAL_INIT_CONST), + cost=self.maintainance_and_repair_data.get(KEY_MAJOR_INSP_COST), + frequency=self.maintainance_and_repair_data.get(KEY_MAJOR_INSP_FREQ), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_MAJOR_INSPECTION] = cost + print("\n4.Major Inspection Cost: ", cost) + return cost + + # 5. Major Repair Cost + def major_repair_cost(self) -> float: + + component = MajorRepairCost() + + cost = component.calculate_cost( + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + init_construction_cost=self.results.get(COST_TOTAL_INIT_CONST), + cost=self.maintainance_and_repair_data.get(KEY_MAJOR_REPAIR_COST), + frequency=self.maintainance_and_repair_data.get(KEY_MAJOR_REPAIR_FREQ), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_MAJOR_REPAIR] = cost + print("\n5.Major Repair Cost: ", cost) + return cost + + # 6. Major Repair Related Carbon Emisson Cost + def major_repair_related_carbon_emission_cost(self): + + component = MajorRepairRelCarbonEmissionCost() + + cost = component.calculate_cost( + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + total_carbon_emission_cost=self.results.get(COST_TOTAL_INIT_CARBON_EMISSION), + cost=self.maintainance_and_repair_data.get(KEY_MAJOR_REPAIR_COST), + frequency=self.maintainance_and_repair_data.get(KEY_MAJOR_REPAIR_FREQ), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_MAJOR_REPAIR_RELATED_CARBON_EMISSION] = cost + print("\n6.Major Repair Related Carbon Emisson Cost: ", cost) + return cost + + # 8. Carbon Emission due to rerouting during Major Repairs + def carbon_emission_rerouting_during_major_repairs(self): + + component = CarbonEmissionDueToRerouting() + + total_traffic = self._get_total_traffic() + SCC = self.carbon_emission_cost_data.get(KEY_SCC) + rerouting_dist = self.traffic_data.get(KEY_ADDIT_REROUTING_DISTANCE) + + cost = component.calculate_cost( + additional_rerouting_dist=rerouting_dist, + duration_major_repair=self.DURATION_MAJOR_REPAIRS, + working_days_month=self.WORKING_DAYS_IN_MONTH, + total_traffic=total_traffic, + scc=SCC, + co2_emission_per_km=self.CO2_EMISSION_PER_KM, + repair_freq=self.maintainance_and_repair_data.get(KEY_MAJOR_REPAIR_FREQ), + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_CARBON_EMISSION_RR_DURING_MAJOR_REPAIR] = cost + print("\n8.Carbon Emission due to rerouting during Major Repairs: ", cost) + return cost + + # Helper function for 9 + def _calculate_superstructure_cost(self) -> float: + obj = InitialConstructionCost() + data = self.get_all_superstructures_data() + print("\nCalculating Total of SuperStructures") + total_cost = 0.0 + print(data) + for item in data: + print(item.get(KEY_QUANTITY),item.get(KEY_RATE)) + cost = obj.calculate_cost( + quantity=item.get(KEY_QUANTITY), + rate=item.get(KEY_RATE) + ) + print(f"\nMaterial={item.get(KEY_TYPE)}\nQuantity={item.get(KEY_QUANTITY)}\nRate={item.get(KEY_RATE)}\nCost={cost}") + total_cost += cost + + print("\nTotal SuperStructures Cost:", total_cost) + + # Store Total SuperStructures cost + self.results[COST_TOTAL_SUPERSTRUCTURE] = total_cost + return total_cost + + # 9. Replacement cost of Bearing and Expansion Joints + def bearing_expansion_joint_replacement_cost(self) -> float: + + component = BearingAndExpansionJointReplacementCost() + total_superstructure_cost = self._calculate_superstructure_cost() + + cost = component.calculate_cost( + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + total_superstructure_cost=total_superstructure_cost, + cost=self.maintainance_and_repair_data.get(KEY_MAJOR_INSP_COST), + frequency=self.maintainance_and_repair_data.get(KEY_MAJOR_REPAIR_FREQ), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_MAJOR_INSPECTION] = cost + print("\n9.Replacement cost of Bearing and Expansion Joints: ", cost) + return cost + + # 11. Carbon Emission due to rerouting during Replacement + def carbon_emission_rerouting_during_replacement(self): + + component = CarbonEmissionDueToRerouting() + + total_traffic = self._get_total_traffic() + SCC = self.carbon_emission_cost_data.get(KEY_SCC) + rerouting_dist = self.traffic_data.get(KEY_ADDIT_REROUTING_DISTANCE) + + cost = component.calculate_cost( + additional_rerouting_dist=rerouting_dist, + duration_major_repair=self.DURATION_REPLACEMENT, + working_days_month=self.WORKING_DAYS_IN_MONTH, + total_traffic=total_traffic, + scc=SCC, + co2_emission_per_km=self.CO2_EMISSION_PER_KM, + repair_freq=self.maintainance_and_repair_data.get(KEY_BEARING_EXP_JOINT_REPAIR_FREQ), + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA), + design_life=self.financial_data.get(KEY_DESIGN_LIFE) + ) + + self.results[COST_CARBON_EMISSION_RR_DURING_REPLACEMENT] = cost + print("\n11.Carbon Emission due to rerouting during Replacement: ", cost) + return cost + #=================Use-Stage-Cost-End===================== + + #==========End-Of-Life-Stage-Cost-Start================== + # Helper function to get sum(quantity*rate) for given Type(Material) + def _get_total_cost_material(self, type:str)->float: + obj = InitialConstructionCost() + data = self.get_all_materials_info() + total_cost = 0.0 + for item in data: + if item.get(KEY_TYPE) == type: + cost = obj.calculate_cost( + quantity=item.get(KEY_QUANTITY), + rate=item.get(KEY_RATE) + ) + total_cost += cost + + print(f"\nMaterial={type}\nTotal Cost={total_cost}") + return total_cost + + # 1. Demolition and Disposal Cost + def demolition_and_disposal_cost(self) -> float: + + component = DemolitionCost() + + cost = component.calculate_cost( + init_constr_cost=self.results.get(COST_TOTAL_INIT_CONST), + demolition_disposal_cost=self.demolition_and_recycling_data.get(KEY_DEMOLITION_DISPOSAL_COST), + analysis_period=self.financial_data.get(KEY_ANALYSIS_PERIOD), + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA) + ) + self.results[COST_DEMOLITION_DISPOSAL] = cost + print("\n1.Demolition and Disposal Cost:", cost) + return cost + + # 2. Demolition and Disposal related Carbon Emission + def demolition_disposal_carbon_emission_cost(self) -> float: + + component = DemolitionCarbonCost() + + cost = component.calculate_cost( + init_carbon_emission_cost=self.results.get(COST_TOTAL_INIT_CARBON_EMISSION), + demolition_disposal_cost=self.demolition_and_recycling_data.get(KEY_DEMOLITION_DISPOSAL_COST), + analysis_period=self.financial_data.get(KEY_ANALYSIS_PERIOD), + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA) + ) + self.results[COST_DEMOLITION_DISPOSAL_CARBON] = cost + print("\n2.Demolition and Disposal related Carbon Emission:", cost) + return cost + + # 4. Carbon Emission due to Rerouting during Demolition and Disposal + def demolition_disposal_rerouting_carbon_emission_cost(self) -> float: + + component = DemolitionCarbonReroutingCost() + SCC = self.carbon_emission_cost_data.get(KEY_SCC) + addit_rerouting = self.traffic_data.get(KEY_ADDIT_REROUTING_DISTANCE) + + cost = component.calculate_cost( + additional_rerouting_dist=addit_rerouting, + init_constr_cost=self.results.get(COST_TOTAL_INIT_CONST), + demolition_disposal_time=self.DURATION_DEMOLITION_DISPOSAL, + working_days_month=self.WORKING_DAYS_IN_MONTH, + scc=SCC, + co2_emission_per_km=self.CO2_EMISSION_PER_KM, + design_life=self.financial_data.get(KEY_DESIGN_LIFE), + analysis_period=self.financial_data.get(KEY_ANALYSIS_PERIOD), + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA) + ) + self.results[COST_DEMOLITION_DISPOSAL_CARBON_REROUTING] = cost + print("\n4.Carbon Emission due to Rerouting during Demolition and Disposal:", cost) + return cost + + # 5. Recycling Cost + def recycling_cost(self) -> float: + total_recycling_cost = 0.0 + + steel_rebar_cost = self._get_total_cost_material(type="Steel Rebar") + struct_steel_cost = self._get_total_cost_material(type="Structural Steel") + pre_stressed_tendons_cost = self._get_total_cost_material(type="Tendons") + + component = RecyclingCost() + + #-------Steel-Rebar-Cost----------------------------------- + steel_rebar_recycle_cost = component.calculate_cost( + total_material_cost=steel_rebar_cost, + material_scrap_rate=self.demolition_and_recycling_data.get(KEY_STEEL_REBAR_SCRAP_RATE), + material_recyclability=self.demolition_and_recycling_data.get(KEY_STEEL_REBAR_RECYLABILITY), + design_life=self.financial_data.get(KEY_DESIGN_LIFE), + analysis_period=self.financial_data.get(KEY_ANALYSIS_PERIOD), + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA) + ) + print(f"\nRecycling Cost of Steel Rebar: ", steel_rebar_recycle_cost) + + #-------Structural-Steel-Cost----------------------------------- + struct_steel_recycle_cost = component.calculate_cost( + total_material_cost=struct_steel_cost, + material_scrap_rate=self.demolition_and_recycling_data.get(KEY_STRUCT_STEEL_SCRAP_RATE), + material_recyclability=self.demolition_and_recycling_data.get(KEY_STRUCT_STEEL_RECYLABILITY), + design_life=self.financial_data.get(KEY_DESIGN_LIFE), + analysis_period=self.financial_data.get(KEY_ANALYSIS_PERIOD), + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA) + ) + print(f"\nRecycling Cost of Structural Steel: ", struct_steel_recycle_cost) + + #-------PreStressed-Tendons-Cost----------------------------------- + pre_stressed_tendons_recycle_cost = component.calculate_cost( + total_material_cost=pre_stressed_tendons_cost, + material_scrap_rate=self.demolition_and_recycling_data.get(KEY_PS_TENDONS_SCRAP_RATE), + material_recyclability=self.demolition_and_recycling_data.get(KEY_PS_TENDONS_RECYLABILITY), + design_life=self.financial_data.get(KEY_DESIGN_LIFE), + analysis_period=self.financial_data.get(KEY_ANALYSIS_PERIOD), + inflation_rate=self.financial_data.get(KEY_INFLATION_RATE), + discount_rate=self.financial_data.get(KEY_DISCOUNT_RATE_IA) + ) + print(f"\nRecycling Cost of Pre Stressed Tendon Cost: ", pre_stressed_tendons_recycle_cost) + + total_recycling_cost = steel_rebar_recycle_cost + struct_steel_recycle_cost + pre_stressed_tendons_recycle_cost + + self.results[COST_RECYCLING] = total_recycling_cost + print(f"\n5.Total Recycling Cost: ", total_recycling_cost) + return total_recycling_cost + - IRC_ROAD_COSTS_DATA = { - # Format: (Vehicle_Type, Lane_Type, Roughness, RF): Grand_Cost - ("Car", "2", "Good", "Rolling"): 15.50, - ("Car", "2", "Good", "Hilly"): 18.20, - ("Car", "2", "Fair", "Rolling"): 17.80, - ("Car", "2", "Fair", "Hilly"): 20.90, - ("Car", "2", "Poor", "Rolling"): 22.40, - ("Car", "2", "Poor", "Hilly"): 26.10, - ("Car", "4", "Good", "Rolling"): 14.20, - ("Car", "4", "Good", "Hilly"): 16.80, - ("Car", "4", "Fair", "Rolling"): 16.50, - ("Car", "4", "Fair", "Hilly"): 19.40, - ("Car", "4", "Poor", "Rolling"): 21.10, - ("Car", "4", "Poor", "Hilly"): 24.70, - - ("Bus", "2", "Good", "Rolling"): 45.80, - ("Bus", "2", "Good", "Hilly"): 52.30, - ("Bus", "2", "Fair", "Rolling"): 48.90, - ("Bus", "2", "Fair", "Hilly"): 55.80, - ("Bus", "2", "Poor", "Rolling"): 56.20, - ("Bus", "2", "Poor", "Hilly"): 64.10, - ("Bus", "4", "Good", "Rolling"): 42.50, - ("Bus", "4", "Good", "Hilly"): 48.70, - ("Bus", "4", "Fair", "Rolling"): 45.60, - ("Bus", "4", "Fair", "Hilly"): 52.10, - ("Bus", "4", "Poor", "Rolling"): 53.80, - ("Bus", "4", "Poor", "Hilly"): 61.40, - - ("HCV", "2", "Good", "Rolling"): 78.90, - ("HCV", "2", "Good", "Hilly"): 89.20, - ("HCV", "2", "Fair", "Rolling"): 84.50, - ("HCV", "2", "Fair", "Hilly"): 95.60, - ("HCV", "2", "Poor", "Rolling"): 96.80, - ("HCV", "2", "Poor", "Hilly"): 109.50, - ("HCV", "4", "Good", "Rolling"): 75.40, - ("HCV", "4", "Good", "Hilly"): 85.30, - ("HCV", "4", "Fair", "Rolling"): 81.20, - ("HCV", "4", "Fair", "Hilly"): 91.80, - ("HCV", "4", "Poor", "Rolling"): 93.70, - ("HCV", "4", "Poor", "Hilly"): 106.00, - - ("MCV", "2", "Good", "Rolling"): 56.70, - ("MCV", "2", "Good", "Hilly"): 64.80, - ("MCV", "2", "Fair", "Rolling"): 61.20, - ("MCV", "2", "Fair", "Hilly"): 69.90, - ("MCV", "2", "Poor", "Rolling"): 70.50, - ("MCV", "2", "Poor", "Hilly"): 80.40, - ("MCV", "4", "Good", "Rolling"): 54.30, - ("MCV", "4", "Good", "Hilly"): 62.10, - ("MCV", "4", "Fair", "Rolling"): 58.80, - ("MCV", "4", "Fair", "Hilly"): 67.20, - ("MCV", "4", "Poor", "Rolling"): 68.20, - ("MCV", "4", "Poor", "Hilly"): 77.90, - - ("LCV", "2", "Good", "Rolling"): 38.40, - ("LCV", "2", "Good", "Hilly"): 44.10, - ("LCV", "2", "Fair", "Rolling"): 41.60, - ("LCV", "2", "Fair", "Hilly"): 47.70, - ("LCV", "2", "Poor", "Rolling"): 48.30, - ("LCV", "2", "Poor", "Hilly"): 55.40, - ("LCV", "4", "Good", "Rolling"): 36.80, - ("LCV", "4", "Good", "Hilly"): 42.30, - ("LCV", "4", "Fair", "Rolling"): 40.10, - ("LCV", "4", "Fair", "Hilly"): 46.00, - ("LCV", "4", "Poor", "Rolling"): 46.90, - ("LCV", "4", "Poor", "Hilly"): 53.80, - } + #==========End-Of-Life-Stage-Cost-End==================== # 4. Called on next from bridge_and_traffic_data.py def calculate_irc_road_cost(self, data: List) -> float: @@ -599,74 +922,6 @@ def additional_carbon_emission_cost(self, data: List) -> float: print("\nAdditional Carbon Emission Cost:", additional_carbon_emission_component.calculate_cost()) # INR return additional_carbon_emission_component.calculate_cost() - # 6. Called on next from maintainance_repair_data.py - def periodic_maintainance_cost(self, data: List, total_initial_cost) -> float: - maintenance_cost_rate = PeriodicMaintenanceCost( - maintenance_cost_rate=float(data[0]), - construction_cost=total_initial_cost, - discount_rate=self.discount_rate, - period=float(data[3]), - design_life=self.design_life - ) - print("\nPeriodic Maintenance Cost:", maintenance_cost_rate.calculate_cost()) # INR - - return maintenance_cost_rate.calculate_cost() - - # 7. Periodic Maintenance Carbon Emission Cost Calculation (Concrete only) - def periodic_maintainance_carbon_emission_cost(self, data: List): - unit_conversions = { - "cum": 2549.25, - "kg": 1.0, - "mt": 1000.0, - "rmt": 1.0, - "sqm": 1.0, - "ltr": 1.0 - } - - # Get carbon emission data from the database - c_data = self.get_carbon_emission_data() - - maintenance_concrete_emission_factor = 0 - maintenance_concrete_kg = 0 - for item in c_data: - type = item.get(KEY_TYPE) - if type[-8:].lower() == "concrete": - qty = item.get(KEY_QUANTITY) - unit = item.get(KEY_UNIT_M3) - # convert quantity to kg - qty = qty * unit_conversions.get(unit.lower()) - maintenance_concrete_emission_factor = item.get(KEY_CARBON_EMISSION_FACTOR) - maintenance_concrete_kg += qty - - maintenance_carbon_cost = self.carbon_cost - maintenance_period = float(data[3]) - periodic_maintenance_concrete_carbon_component = PeriodicMaintenanceCarbonCost( - material_quantity=maintenance_concrete_kg, - carbon_emission_factor=maintenance_concrete_emission_factor, - carbon_cost=maintenance_carbon_cost, - discount_rate=self.discount_rate, - period=maintenance_period, - design_life=self.design_life - ) - print("\nPeriodic Maintenance Carbon Emission Cost (Concrete only):", periodic_maintenance_concrete_carbon_component.calculate_cost()) # INR - return periodic_maintenance_concrete_carbon_component.calculate_cost() - - # 8. Annual Routine Inspection Cost Calculation - def routine_inspection_cost(self, data: List, total_initial_construction_cost: float) -> float: - - inspection_rate = float(data[1]) - inspection_period = float(data[4]) - - inspection_component = RoutineInspectionCost( - inspection_cost_rate=inspection_rate, # Use inspection_rate as inspection cost rate - construction_cost=total_initial_construction_cost, # Always use total_initial_construction_cost - discount_rate=self.discount_rate, - design_life=self.design_life, - period=inspection_period # always annual - ) - total_routine_inspection_cost = inspection_component.calculate_cost() - print("\nTotal Routine Inspection Cost:", total_routine_inspection_cost) # INR - return total_routine_inspection_cost # 9. Repair and Rehabilitation Cost Calculation def repair_and_rehabilitation_cost(self, total_initial_construction_cost: float, data: List) -> float: @@ -683,61 +938,6 @@ def repair_and_rehabilitation_cost(self, total_initial_construction_cost: float, print("\nRepair and Rehabilitation Cost:", repair_component.calculate_cost()) # INR return repair_component.calculate_cost() - # 10. Demolition and Disposal Cost Calculation - def demolition_and_disposal_cost(self, data: List, total_initial_construction_cost: float) -> float: - demolition_rate = float(data[0]) - - demolition_component = DemolitionCost( - demolition_rate=demolition_rate, - construction_cost=total_initial_construction_cost, - discount_rate=self.discount_rate, - design_life=self.design_life - ) - print("\nDemolition and Disposal Cost:", demolition_component.calculate_cost()) # INR - return demolition_component.calculate_cost() - - # 11. Recycling Cost Calculation - def recycling_cost(self, data: List) -> float: - - # To convert to MT - unit_conversions = { - "kg": 1000.0, - "mt": 1.0 - } - - steel_rebar_quantity = 0 - struct_steel_quantity = 0 - # Get carbon emission data from the database - data = self.get_carbon_emission_data() - for item in data: - material = item.get(KEY_TYPE) - if material == "Steel Rebar": - steel_rebar_quantity += float(item.get(KEY_QUANTITY)) * unit_conversions.get(item.get(KEY_UNIT_M3)) - elif material == "Structural Steel": - struct_steel_quantity += float(item.get(KEY_QUANTITY)) * unit_conversions.get(item.get(KEY_UNIT_M3)) - - # Structural Steel-----------recycling-cost----------------------------------------- - ss_recycling_component = RecyclingCost( - scrap_value=float(data[1]), - quantity=struct_steel_quantity, - scrap_rate=float(data[2]), - discount_rate=self.discount_rate, - design_life=self.design_life - ) - print("\nRecycling Cost (Structural Steel):", ss_recycling_component.calculate_cost()) - - # Steel-rebar-----------------recycling-cost----------------------------------------- - sr_recycling_component = RecyclingCost( - scrap_value=float(data[3]), - quantity=steel_rebar_quantity, - scrap_rate=float(data[4]), - discount_rate=self.discount_rate, - design_life=self.design_life - ) - print("\nRecycling Cost (Steel Rebar):", sr_recycling_component.calculate_cost()) - - return sr_recycling_component.calculate_cost() + ss_recycling_component.calculate_cost() - # 12. Reconstruction Cost Calculation def reconstruction_cost(self, initial_construction_cost: float, demolition_cost: float, @@ -771,5 +971,7 @@ def reconstruction_cost(self, initial_construction_cost: float, reconstruction_result = 0 print("\nReconstruction Cost:", reconstruction_result) # INR return reconstruction_result + + \ No newline at end of file From 92601c014f4ab073326493f37b0eb5e568b99f7d Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Sun, 30 Nov 2025 15:17:04 +0530 Subject: [PATCH 19/22] -Accident related cost -Value of Time (VOT) --- .../desktop_app/bash.exe.stackdump | 28 -- .../widgets/bridge_and_traffic_data.py | 55 +++- .../desktop_app/widgets/utils/IRC_SP_30.py | 247 ++++++++++++++++++ .../desktop_app/widgets/utils/a.py | 4 - .../desktop_app/widgets/utils/data.py | 48 +++- .../desktop_app/widgets/utils/database.py | 230 ++++++++++++---- 6 files changed, 504 insertions(+), 108 deletions(-) delete mode 100644 src/osbridgelcca/desktop_app/bash.exe.stackdump create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py delete mode 100644 src/osbridgelcca/desktop_app/widgets/utils/a.py diff --git a/src/osbridgelcca/desktop_app/bash.exe.stackdump b/src/osbridgelcca/desktop_app/bash.exe.stackdump deleted file mode 100644 index 66ca9bf..0000000 --- a/src/osbridgelcca/desktop_app/bash.exe.stackdump +++ /dev/null @@ -1,28 +0,0 @@ -Stack trace: -Frame Function Args -0007FFFF9F00 00021006118E (00021028DEE8, 000210272B3E, 0007FFFF9F00, 0007FFFF8E00) msys-2.0.dll+0x2118E -0007FFFF9F00 0002100469BA (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x69BA -0007FFFF9F00 0002100469F2 (00021028DF99, 0007FFFF9DB8, 0007FFFF9F00, 000000000000) msys-2.0.dll+0x69F2 -0007FFFF9F00 00021006A41E (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x2A41E -0007FFFF9F00 00021006A545 (0007FFFF9F10, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x2A545 -0001004F94B7 00021006B9A5 (0007FFFF9F10, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x2B9A5 -End of stack trace -Loaded modules: -000100400000 bash.exe -7FFC33540000 ntdll.dll -7FFC328E0000 KERNEL32.DLL -7FFC30A90000 KERNELBASE.dll -7FFC319A0000 USER32.dll -7FFC309A0000 win32u.dll -000210040000 msys-2.0.dll -7FFC33420000 GDI32.dll -7FFC31270000 gdi32full.dll -7FFC30E50000 msvcp_win.dll -7FFC30F00000 ucrtbase.dll -7FFC318E0000 advapi32.dll -7FFC32AC0000 msvcrt.dll -7FFC31570000 sechost.dll -7FFC32B70000 RPCRT4.dll -7FFC2FE20000 CRYPTBASE.DLL -7FFC31050000 bcryptPrimitives.dll -7FFC333D0000 IMM32.DLL diff --git a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py index 7ff9da5..43d9e44 100644 --- a/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py +++ b/src/osbridgelcca/desktop_app/widgets/bridge_and_traffic_data.py @@ -17,6 +17,8 @@ def __init__(self, database, parent=None): self.database_manager = database self.data = bridge_traffic_data.get(KEY_BRIDGE_TRAFFIC) self.widgets = [] + self.vehicle_distribution = [] + self.accident_distribution = [] self.text_box_width = 200 self.setStyleSheet(""" @@ -383,9 +385,9 @@ def __init__(self, database, parent=None): vehicle_layout.addWidget(per_dist_label, 0, 1) vehicles = [ - "Minor Injury", - "Major Injury", - "Fatal" + KEY_MINOR_INJURY, + KEY_MAJOR_INJURY, + KEY_FATAL ] for i, vehicle in enumerate(vehicles): @@ -394,6 +396,7 @@ def __init__(self, database, parent=None): v_label.setAlignment(Qt.AlignCenter) v_label.setStyleSheet("background-color: #FFFFFF; border: 1px solid #FFFFFF; border-radius: 10px; padding: 10px 10px 10px 1px;") v0_input = QLineEdit() + self.vehicle_distribution.append(v0_input) v0_input.setFixedWidth(self.text_box_width*2) v0_input.setStyleSheet(""" QLineEdit { @@ -463,14 +466,14 @@ def __init__(self, database, parent=None): vehicle_layout.addWidget(per_dist_label, 0, 2) vehicles = [ - "Two Wheeler", - "Small Car", - "Big Car", - "Ordinary Bus", - "Deluxe Bus", - "LCV", - "MCV", - "HCV" + KEY_TWO_WHEELER, + KEY_SMALL_CARS, + KEY_BIG_CARS, + KEY_ORDINARY_BUS, + KEY_DELUXE_BUS, + KEY_LCV, + KEY_MCV, + KEY_HCV ] self.average_daily_traffic = [] @@ -492,6 +495,7 @@ def __init__(self, database, parent=None): } """) v1_input = QLineEdit() + self.vehicle_distribution.append(v1_input) v1_input.setFixedWidth(self.text_box_width) v1_input.setStyleSheet(""" QLineEdit { @@ -568,21 +572,44 @@ def collect_data(self): } # Collect other data data = { - KEY_ALTER_ROAD_CARRIAGEWAY: self.widgets[0].currentText(), + KEY_ALTER_ROAD_CARRIAGEWAY: self.widgets[0].currentText(), # String KEY_ADDIT_REROUTING_DISTANCE: 0.0 if not self.widgets[1].text() else float(self.widgets[1].text()), - KEY_ADDIT_TRAVEL_TIME: 0.0 if not self.widgets[2].text() else float(self.widgets[2].text()), + KEY_ADDIT_TRAVEL_TIME: 0.0 if not self.widgets[2].text() else float(self.widgets[2].text())/60, # hours KEY_ROAD_ROUGHNESS: self.widgets[3].currentText(), KEY_ROAD_RISE: self.widgets[4].currentText(), KEY_ROAD_FALL: self.widgets[5].currentText(), KEY_ROAD_TYPE: self.widgets[6].currentText(), KEY_CRASH_RATE: 0.0 if not self.widgets[7].text() else float(self.widgets[7].text()) } - print("\nCollected Traffic Composition Data from UI:") + # Vehicle Distribution + vehicle_dist = { + KEY_TWO_WHEELER: 0.0 if not self.vehicle_distribution[0].text() else float(self.vehicle_distribution[0].text()), + KEY_SMALL_CARS: 0.0 if not self.vehicle_distribution[1].text() else float(self.vehicle_distribution[1].text()), + KEY_BIG_CARS: 0.0 if not self.vehicle_distribution[2].text() else float(self.vehicle_distribution[2].text()), + KEY_ORDINARY_BUS: 0.0 if not self.vehicle_distribution[3].text() else float(self.vehicle_distribution[3].text()), + KEY_DELUXE_BUS: 0.0 if not self.vehicle_distribution[4].text() else float(self.vehicle_distribution[4].text()), + KEY_LCV: 0.0 if not self.vehicle_distribution[5].text() else float(self.vehicle_distribution[5].text()), + KEY_MCV: 0.0 if not self.vehicle_distribution[6].text() else float(self.vehicle_distribution[6].text()), + KEY_HCV: 0.0 if not self.vehicle_distribution[7].text() else float(self.vehicle_distribution[7].text()) + } + # Accident distribution + accident_dist = { + KEY_MINOR_INJURY: 0.0 if not self.accident_distribution[0].text() else float(self.accident_distribution[0].text()), + KEY_MAJOR_INJURY: 0.0 if not self.accident_distribution[1].text() else float(self.accident_distribution[1].text()), + KEY_FATAL: 0.0 if not self.accident_distribution[2].text() else float(self.accident_distribution[2].text()) + } + print("\nCollected Traffic Composition Data from UI:\nDaily Avg Traffic...\n") pprint(traffic_data) + print("\nUI Data...\n") + pprint(data) + print("\nAccident Distribution...\n") + pprint(accident_dist) # Save UI Data to Backend self.database_manager.traffic_data = data self.database_manager.daily_average_traffic_data = traffic_data + self.database_manager.accident_distribution = accident_dist + self.database_manager.vehicle_distribution = vehicle_dist # Carbon Emission due to Rerouting during Initial Construction self.database_manager.init_carbon_emission_rerouting() diff --git a/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py b/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py new file mode 100644 index 0000000..29338f8 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py @@ -0,0 +1,247 @@ +import pandas as pd +from .data import * + +class IRC_SP_30: + def __init__(self): + """Initialize the dataframes with IRC SP-30 data.""" + self._initialize_dataframes() + + def _initialize_dataframes(self): + """Create and populate all DataFrames with IRC SP-30 data.""" + + # Table 8: Accident Type Costs (Pg. 17) + self.accident_type_costs = pd.DataFrame({ + COL_ACCIDENT_CATEGORY: [KEY_FATAL, KEY_MAJOR_INJURY, KEY_MINOR_INJURY], + COL_COST_INR: [1325049.00, 432651.00, 46680.00] + }).set_index(COL_ACCIDENT_CATEGORY) + + # Table 9: Vehicle Damage Costs (Pg. 18) + self.vehicle_damage_costs = pd.DataFrame({ + COL_TYPE_OF_VEHICLE: [KEY_TWO_WHEELER, KEY_SMALL_CARS, KEY_BIG_CARS, + KEY_ORDINARY_BUS, KEY_DELUXE_BUS, + KEY_LCV, KEY_MCV, KEY_HCV], + COL_COST_INR: [ 10194.00, 40088.00, 40088.00, + 116585.00, 116585.00, + 205483.00, 205483.00, 120494.00] + }).set_index(COL_TYPE_OF_VEHICLE) + + # Table 6: VOT of Passengers (Pg. 16) + self.vot_of_passengers = pd.DataFrame({ + COL_TYPE_OF_VEHICLE: [KEY_TWO_WHEELER, KEY_SMALL_CARS, KEY_BIG_CARS, + KEY_ORDINARY_BUS, KEY_DELUXE_BUS, + KEY_LCV, KEY_MCV, KEY_HCV], + KEY_SINGLE_LANE_ROAD: [41.3, 98.5, 98.5, 27.2, 0, 0, 0, 0], + KEY_INTERMEDIATE_LANE_ROAD: [41.3, 98.5, 98.5, 27.2, 0, 0, 0, 0], + KEY_TWO_LANE_ROAD: [60.1, 117.3, 117.3, 73.2, 81.6, 0, 0, 0], + KEY_FOUR_LANE_DIVIDED_ROAD: [60.5, 178.5, 258.0, 73.2, 109.0, 0, 0, 0], + KEY_SIX_LANE_DIVIDED_ROAD: [60.5, 178.5, 258.0, 73.2, 109.0, 0, 0, 0], + KEY_FOUR_LANE_DIVIDED_EXPRESSWAY: [60.5, 178.5, 258.0, 73.2, 109.0, 0, 0, 0], + KEY_SIX_LANE_DIVIDED_EXPRESSWAY: [60.5, 178.5, 258.0, 73.2, 109.0, 0, 0, 0], + KEY_EIGHT_LANE_DIVIDED_URBAN_EXPRESSWAY: [60.5, 178.5, 258.0, 73.2, 109.0, 0, 0, 0], + COL_OCCUPANCY: [1.71, 3.23, 4.28, 30.0, 40.0, 2.5, 2.0, 1.5] + }).set_index(COL_TYPE_OF_VEHICLE) + + # WPI: Medical Accessories for Human Injury Cost + self.wpi_medical_accessories = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_FATAL: [132.5, 135.8, 139.5, 140.5, 142.5, 144.0], + KEY_MAJOR_INJURY: [132.5, 135.8, 139.5, 140.5, 142.5, 144.0], + KEY_MINOR_INJURY: [132.5, 135.8, 139.5, 140.5, 142.5, 144.0] + }).set_index(COL_YEAR) + + # WPI: Travel Time (Table 6, Pg. 16) + self.wpi_vot = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_SMALL_CARS: [121.2, 121.8, 135.0, 151.3, 151.3, 154.0], + KEY_BIG_CARS: [121.2, 121.8, 135.0, 151.3, 151.3, 154.0], + KEY_TWO_WHEELER: [121.2, 121.8, 135.0, 151.3, 151.3, 154.0], + KEY_ORDINARY_BUS: [121.2, 121.8, 135.0, 151.3, 151.3, 154.0], + KEY_DELUXE_BUS: [121.2, 121.8, 135.0, 151.3, 151.3, 154.0], + KEY_LCV: [121.2, 121.8, 135.0, 151.3, 151.3, 154.0], + KEY_HCV: [121.2, 121.8, 135.0, 151.3, 151.3, 154.0], + KEY_MCV: [121.2, 121.8, 135.0, 151.3, 151.3, 154.0] + }).set_index(COL_YEAR) + + # WPI: Property Damage (Manufacture of parts and accessories for motor vehicles) + self.wpi_property_damage = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_SMALL_CARS: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_BIG_CARS: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_TWO_WHEELER: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_ORDINARY_BUS: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_DELUXE_BUS: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_LCV: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_HCV: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_MCV: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0] + }).set_index(COL_YEAR) + + # WPI: VOC: Fuel Costs (Engine Oil, Other Oil, Grease) + self.voc_fuel_costs = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_PETROL: [85.4, 74.2, 109.9, 159.8, 158.9, 154.3], + KEY_DIESEL: [94.4, 79.4, 114.7, 183.5, 174.2, 167.4], + KEY_ENGINE_OIL: [131.2, 134.0, 157.8, 174.7, 188.0, 190.2], + KEY_OTHER_OIL: [92.5, 78.1, 113.8, 168.2, 160.1, 156.8], + KEY_GREASE: [92.5, 78.1, 113.8, 168.2, 160.1, 156.8] + }).set_index(COL_YEAR) + + # WPI: Tyre Cost (for each vehicle) + self.tyre_costs = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_SMALL_CARS: [99.2, 98.7, 102.8, 109.9, 111.5, 111.5], + KEY_BIG_CARS: [99.2, 98.7, 102.8, 109.9, 111.5, 111.5], + KEY_TWO_WHEELER: [104.0, 102.0, 105.9, 116.4, 119.8, 117.9], + KEY_ORDINARY_BUS: [97.5, 96.1, 103.2, 110.5, 114.4, 114.1], + KEY_DELUXE_BUS: [97.5, 96.1, 103.2, 110.5, 114.4, 114.1], + KEY_LCV: [97.5, 96.1, 103.2, 110.5, 114.4, 114.1], + KEY_HCV: [97.5, 96.1, 103.2, 110.5, 114.4, 114.1], + KEY_MCV: [97.5, 96.1, 103.2, 110.5, 114.4, 114.1] + }).set_index(COL_YEAR) + + # WPI: Spare Parts: New Price (Manufacture of parts and accessories for motor vehicles) + self.spare_parts_costs = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_SMALL_CARS: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_BIG_CARS: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_TWO_WHEELER: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_ORDINARY_BUS: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_DELUXE_BUS: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_LCV: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_HCV: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0], + KEY_MCV: [113.2, 115.5, 120.6, 128.5, 128.2, 129.0] + }).set_index(COL_YEAR) + + # WPI: Fixed and Depreciation Costs: Manufacture of motor vehicles, trailers and semi-trailers + self.fixed_depreciation_costs = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_SMALL_CARS: [113.8, 116.9, 121.1, 127.1, 128.0, 129.6], + KEY_BIG_CARS: [113.8, 116.9, 121.1, 127.1, 128.0, 129.6], + KEY_TWO_WHEELER: [113.8, 116.9, 121.1, 127.1, 128.0, 129.6], + KEY_ORDINARY_BUS: [113.8, 116.9, 121.1, 127.1, 128.0, 129.6], + KEY_DELUXE_BUS: [113.8, 116.9, 121.1, 127.1, 128.0, 129.6], + KEY_LCV: [113.8, 116.9, 121.1, 127.1, 128.0, 129.6], + KEY_HCV: [113.8, 116.9, 121.1, 127.1, 128.0, 129.6], + KEY_MCV: [113.8, 116.9, 121.1, 127.1, 128.0, 129.6] + }).set_index(COL_YEAR) + + # WPI: Commodity Holding Cost: Fuel & Power + self.commodity_holding_cost = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_SMALL_CARS: [101.7, 93.3, 116.1, 155.2, 152.7, 150.4], + KEY_BIG_CARS: [101.7, 93.3, 116.1, 155.2, 152.7, 150.4], + KEY_TWO_WHEELER: [101.7, 93.3, 116.1, 155.2, 152.7, 150.4], + KEY_ORDINARY_BUS: [101.7, 93.3, 116.1, 155.2, 152.7, 150.4], + KEY_DELUXE_BUS: [101.7, 93.3, 116.1, 155.2, 152.7, 150.4], + KEY_LCV: [101.7, 93.3, 116.1, 155.2, 152.7, 150.4], + KEY_HCV: [101.7, 93.3, 116.1, 155.2, 152.7, 150.4], + KEY_MCV: [101.7, 93.3, 116.1, 155.2, 152.7, 150.4] + }).set_index(COL_YEAR) + + # WPI: Passenger and Crew Costs + self.passenger_crew_costs = pd.DataFrame({ + COL_YEAR: [2019, 2020, 2021, 2022, 2023, 2024], + KEY_PASSENGER_COST: [138.58, 147.91, 155.33, 166.94, 176.38, 184.27], + KEY_CREW_COST: [118.07, 131.77, 145.91, 159.05, 160.28, 164.18] + }).set_index(COL_YEAR) + + # ==================== Get Methods ==================== + + def _get_accident_cost(self, category: str) -> float: + """ + Get the economic cost for a specific accident category. + + Args: + category: 'Fatal', 'Major Injury', or 'Minor Injury' + + Returns: + Economic cost in INR + """ + try: + return float(self.accident_type_costs.loc[category, COL_COST_INR]) + except KeyError: + raise ValueError(f"Invalid accident category: '{category}'. " + f"Valid options: {list(self.accident_type_costs.index)}") + + def _get_vehicle_damage_cost(self, vehicle_type: str) -> float: + """ + Get the economic cost of vehicle damage. + + Args: + vehicle_type: e.g., 'Two Wheeler', 'Small Cars', 'LCV' + + Returns: + Economic cost in INR + """ + try: + return float(self.vehicle_damage_costs.loc[vehicle_type, COL_COST_INR]) + except KeyError: + raise ValueError(f"Invalid vehicle type: '{vehicle_type}'. " + f"Valid options: {list(self.vehicle_damage_costs.index)}") + + def _get_wpi(self, table: str, column: str, current_year: int, base_year: int) -> float: + """ + Calculate WPI ratio between current and base year. + + Args: + table: 'medical' or 'vot' + column: Column name (lowercase with underscores) + current_year: Target year + base_year: Reference year (default: 2019) + + Returns: + WPI ratio (current/base) + """ + # Select the appropriate DataFrame + if table == TABLE_WPI_MEDICAL: + df = self.wpi_medical_accessories + elif table == TABLE_VOT: + df = self.wpi_vot + else: + raise ValueError(f"Invalid table: '{table}'. Use {TABLE_WPI_MEDICAL} or {TABLE_VOT}") + + try: + cur_value = df.loc[current_year, column] + base_value = df.loc[base_year, column] + return float(cur_value / base_value) + except KeyError as e: + raise ValueError(f"Year or column not found: {e}") + + def _get_vot(self, vehicle_type: str, column: str) -> float: + """ + Get Value of Travel Time for specific vehicle and road type. + + Args: + vehicle_type: e.g., 'Two Wheeler', 'Small Car', 'Big Car' + road_type: Road type (lowercase with underscores) + + Returns: + VOT value in INR per hour + """ + try: + return float(self.vot_of_passengers.loc[vehicle_type, column]) + except KeyError: + raise ValueError(f"Invalid vehicle type '{vehicle_type}' or " + f"column '{column}'") + + def _get_occupancy(self, vehicle_type: str) -> float: + """ + Get average occupancy for a vehicle type. + + Args: + vehicle_type: e.g., 'Two Wheeler', 'Small Car' + + Returns: + Average occupancy (persons per vehicle) + """ + try: + return float(self.vot_of_passengers.loc[vehicle_type, COL_OCCUPANCY]) + except KeyError: + raise ValueError(f"Invalid vehicle type: '{vehicle_type}'") + +if __name__ == "__main__": + irc_sp_30 = IRC_SP_30() + print(irc_sp_30.accident_type_costs) + print(irc_sp_30.vehicle_damage_costs) + print(irc_sp_30.wpi_medical_accessories) + print(irc_sp_30.wpi_vot) + print(irc_sp_30.vot_of_passengers) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/a.py b/src/osbridgelcca/desktop_app/widgets/utils/a.py deleted file mode 100644 index c7f5c7f..0000000 --- a/src/osbridgelcca/desktop_app/widgets/utils/a.py +++ /dev/null @@ -1,4 +0,0 @@ -i=0.00515 -r=0.0067 -a=sum(((1 + i) / (1 + r)) ** period for period in range(1, 50, 1)) -print(a) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/utils/data.py b/src/osbridgelcca/desktop_app/widgets/utils/data.py index daa5506..ab0a139 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/data.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/data.py @@ -1,3 +1,26 @@ +# WPI Calculation +BASE_YEAR = 2019 +KEY_PASSENGER_COST = "Passenger Cost" +KEY_CREW_COST = "Crew Cost" + +# Keys for database +TABLE_WPI_MEDICAL = "wpi_medical_accessories" +TABLE_VOT = "wpi_vot" +COL_ACCIDENT_CATEGORY = "Category_of_Accident" +COL_COST_INR = "Economic_Cost_INR" +COL_TYPE_OF_VEHICLE = "Type_of_Vehicle" +COL_YEAR = "Year" + +KEY_SINGLE_LANE_ROAD = "Single Lane Roads" +KEY_INTERMEDIATE_LANE_ROAD = "Intermediate Lane Roads" +KEY_TWO_LANE_ROAD = "Two Lane Roads" +KEY_FOUR_LANE_DIVIDED_ROAD = "Four Lane Divided Roads" +KEY_SIX_LANE_DIVIDED_ROAD = "Six Lane Divided Roads" +KEY_FOUR_LANE_DIVIDED_EXPRESSWAY = "Four Lane Divided Expressways" +KEY_SIX_LANE_DIVIDED_EXPRESSWAY = "Six Lane Divided Expressways" +KEY_EIGHT_LANE_DIVIDED_URBAN_EXPRESSWAY = "Eight Lane Divided Urban Expressways" +COL_OCCUPANCY = "Occupancy" + # Keys for All Type of Tabs KEY_STRUCTURE_WORKS_DATA = "Structure Works Data" KEY_FOUNDATION = "Foundation" @@ -40,6 +63,15 @@ KEY_LCV = "LCV" KEY_HCV = "HCV" KEY_MCV = "MCV" +KEY_MINOR_INJURY = "Minor Injury" +KEY_MAJOR_INJURY = "Major Injury" +KEY_FATAL = "Fatal" + +KEY_PETROL = "Petrol" +KEY_DIESEL = "Diesel" +KEY_ENGINE_OIL = "Engine Oil" +KEY_OTHER_OIL = "Other Oil" +KEY_GREASE = "Grease" KEY_LANES = "lanes" KEY_ROADROUGHNESS = "road_roughness" @@ -394,14 +426,14 @@ KEY_BRIDGE_TRAFFIC: { KEY_LANES: { KEY_OPTIONS: [ - "Single Lane Roads", - "Intermediate Lane Roads", - "Two Lane Roads", - "Four Lane Divided Roads", - "Six Lane Divided Roads", - "Four Lane Divided Expressways", - "Six Lane Divided Expressways", - "Eight Lane Divided Urban Expressways" + KEY_SINGLE_LANE_ROAD, + KEY_INTERMEDIATE_LANE_ROAD, + KEY_TWO_LANE_ROAD, + KEY_FOUR_LANE_DIVIDED_ROAD, + KEY_SIX_LANE_DIVIDED_ROAD, + KEY_FOUR_LANE_DIVIDED_EXPRESSWAY, + KEY_SIX_LANE_DIVIDED_EXPRESSWAY, + KEY_EIGHT_LANE_DIVIDED_URBAN_EXPRESSWAY ], }, diff --git a/src/osbridgelcca/desktop_app/widgets/utils/database.py b/src/osbridgelcca/desktop_app/widgets/utils/database.py index d6bbb83..69e01e1 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/database.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/database.py @@ -1,6 +1,5 @@ import sqlite3 from typing import List, Dict, Tuple -from .data import * from .cost_component import ( BearingAndExpansionJointReplacementCost, CarbonEmissionDueToRerouting, DemolitionCarbonCost, DemolitionCarbonReroutingCost, InitialConstructionCost, MajorInspectionCost, MajorRepairCost, MajorRepairRelCarbonEmissionCost, TimeCost, RoadUserCost, @@ -9,6 +8,9 @@ DemolitionCost, RecyclingCost, ReconstructionCost, PeriodicMaintenanceCarbonCost ) +from .data import * +from .IRC_SP_30 import IRC_SP_30 + class DatabaseManager: """Database manager for Structure Works Data""" @@ -21,17 +23,72 @@ def __init__(self, db_path: str = "widgets/utils/structure_works.db", recreate: db_path: Path to the database file recreate: If True, delete existing database and create fresh. If False, use existing database. """ + + # Instantiate IRC_SP_30 + self.irc_sp_30 = IRC_SP_30() + self.db_path = db_path self.conn = None self.create_database(recreate=recreate) # Data from UI - self.financial_data = {} + self.financial_data = { + KEY_DISCOUNT_RATE_IA: 0.067, + KEY_INFLATION_RATE: 0.0515, + KEY_INTEREST_RATE: 0.0775, + KEY_INVESTMENT_RATIO: 0.5, + KEY_DESIGN_LIFE: 50, + KEY_CONSTR_TIME: 5, + KEY_ANALYSIS_PERIOD: 50, + } self.carbon_emission_cost_data = {} - self.daily_average_traffic_data = {} + self.daily_average_traffic_data = { + KEY_TWO_WHEELER: 100, + KEY_SMALL_CARS: 100, + KEY_ORDINARY_BUS: 100, + KEY_DELUXE_BUS: 100, + KEY_LCV: 100, + KEY_HCV: 100, + KEY_MCV: 100 + } self.maintainance_and_repair_data = {} self.demolition_and_recycling_data = {} - self.traffic_data = {} + self.traffic_data = { + KEY_ALTER_ROAD_CARRIAGEWAY: "Two Lane Roads", # String + KEY_ADDIT_REROUTING_DISTANCE: 6.0, + KEY_ADDIT_TRAVEL_TIME: 1.5, # hours + KEY_ROAD_ROUGHNESS: 3000, # String + KEY_ROAD_RISE: 2000, # String + KEY_ROAD_FALL: 1000, # String + KEY_ROAD_TYPE: "Urban Road", # String + KEY_CRASH_RATE: 30.0 + } + self.accident_distribution = { + KEY_MINOR_INJURY: 60.0, + KEY_MAJOR_INJURY: 20.0, + KEY_FATAL: 20.0 + } + self.vehicle_distribution = { + KEY_TWO_WHEELER: 5.0, + KEY_SMALL_CARS: 10.0, + KEY_BIG_CARS: 50.0, + KEY_ORDINARY_BUS: 10.0, + KEY_DELUXE_BUS: 10.0, + KEY_LCV: 5.0, + KEY_MCV: 5.0, + KEY_HCV: 5.0 + } + + # Constants + self.PETROL_DIESEL_RATIO_SM_CARS = { + KEY_PETROL: 0.7, + KEY_DIESEL: 0.3 + } + self.PETROL_DIESEL_RATIO_BG_CARS = { + KEY_PETROL: 0.3, + KEY_DIESEL: 0.7 + } + self.WORK_ZONE_MULTIPLIER = 1.0 # Total Costs self.results = { @@ -871,57 +928,118 @@ def recycling_cost(self) -> float: #==========End-Of-Life-Stage-Cost-End==================== - # 4. Called on next from bridge_and_traffic_data.py - def calculate_irc_road_cost(self, data: List) -> float: - # fetch Construction time - cursor = self.conn.cursor() - cursor.execute(''' - SELECT time_of_project - FROM financial_data - ''') - construction_time = float(cursor.fetchone()[0]) - - total_road_user_cost = 0 - lanes = data[0] - roughness = data[2] - rf = data[3] - - key = ["Car", "Bus", "HCV", "MCV", "LCV"] - for vehicle in key: - count = int(data[6+key.index(vehicle)]) - key_vehicle = (vehicle, lanes, roughness, rf) - cost_per_km = self.IRC_ROAD_COSTS_DATA.get(key_vehicle, "") - if cost_per_km: - ct = construction_time * float(data[1]) - road_user_cost_component = RoadUserCost( - vehicles_affected=count, - vehicle_operation_cost=cost_per_km, - construction_time=ct - ) - total_road_user_cost += road_user_cost_component.calculate_cost() - else: - print(f"No Grand_Cost found for {vehicle}, {lanes}, {roughness}, {rf}") - - print("\nTotal Road User Cost:", total_road_user_cost) # INR - return total_road_user_cost + #==========IRC-Road_User-Cost-Start====================== - # 5. Called on next from bridge_and_traffic_data.py - def additional_carbon_emission_cost(self, data: List) -> float: - total_vehicles_affected = 0 - for i in range(6,11): - total_vehicles_affected += float(data[i]) - - reroute_distance = float(data[1]) - - additional_carbon_emission_component = AdditionalCarbonEmissionCost( - vehicles_affected=total_vehicles_affected, - reroute_distance=reroute_distance, - co2_emission_per_km=0.1213, - carbon_cost=self.carbon_cost - ) - print("\nAdditional Carbon Emission Cost:", additional_carbon_emission_component.calculate_cost()) # INR - return additional_carbon_emission_component.calculate_cost() + #==========2. Accident-Related-Cost-Start========== + + #==========2.1 Human-Injury-Cost-Start========== + + def _no_of_accidents(self) -> float: # Per Day + crash_rate = self.traffic_data.get(KEY_CRASH_RATE) + adt = self._get_total_traffic() + multiplier = self.WORK_ZONE_MULTIPLIER + rerouting_dist = self.traffic_data.get(KEY_ADDIT_REROUTING_DISTANCE) + return crash_rate * adt * multiplier * rerouting_dist * (10**-6) + + def _accident_in_constr_time(self) -> float: + no_of_accidents = self._no_of_accidents() + month = self.financial_data.get(KEY_CONSTR_TIME) * 12 + days = self.WORKING_DAYS_IN_MONTH * month + return no_of_accidents * days + + def _wpi_adj_economic_cost_of_accident_category(self, accident_category:str) -> float: + wpi = self.irc_sp_30._get_wpi(table=TABLE_WPI_MEDICAL, + column=accident_category, + current_year=2024, # Hard Coded + base_year=BASE_YEAR) + economic_cost_of_accident = self.irc_sp_30._get_accident_cost(accident_category) + return economic_cost_of_accident * wpi + + def _count_accident_type(self, accident_category:str) -> float: + accident_dist = self.accident_distribution.get(accident_category) + return self._accident_in_constr_time() * accident_dist + + def _injury_cost(self, accident_category:str) -> float: + cost = self._wpi_adj_economic_cost_of_accident_category(accident_category) + return cost * self._count_accident_type(accident_category) + + def total_human_injury_cost(self) -> float: + cost = 0.0 + for injury in [KEY_MINOR_INJURY, KEY_MAJOR_INJURY, KEY_FATAL]: + cost += self._injury_cost(injury) + return cost + #==========2.1 Human-Injury-Cost-End========== + + #==========2.2 Vehicle-Damage-Cost-Start========== + def _wpi_adj_economic_cost_of_quantum_damage(self, vehicle_category:str) -> float: + wpi = self.irc_sp_30._get_wpi(table=TABLE_VOT, + column=vehicle_category, + current_year=2024, # Hard Coded + base_year=BASE_YEAR) + economic_cost_of_quantum_damage = self.irc_sp_30._get_vehicle_damage_cost(vehicle_category) + return economic_cost_of_quantum_damage * wpi + + def _count_vehicle_damage(self, vehicle_category:str) -> float: + vehicle_dist = self.vehicle_distribution.get(vehicle_category) + return self._accident_in_constr_time() * vehicle_dist + + def _vehicle_damage_cost(self, vehicle_category:str): + cost = self._wpi_adj_economic_cost_of_quantum_damage(vehicle_category) + return cost * self._count_vehicle_damage(vehicle_category) + + def total_vehicle_damage_cost(self) -> float: + cost = 0.0 + for vehicle in [KEY_TWO_WHEELER, + KEY_SMALL_CARS, + KEY_BIG_CARS, + KEY_ORDINARY_BUS, + KEY_DELUXE_BUS, + KEY_LCV, + KEY_HCV, + KEY_MCV]: + cost += self._vehicle_damage_cost(vehicle) + return cost + #==========2.2 Vehicle-Damage-Cost-End========== + + def accident_related_cost(self) -> float: + return self.total_human_injury_cost() + self.total_vehicle_damage_cost() + #==========2. Accident-Related-Cost-End========== + + #==========3. VOT-Start========================== + def _wpi_adj_vot(self, vehicle_type:str, type_of_road:str) -> float: + wpi = self.irc_sp_30._get_wpi(table=TABLE_VOT, + column=vehicle_type, + current_year=2024, # Hard Coded + base_year=BASE_YEAR) + time_value = self.irc_sp_30._get_vot(column=type_of_road, + vehicle_type=vehicle_type) + return time_value * wpi + + def _vot_per_day(self, vehicle_type: str, type_of_road: str) -> float: + time_value = self._wpi_adj_vot(vehicle_type=vehicle_type, + type_of_road=type_of_road) + vehicle_per_day = self.daily_average_traffic_data.get(vehicle_type) + addit_travel_time = self.traffic_data.get(KEY_ADDIT_TRAVEL_TIME) + occupancy = self.irc_sp_30._get_occupancy(vehicle_type=vehicle_type) + return time_value * vehicle_per_day * addit_travel_time * occupancy + + def vot_per_year(self) -> float: + month = self.financial_data.get(KEY_CONSTR_TIME) * 12 + days = self.WORKING_DAYS_IN_MONTH * month + road_type = self.traffic_data.get(KEY_ALTER_ROAD_CARRIAGEWAY) + + total_vot = 0.0 + for vehicle_type in self.daily_average_traffic_data.keys(): + total_vot += self._vot_per_day(vehicle_type=vehicle_type, + type_of_road=road_type) + return total_vot * days + + #==========3. VOT-End============================ + + + + #==========IRC-Road_User-Cost-End==================== # 9. Repair and Rehabilitation Cost Calculation def repair_and_rehabilitation_cost(self, total_initial_construction_cost: float, data: List) -> float: @@ -972,6 +1090,10 @@ def reconstruction_cost(self, initial_construction_cost: float, print("\nReconstruction Cost:", reconstruction_result) # INR return reconstruction_result - +if __name__ == "__main__": + d = DatabaseManager() + print("vot=",d.vot_per_year()) + print("acccident_cost=",d.accident_related_cost()) + \ No newline at end of file From 4fe57ea6380d3796ffb6e775815ea57ab22d5b77 Mon Sep 17 00:00:00 2001 From: swas02 <60228256+swas02@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:38:23 +0530 Subject: [PATCH 20/22] updated utils/IRC_SP_30.py for Added a new function 'getWPI()'. --- build/lib/osbridgelcca/__init__.py | 0 src/osbridgelcca.egg-info/PKG-INFO | 21 +++++ src/osbridgelcca.egg-info/SOURCES.txt | 9 ++ .../dependency_links.txt | 1 + src/osbridgelcca.egg-info/requires.txt | 13 +++ src/osbridgelcca.egg-info/top_level.txt | 1 + .../desktop_app/widgets/utils/IRC_SP_30.py | 84 ++++++++++++++++++- 7 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 build/lib/osbridgelcca/__init__.py create mode 100644 src/osbridgelcca.egg-info/PKG-INFO create mode 100644 src/osbridgelcca.egg-info/SOURCES.txt create mode 100644 src/osbridgelcca.egg-info/dependency_links.txt create mode 100644 src/osbridgelcca.egg-info/requires.txt create mode 100644 src/osbridgelcca.egg-info/top_level.txt diff --git a/build/lib/osbridgelcca/__init__.py b/build/lib/osbridgelcca/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/osbridgelcca.egg-info/PKG-INFO b/src/osbridgelcca.egg-info/PKG-INFO new file mode 100644 index 0000000..d77a7c4 --- /dev/null +++ b/src/osbridgelcca.egg-info/PKG-INFO @@ -0,0 +1,21 @@ +Metadata-Version: 2.4 +Name: osbridgelcca +Version: 0.1.0 +Summary: Life Cycle Cost Analysis for Bridges +Author-email: Your Name +License: MIT +License-File: LICENSE +Requires-Dist: django +Requires-Dist: djangorestframework +Requires-Dist: pyqt5 +Requires-Dist: plotly +Requires-Dist: pylatex +Requires-Dist: pytest +Requires-Dist: matplotlib +Requires-Dist: pandas +Requires-Dist: numpy +Requires-Dist: psycopg2-binary +Requires-Dist: requests +Requires-Dist: flask +Requires-Dist: gunicorn +Dynamic: license-file diff --git a/src/osbridgelcca.egg-info/SOURCES.txt b/src/osbridgelcca.egg-info/SOURCES.txt new file mode 100644 index 0000000..961eb5b --- /dev/null +++ b/src/osbridgelcca.egg-info/SOURCES.txt @@ -0,0 +1,9 @@ +LICENSE +README.md +pyproject.toml +src/osbridgelcca/__init__.py +src/osbridgelcca.egg-info/PKG-INFO +src/osbridgelcca.egg-info/SOURCES.txt +src/osbridgelcca.egg-info/dependency_links.txt +src/osbridgelcca.egg-info/requires.txt +src/osbridgelcca.egg-info/top_level.txt \ No newline at end of file diff --git a/src/osbridgelcca.egg-info/dependency_links.txt b/src/osbridgelcca.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/osbridgelcca.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/osbridgelcca.egg-info/requires.txt b/src/osbridgelcca.egg-info/requires.txt new file mode 100644 index 0000000..d0250d3 --- /dev/null +++ b/src/osbridgelcca.egg-info/requires.txt @@ -0,0 +1,13 @@ +django +djangorestframework +pyqt5 +plotly +pylatex +pytest +matplotlib +pandas +numpy +psycopg2-binary +requests +flask +gunicorn diff --git a/src/osbridgelcca.egg-info/top_level.txt b/src/osbridgelcca.egg-info/top_level.txt new file mode 100644 index 0000000..7409575 --- /dev/null +++ b/src/osbridgelcca.egg-info/top_level.txt @@ -0,0 +1 @@ +osbridgelcca diff --git a/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py b/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py index 29338f8..a1df10d 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py @@ -1,5 +1,6 @@ import pandas as pd -from .data import * +from data import * +import json class IRC_SP_30: def __init__(self): @@ -237,6 +238,86 @@ def _get_occupancy(self, vehicle_type: str) -> float: return float(self.vot_of_passengers.loc[vehicle_type, COL_OCCUPANCY]) except KeyError: raise ValueError(f"Invalid vehicle type: '{vehicle_type}'") + + def getWPI(self, year: int): + """ + Get the WPI of all relevant items for a given year, relative to BASE_YEAR. + + Returns: + dict: Nested dictionary of WPI values (current_year / BASE_YEAR) + """ + + wpi_dict = {"year": year, "WPI": {}} + + def calc_ratio(df, col): + try: + return float(df.loc[year, col] / df.loc[BASE_YEAR, col]) + except KeyError: + return None + + # ------------------------------------------------ + # 1. Fuel Costs + # ------------------------------------------------ + wpi_dict["WPI"]["fuelCost"] = { + col: calc_ratio(self.voc_fuel_costs, col) + for col in self.voc_fuel_costs.columns + } + + # ------------------------------------------------ + # 2. Vehicle Costs – separated categories + # ------------------------------------------------ + wpi_dict["WPI"]["vehicleCost"] = { + "propertyDamage": { + col: calc_ratio(self.wpi_property_damage, col) + for col in self.wpi_property_damage.columns + }, + "tyreCost": { + col: calc_ratio(self.tyre_costs, col) + for col in self.tyre_costs.columns + }, + "spareParts": { + col: calc_ratio(self.spare_parts_costs, col) + for col in self.spare_parts_costs.columns + }, + "fixedDepreciation": { + col: calc_ratio(self.fixed_depreciation_costs, col) + for col in self.fixed_depreciation_costs.columns + } + } + + # ------------------------------------------------ + # 3. Commodity Holding Cost + # ------------------------------------------------ + wpi_dict["WPI"]["commodityHoldingCost"] = { + col: calc_ratio(self.commodity_holding_cost, col) + for col in self.commodity_holding_cost.columns + } + + # ------------------------------------------------ + # 4. Passenger & Crew Costs + # ------------------------------------------------ + wpi_dict["WPI"]["passengerCrewCost"] = { + col: calc_ratio(self.passenger_crew_costs, col) + for col in self.passenger_crew_costs.columns + } + + # ------------------------------------------------ + # 5. Medical Accessories (Fatal / Major / Minor) + # ------------------------------------------------ + wpi_dict["WPI"]["medicalCost"] = { + col: calc_ratio(self.wpi_medical_accessories, col) + for col in self.wpi_medical_accessories.columns + } + + # ------------------------------------------------ + # 6. Travel Time (VOT) + # ------------------------------------------------ + wpi_dict["WPI"]["votCost"] = { + col: calc_ratio(self.wpi_vot, col) + for col in self.wpi_vot.columns + } + + return wpi_dict if __name__ == "__main__": irc_sp_30 = IRC_SP_30() @@ -245,3 +326,4 @@ def _get_occupancy(self, vehicle_type: str) -> float: print(irc_sp_30.wpi_medical_accessories) print(irc_sp_30.wpi_vot) print(irc_sp_30.vot_of_passengers) + print(irc_sp_30.getWPI(2024)) # add this From 5e9eebc78c8affcddb8f0e9aa1e8bbbaa768b09e Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Thu, 4 Dec 2025 01:54:17 +0530 Subject: [PATCH 21/22] Added voc calculations --- .../utils/core/IRC_standards/IRCSP30_2019.py | 84 +++++ .../desktop_app/widgets/utils/core/main.py | 111 ++++++ .../widgets/utils/core/voc/congestion/core.py | 102 ++++++ .../utils/core/voc/congestion/formulas.py | 164 +++++++++ .../core/voc/congestion/input_validation.py | 83 +++++ .../widgets/utils/core/voc/core.py | 70 ++++ .../core/voc/utils/carriage_way_standards.py | 81 +++++ .../widgets/utils/core/voc/utils/constants.py | 44 +++ .../utils/core/voc/utils/input_validation.py | 106 ++++++ .../utils/core/voc/utils/output_builder.py | 114 +++++++ .../utils/core/voc/utils/post_processor.py | 317 ++++++++++++++++++ .../utils/core/voc/utils/pre_processor.py | 38 +++ .../utils/core/voc/vehicle_types/big_cars.py | 107 ++++++ .../utils/core/voc/vehicle_types/buses.py | 112 +++++++ .../utils/core/voc/vehicle_types/hcv.py | 120 +++++++ .../utils/core/voc/vehicle_types/lcv.py | 113 +++++++ .../utils/core/voc/vehicle_types/mcv.py | 114 +++++++ .../core/voc/vehicle_types/small_cars.py | 89 +++++ .../core/voc/vehicle_types/two_wheeler.py | 107 ++++++ .../widgets/utils/value_of_travel_time.sql | 143 ++++++++ 20 files changed, 2219 insertions(+) create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/IRC_standards/IRCSP30_2019.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/main.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/core.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/formulas.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/input_validation.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/core.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/carriage_way_standards.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/constants.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/input_validation.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/output_builder.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/post_processor.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/pre_processor.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/big_cars.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/buses.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/hcv.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/lcv.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/mcv.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/small_cars.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/two_wheeler.py create mode 100644 src/osbridgelcca/desktop_app/widgets/utils/value_of_travel_time.sql diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/IRC_standards/IRCSP30_2019.py b/src/osbridgelcca/desktop_app/widgets/utils/core/IRC_standards/IRCSP30_2019.py new file mode 100644 index 0000000..b28335d --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/IRC_standards/IRCSP30_2019.py @@ -0,0 +1,84 @@ +# IRC SP-30:2019, Table C.1 Current Vehicle Operating Cost Inputs + +vehicle_costs = { + "two_wheelers": {"ET": 34209, "IT": 61235}, + "small_cars": {"ET": 273728, "IT": 489972}, + "big_cars": {"ET": 558599, "IT": 999892}, + "buses": {"ET": 1647150, "IT": 2948400}, + "lcv": {"ET": 449721, "IT": 805000}, + "hcv": {"ET": 940531, "IT": 1683550}, + "mcv": {"ET": 1415350, "IT": 1415350}, +} + +petroleum_products_costs = { + "petrol": { + "ET": 33.58, + "IT": 79.92, + "units": "Rs/l" + }, + "diesel": { + "ET": 30.51, + "IT": 72.61, + "units": "Rs/l" + }, + "engine_oil": { + "ET": 187.96, + "IT": 384.39, + "units": "Rs/l" + }, + "other_oil": { + "ET": 167.70, + "IT": 338.78, + "units": "Rs/l" + }, + "grease": { + "ET": 183.70, + "IT": 390.90, + "units": "Rs/kg" + } +} + +new_tyres_costs = { + "two_wheelers": { + "ET": 1355, + "IT": 1668, + "units": "Rs/unit", + "num_of_wheels": 2 # Source "Steel - reconstruction, inflation included" sheets + }, + "big_cars": { + "ET": 2940, + "IT": 4456, + "units": "Rs/unit", + "num_of_wheels": 4 # Source "Steel - reconstruction, inflation included" sheets + }, + "small_cars": { + "ET": 2940, + "IT": 4456, + "units": "Rs/unit", + "num_of_wheels": 4 # Source "Steel - reconstruction, inflation included" sheets + }, + "buses": { + "ET": 13475, + "IT": 17500, + "units": "Rs/unit", + "num_of_wheels": 6 # Source "Steel - reconstruction, inflation included" sheets + }, + "lcv": { + "ET": 5420, + "IT": 8900, + "units": "Rs/unit", + "num_of_wheels": 6 # Source "Steel - reconstruction, inflation included" sheets + }, + "hcv": { + "ET": 13890, + "IT": 20000, + "units": "Rs/unit", + "num_of_wheels": 10 # Source "Steel - reconstruction, inflation included" sheets + }, + "mcv": { + "ET": 13890, + "IT": 20000, + "units": "Rs/unit", + "num_of_wheels": 14 # Source "Steel - reconstruction, inflation included" sheets + } +} diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/main.py b/src/osbridgelcca/desktop_app/widgets/utils/core/main.py new file mode 100644 index 0000000..d35e71d --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/main.py @@ -0,0 +1,111 @@ +from voc import core +import voc.congestion.core as co + +vc = 0.8854 +wpi = {'year': 2024, + 'WPI': { + 'fuelCost': { + 'Petrol': 1.8067915690866512, + 'Diesel': 1.7733050847457628, + 'Engine Oil': 1.4496951219512195, + 'Other Oil': 1.6951351351351354, + 'Grease': 1.6951351351351354 + }, + 'vehicleCost': { + 'propertyDamage': { + 'Small Cars': 1.1395759717314486, + 'Big Cars': 1.1395759717314486, + 'Two Wheeler': 1.1395759717314486, + 'Ordinary Buses': 1.1395759717314486, + 'Deluxe Buses': 1.1395759717314486, + 'LCV': 1.1395759717314486, + 'HCV': 1.1395759717314486, + 'MCV': 1.1395759717314486 + }, + 'tyreCost': { + 'Small Cars': 1.123991935483871, + 'Big Cars': 1.123991935483871, + 'Two Wheeler': 1.1336538461538461, + 'Ordinary Buses': 1.1702564102564101, + 'Deluxe Buses': 1.1702564102564101, + 'LCV': 1.1702564102564101, + 'HCV': 1.1702564102564101, + 'MCV': 1.1702564102564101 + }, + 'spareParts': { + 'Small Cars': 1.1395759717314486, + 'Big Cars': 1.1395759717314486, + 'Two Wheeler': 1.1395759717314486, + 'Ordinary Buses': 1.1395759717314486, + 'Deluxe Buses': 1.1395759717314486, + 'LCV': 1.1395759717314486, + 'HCV': 1.1395759717314486, + 'MCV': 1.1395759717314486 + }, + 'fixedDepreciation': { + 'Small Cars': 1.1388400702987698, + 'Big Cars': 1.1388400702987698, + 'Two Wheeler': 1.1388400702987698, + 'Ordinary Buses': 1.1388400702987698, + 'Deluxe Buses': 1.1388400702987698, + 'LCV': 1.1388400702987698, + 'HCV': 1.1388400702987698, + 'MCV': 1.1388400702987698 + } + }, + 'commodityHoldingCost': { + 'Small Cars': 1.4788593903638152, + 'Big Cars': 1.4788593903638152, + 'Two Wheeler': 1.4788593903638152, + 'Ordinary Buses': 1.4788593903638152, + 'Deluxe Buses': 1.4788593903638152, + 'LCV': 1.4788593903638152, + 'HCV': 1.4788593903638152, + 'MCV': 1.4788593903638152 + }, + 'passengerCrewCost': { + 'Passenger Cost': 1.2706270627062706, + 'Crew Cost': 1.2706270627062706 + }, + 'medicalCost': { + 'Fatal': 1.0867924528301887, + 'Major Injury': 1.0867924528301887, + 'Minor Injury': 1.0867924528301887 + }, + 'votCost': { + 'Small Cars': 1.2706270627062706, + 'Big Cars': 1.2706270627062706, + 'Two Wheeler': 1.2706270627062706, + 'Ordinary Buses': 1.2706270627062706, + 'Deluxe Buses': 1.2706270627062706, + 'LCV': 1.2706270627062706, + 'HCV': 1.2706270627062706, + 'MCV': 1.2706270627062706 + } + } + } + +vehicle_input = { + "vehicle_info": { + "small_cars": 3943, + "big_cars": 2397, + "two_wheelers": 12505, + "buses": 329, + "lcv": 271, + "hcv": 0, + "mcv": 1 + }, + # "carriageway_width": 10, ### ONLY REQUIRED WHEN "lane_type" = "EW" + "rg_roughness_factor": 2000, + "fl_fall_factor": 0, + "rs_rise_factor": 0, + "lane_type": "2L", + "power_weight_ratio_pwr": { + "mcv": 8, + "hcv": 7.22 + } +} + +a = core.main(vehicle_input, wpi) +calculate_total_adjusted_costs = co.calculate_total_adjusted_costs(a, vc, vehicle_input) +print(calculate_total_adjusted_costs) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/core.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/core.py new file mode 100644 index 0000000..a950155 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/core.py @@ -0,0 +1,102 @@ +import voc.congestion.formulas as cf +import voc.congestion.input_validation as validate + +def calculate_total_adjusted_costs(a, vc, vehicle_input, debug=False): + """ + Calculate total adjusted costs (distance + time) with congestion factors applied. + Parameters: + a (dict): Base cost data containing 'distanceCost' and 'timeCost'. + vc (float): Volume-to-capacity ratio. + lane_type (str): Type of lane (e.g., "SL", "IL", "2L", "4L", "6L"). + vehicle_input (dict): Vehicle input data containing vehicle counts. + debug (bool): If True, includes detailed breakdown of calculations. + """ + lane_type = vehicle_input["lane_type"] + validate.validate(a, vc, lane_type, vehicle_input, debug) + + cv = { + "distance_congestion_factors": cf.distance_congestion_factors(lane_type, vc=vc), + "time_congestion_factors": cf.time_congestion_factors(lane_type, vc=vc) + } + + total_distance_cost = {'IT': 0, 'ET': 0} + total_time_cost = {'IT': 0, 'ET': 0} + + breakdown = {} # NEW dictionary for detailed debugging + + vehicles = set(a['distanceCost'].keys()) & set(a['timeCost'].keys()) + vehicles -= {'units', 'total'} + + for vehicle in vehicles: + + veh_count = vehicle_input["vehicle_info"].get(vehicle, 0) + + # Base costs + distance_it = a['distanceCost'][vehicle]['IT'] + distance_et = a['distanceCost'][vehicle]['ET'] + time_it = a['timeCost'][vehicle]['IT'] + time_et = a['timeCost'][vehicle]['ET'] + + # Factors + cd = cv['distance_congestion_factors'].get(vehicle, 1) + ct = cv['time_congestion_factors'].get(vehicle, 1) + # pcu_factor = pcu.get(vehicle, 1) + + # Adjusted costs per vehicle + adj_distance_it = distance_it * cd + adj_distance_et = distance_et * cd + adj_time_it = time_it * ct + adj_time_et = time_et * ct + + # Total cost including volume + total_distance_cost['IT'] += adj_distance_it * veh_count + total_distance_cost['ET'] += adj_distance_et * veh_count + total_time_cost['IT'] += adj_time_it * veh_count + total_time_cost['ET'] += adj_time_et * veh_count + + if debug: + # Save detailed breakdown + breakdown[vehicle] = { + "vehicle_count": veh_count, + "factors": { + "distance_congestion_factor": cd, + "time_congestion_factor": ct, + }, + "base_costs": { + "distance": {"IT": distance_it, "ET": distance_et}, + "time": {"IT": time_it, "ET": time_et} + }, + "adjusted_costs_per_vehicle": { + "distance": {"IT": adj_distance_it, "ET": adj_distance_et}, + "time": {"IT": adj_time_it, "ET": adj_time_et} + }, + "total_adjusted_costs": { + "distance": { + "IT": adj_distance_it * veh_count, + "ET": adj_distance_et * veh_count + }, + "time": { + "IT": adj_time_it * veh_count, + "ET": adj_time_et * veh_count + } + }, + "total": { # <-- NEW FIELD + "IT": (adj_distance_it * veh_count) + (adj_time_it * veh_count), + "ET": (adj_distance_et * veh_count) + (adj_time_et * veh_count) + } + } + + result = { + "distance_total": total_distance_cost, + "time_total": total_time_cost, + "total": { + "IT": total_distance_cost["IT"] + total_time_cost["IT"], + "ET": total_distance_cost["ET"] + total_time_cost["ET"] + }, + "unit": "Rs/km" + } + + if debug: + result["breakdown"] = breakdown + + return result diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/formulas.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/formulas.py new file mode 100644 index 0000000..90746f0 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/formulas.py @@ -0,0 +1,164 @@ +def _cap_between_one_and_two(results: dict) -> dict: + return { + k: (1 if (x := v) < 1 else 2 if x > 2 else x) + if isinstance(v, (int, float)) + else (_ for _ in ()).throw(ValueError(f"Non-numeric value for '{k}': {v}")) + for k, v in results.items() + } + +# -------------------------------------------------------- +# Table 10 – Time-Related Congestion Factors +# -------------------------------------------------------- +def time_congestion_factors(lane_type: str, vc: float) -> dict: + if type(vc) not in [int, float]: + raise ValueError(f"Volume to capacity ratio 'vc' must be a number, got {type(vc)} instead.") + + if lane_type == "SL": + results = { + "small_cars": 0.747 + 1.458 * vc, + "big_cars": 0.747 + 1.458 * vc, + "two_wheelers": 0.911 + 0.807 * vc, + "buses": 0.838 + 1.307 * vc, + "lcv": 0.880 + 1.200 * vc, + "hcv": 0.858 + 1.101 * vc, + "mcv": 0.858 + 1.101 * vc + } + + elif lane_type == "IL": + results = { + "small_cars": 0.930 + 1.025 * vc, + "big_cars": 0.930 + 1.025 * vc, + "two_wheelers": 0.776 + 0.728 * vc, + "buses": 0.942 + 0.670 * vc, + "lcv": 1.012 + 0.863 * vc, + "hcv": 0.920 + 1.033 * vc, + "mcv": 0.920 + 1.033 * vc + } + + elif lane_type == "2L": + results = { + "small_cars": 1.087 + 0.483 * vc, + "big_cars": 1.087 + 0.483 * vc, + "two_wheelers": 0.804 + 0.865 * vc, + "buses": 0.864 + 0.543 * vc, + "lcv": 0.925 + 0.573 * vc, + "hcv": 0.878 + 0.561 * vc, + "mcv": 0.878 + 0.561 * vc + } + + elif lane_type == "4L": + results = { + "small_cars": 0.4834*(vc**2) + 0.4095*vc + 0.99, + "big_cars": 0.4834*(vc**2) + 0.4095*vc + 0.99, + "two_wheelers": 1.1063*vc + 0.99, + "buses": 1.534*(vc**2) - 0.2301*vc + 0.99, + "lcv": 0.8441*(vc**2) + 0.4337*vc + 0.99, + "hcv": 1.1036*(vc**2) + 0.4124*vc + 0.99, + "mcv": 0.3709*(vc**2) + 0.4604*vc + 0.99 + } + + elif lane_type == "6L": + results = { + "small_cars": 2.1947*(vc**2) - 0.3352*vc + 1, + "big_cars": 2.1947*(vc**2) - 0.3352*vc + 1, + "two_wheelers": 0.8998*(vc**2) + 0.9407*vc + 1, + "buses": 0.9412*(vc**2) - 0.1881*vc + 1, + "lcv": 0.8441*(vc**2) + 0.4337*vc + 0.99, + "hcv": 1.593*(vc**2) - 0.0523*vc + 1, + "mcv": 1.0234*vc + 1 + } + + elif lane_type in ["8L", "EW"]: + results = { + "small_cars": -0.2441*(vc**2) + 0.9003*vc + 0.99, + "big_cars": -0.2441*(vc**2) + 0.9003*vc + 0.99, + "two_wheelers": 0.3973*vc + 1, + "buses": -0.0092*(vc**2) + 0.4559*vc + 1, + "lcv": -0.1476*(vc**2) + 0.5986*vc + 0.99, + "hcv": 0.2143*(vc**2) + 0.457*vc + 1, + "mcv": -0.373*(vc**2) + 0.7575*vc + 1 + } + + else: + raise ValueError("Unknown lane type. Use 'SL','IL','2L','4L','6L','8L'.") + + return _cap_between_one_and_two(results) + + +# -------------------------------------------------------- +# Table 11 – Distance-Related Congestion Factors +# -------------------------------------------------------- +def distance_congestion_factors(lane_type: str, vc: float) -> dict: + if type(vc) not in [int, float]: + raise ValueError(f"'vc' must be numeric, got {type(vc)}") + + if lane_type == "SL": + results = { + "small_cars": 0.924 + 0.680 * vc, + "big_cars": 0.924 + 0.680 * vc, + "two_wheelers": 0.990 + 0.830 * vc, + "buses": 1.000 + 1.000 * vc, + "lcv": 1.00 + 0.90 * vc, + "hcv": 1.179 + 0.757 * vc, + "mcv": 1.179 + 0.757 * vc + } + + elif lane_type == "IL": + results = { + "small_cars": 0.924 + 0.635 * vc, + "big_cars": 0.924 + 0.635 * vc, + "two_wheelers": 0.942 + 0.118 * vc, + "buses": 0.800 + 1.200 * vc, + "lcv": 0.90 + 1.00 * vc, + "hcv": 1.104 + 0.755 * vc, + "mcv": 1.104 + 0.755 * vc + } + + elif lane_type == "2L": + results = { + "small_cars": 0.893 + 0.259 * vc, + "big_cars": 0.893 + 0.259 * vc, + "two_wheelers": 0.917 + 0.112 * vc, + "buses": 0.800 + 1.10 * vc, + "lcv": 0.90 + 1.00 * vc, + "hcv": 0.925 + 0.482 * vc, + "mcv": 0.900 + 1.40 * vc + } + + elif lane_type == "4L": + results = { + "small_cars": 2.4405*(vc**2) - 2.8919*vc + 1.8939, + "big_cars": 3.713*(vc**2) - 4.2811*vc + 2.2173, + "two_wheelers": 4.9774*(vc**2) - 4.8846*vc + 2.1831, + "buses": 3.713*(vc**2) - 4.2811*vc + 2.2173, + "lcv": 2.2518*(vc**2) - 1.2471*vc + 1.1348, + "hcv": 2.8147*(vc**2) - 1.5589*vc + 1.4185, + "mcv": 3.6591*(vc**2) - 2.0266*vc + 1.8441 + } + + elif lane_type == "6L": + results = { + "small_cars": 2.8163*(vc**2) - 3.1278*vc + 1.9629, + "big_cars": 4.3108*(vc**2) - 4.6276*vc + 2.3129, + "two_wheelers": 5.6528*(vc**2) - 4.5691*vc + 1.9083, + "buses": 4.3108*(vc**2) - 4.6276*vc + 2.3129, + "lcv": 14.990*(vc**2) - 12.014*vc + 3.2242, + "hcv": 18.737*(vc**2) - 15.017*vc + 4.0302, + "mcv": 24.3581*(vc**2) - 19.522*vc + 5.2393 + } + + elif lane_type in ["8L", "EW"]: + results = { + "small_cars": 0.5239*(vc**2) - 0.9289*vc + 1.4847, + "big_cars": 0.7734*(vc**2) - 1.3037*vc + 1.596, + "two_wheelers": 2.4879*(vc**2) - 3.9095*vc + 2.6253, + "buses": 0.7734*(vc**2) - 1.3037*vc + 1.596, + "lcv": 0.7707*(vc**2) - 0.7214*vc + 1.0232, + "hcv": 0.9634*(vc**2) - 0.9018*vc + 1.279, + "mcv": 1.2524*(vc**2) - 1.1723*vc + 1.6627 + } + + else: + raise ValueError("Unknown lane type. Use 'SL','IL','2L','4L','6L','8L'.") + + return _cap_between_one_and_two(results) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/input_validation.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/input_validation.py new file mode 100644 index 0000000..ee250e5 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/congestion/input_validation.py @@ -0,0 +1,83 @@ +from voc.utils import carriage_way_standards + +def validate(a, vc, lane_type, vehicle_input, debug=False): + errors = [] + + # ----------------------------- + # Validate 'a' + # ----------------------------- + if not isinstance(a, dict): + errors.append("'a' must be a dictionary.") + else: + for key in ['distanceCost', 'timeCost']: + if key not in a: + errors.append(f"'a' must contain '{key}' key.") + elif not isinstance(a[key], dict): + errors.append(f"'{key}' in 'a' must be a dictionary.") + else: + # Check totals + if 'total' not in a[key]: + errors.append(f"'{key}' must contain 'total' breakdown.") + # Check units + if 'units' not in a[key]: + errors.append(f"'{key}' must contain 'units' key.") + + # ----------------------------- + # Validate 'vc' + # ----------------------------- + if not isinstance(vc, (int, float)) or vc < 0: + errors.append(f"'vc' must be a non-negative number. Provided: {vc}") + + # ----------------------------- + # Validate 'lane_type' + # ----------------------------- + if not isinstance(lane_type, str): + errors.append(f"'lane_type' must be a string. Provided: {lane_type}") + else: + available_types, _ = carriage_way_standards.CarriagewayStandards.list_types() + if lane_type not in available_types: + errors.append(f"'lane_type' must be one of {available_types}. Provided: {lane_type}") + + # ----------------------------- + # Validate 'vehicle_input' + # ----------------------------- + if not isinstance(vehicle_input, dict): + errors.append("'vehicle_input' must be a dictionary.") + else: + vehicle_info = vehicle_input.get("vehicle_info") + if not isinstance(vehicle_info, dict): + errors.append("'vehicle_input' must contain 'vehicle_info' dictionary.") + else: + # Check vehicle counts are non-negative numbers + for vt, count in vehicle_info.items(): + if not isinstance(count, (int, float)) or count < 0: + errors.append(f"Vehicle count for '{vt}' must be non-negative number. Provided: {count}") + + # Check power_weight_ratio_pwr if present + pwr = vehicle_input.get("power_weight_ratio_pwr") + if pwr is not None: + if isinstance(pwr, dict): + for vt, val in pwr.items(): + if not isinstance(val, (int, float)) or val <= 0: + errors.append(f"power_weight_ratio_pwr for '{vt}' must be > 0. Provided: {val}") + elif not isinstance(pwr, (int, float)) or pwr <= 0: + errors.append(f"power_weight_ratio_pwr must be numeric and > 0. Provided: {pwr}") + + # Check carriageway_width + width = vehicle_input.get("carriageway_width") + if width is None or not isinstance(width, (int, float)) or width <= 0: + errors.append(f"'carriageway_width' must be positive number. Provided: {width}") + + # ----------------------------- + # Validate 'debug' + # ----------------------------- + if not isinstance(debug, bool): + errors.append(f"'debug' must be a boolean. Provided: {debug}") + + # ----------------------------- + # Raise error if any validation fails + # ----------------------------- + if errors: + raise ValueError("\n".join(errors)) + + return True diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/core.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/core.py new file mode 100644 index 0000000..2ac8edc --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/core.py @@ -0,0 +1,70 @@ +from voc.utils.input_validation import validate_input +from voc.vehicle_types import big_cars, buses, hcv, lcv, mcv, small_cars, two_wheeler +import voc.utils.post_processor as pp +import sys + +# Map vehicle_info keys to their model modules +MODEL_MAP = { + "big_cars": big_cars, + "small_cars": small_cars, + "two_wheelers": two_wheeler, + "buses": buses, + "hcv": hcv, + "lcv": lcv, + "mcv": mcv, +} + +def main(vehicle_input, wpi, debug = False): + """ + Validates input and executes the correct vehicle models for all vehicles with count > 0. + Adapts input to match the old format expected by model_module.test(). + + Parameters: + vehicle_input (dict): Input data containing vehicle counts and other parameters. + wpi (dict): Wholesale Price Index data for cost adjustments. + debug (bool): If True, includes detailed breakdown of calculations. Files generated in the `debug` folder. + """ + + # -------------------- + # 1. Validate input + # -------------------- + + # -------------------- + # 1. Validate input + # -------------------- + try: + validate_input(vehicle_input) + except ValueError as e: + print("Input validation failed:\n", str(e)) + sys.exit(1) # Terminate the program with an error code + + + # -------------------- + # 2. Vehicle model execution + # -------------------- + vehicle_info = vehicle_input.get("vehicle_info", {}) + results = {} + + for vt, count in vehicle_info.items(): + if count > 0: + model_module = MODEL_MAP.get(vt) + if model_module is None: + results[vt] = {"status": "error", + "message": f"No model available for '{vt}'."} + else: + # Keep input_for_model same structure as before + input_for_model = { + **{k: v for k, v in vehicle_input.items() if k != "vehicle_info"}, + "vehicle_type": vt, + "rf_rise_and_fall_factor": vehicle_input.get("fl_fall_factor", 0) + + vehicle_input.get("rs_rise_factor", 0) + } + + # Adapt power_weight_ratio_pwr if it's a dict + if isinstance(vehicle_input.get("power_weight_ratio_pwr"), dict): + input_for_model["power_weight_ratio_pwr"] = vehicle_input["power_weight_ratio_pwr"].get(vt, 0) + + results[vt] = model_module.compute_voc(input_for_model) + summaryOfVOC = pp.post_process(results, wpi, debug) + + return summaryOfVOC diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/carriage_way_standards.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/carriage_way_standards.py new file mode 100644 index 0000000..c503070 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/carriage_way_standards.py @@ -0,0 +1,81 @@ +from dataclasses import dataclass +from types import MappingProxyType +from typing import Optional, Tuple, List, Union + +@dataclass(frozen=True) +class CarriagewayStandards: + """ + Manages standard carriageway widths for different road types. + + Methods: + - list_types(): Returns available carriageway types and usage note. + - get_width(type_name, custom_width=None): Retrieves width for a given type, with type checks. + """ + + _STANDARD_WIDTHS: MappingProxyType = MappingProxyType({ + "SL": 3.75, + "IL": 5.50, + "2L": 7.00, + "4L": 2 * 3.5, + "6L": 3 * 3.5, + "8L": 4 * 3.5, + "EW": None, # custom input required + }) + + NOTE: str = ( + "Note: 'Expressway (custom width required)' requires user input. " + "Carriageway width represents the total width of the roadway for vehicular traffic." + ) + + @classmethod + def list_types(cls) -> Tuple[List[str], str]: + """ + List all available carriageway types with a usage note. + + Returns: + tuple: (list of carriageway type names, usage note) + """ + return list(cls._STANDARD_WIDTHS.keys()), cls.NOTE + + @classmethod + def get_width(cls, type_name: str, custom_width: Optional[Union[int, float]] = None) -> Tuple[Optional[float], str]: + """ + Retrieve the width for a given carriageway type, with input type validation. + + Args: + type_name (str): Name of the carriageway type. + custom_width (float or int, optional): Custom width for expressways. + + Returns: + tuple: (width in meters or None, message string) + """ + # Check type_name + if not isinstance(type_name, str): + return None, f"Error: 'type_name' must be a string, got {type(type_name).__name__}." + + if type_name not in cls._STANDARD_WIDTHS: + return None, f"Error: Unknown carriageway type '{type_name}'." + + width = cls._STANDARD_WIDTHS[type_name] + + # Standard width available + if width is not None: + return width, "Standard width applied." + + # Custom width required (Expressway) + if custom_width is None: + return None, ( + "Custom width required for this type. " + "Please provide a width in meters (float or int). " + "Carriageway width represents the total width of the roadway for vehicular traffic." + ) + + # Validate custom_width type + if not isinstance(custom_width, (int, float)): + return None, f"Error: 'custom_width' must be a number (int or float), got {type(custom_width).__name__}." + + # Validate positive width + if custom_width <= 0: + return None, "Error: 'custom_width' must be a positive number." + + return float(custom_width), "Custom expressway width applied." diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/constants.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/constants.py new file mode 100644 index 0000000..441e1e9 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/constants.py @@ -0,0 +1,44 @@ +vehicle_type_list = ["small_cars", "big_cars", + "two_wheelers", "buses", "lcv", "hcv", "mcv"] + +petrolToDieselRatio = { + "small_cars": { + "petrol": 0.7, + "diesel": 0.3 + }, + "big_cars": { + "petrol": 0.3, + "diesel": 0.7 + }, + "two_wheelers": { + "petrol": 1, + "diesel": 0 + }, + "buses": { + "petrol": 0, + "diesel": 1 + }, + "lcv": { + "petrol": 0, + "diesel": 1 + }, + "hcv": { + "petrol": 0, + "diesel": 1 + }, + "mcv": { + "petrol": 0, + "diesel": 1 + }, +} + + +pcu = { + "small_cars": 1, + "big_cars": 1, + "two_wheelers": 0.75, + "buses": 2.2, + "lcv": 1.4, + "hcv": 2.2, + "mcv": 2.2 +} \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/input_validation.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/input_validation.py new file mode 100644 index 0000000..5bb0ca5 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/input_validation.py @@ -0,0 +1,106 @@ +from voc.utils import carriage_way_standards +import voc.utils.constants as constants + +def validate_input(vehicle_input): + errors = [] + + # ----------------------------- + # Validate lane_type using carriagewayStandards + # ----------------------------- + available_types, _ = carriage_way_standards.CarriagewayStandards.list_types() + lane_type = vehicle_input.get("lane_type") + + if not isinstance(lane_type, str): + errors.append("lane_type must be a string.") + else: + if lane_type not in available_types: + errors.append( + f"lane_type '{lane_type}' is invalid. Allowed: {available_types}" + ) + + # ----------------------------- + # Validate carriageway width logic + # ----------------------------- + if lane_type == "EW": + custom_width = vehicle_input.get("carriageway_width") + if custom_width is None or not isinstance(custom_width, (int, float)) or custom_width <= 0: + errors.append( + "For Expressway type, 'carriageway_width' must be a positive number (custom width required)." + ) + else: + standard_width, msg = carriage_way_standards.CarriagewayStandards.get_width(lane_type) + if standard_width is None: + errors.append(f"Could not retrieve standard width: {msg}") + else: + vehicle_input["carriageway_width"] = standard_width + + # ----------------------------- + # Validate numeric fields + # ----------------------------- + numeric_fields = [ + "rg_roughness_factor", + "fl_fall_factor", + "rs_rise_factor" + ] + + for field in numeric_fields: + value = vehicle_input.get(field) + if not isinstance(value, (int, float)): + errors.append(f"{field} must be a number (int or float).") + + # ----------------------------- + # Validate vehicle_info + # ----------------------------- + vehicle_info = vehicle_input.get("vehicle_info") + if not isinstance(vehicle_info, dict): + errors.append("vehicle_info must be a dictionary.") + else: + missing_keys = [vtype for vtype in constants.vehicle_type_list if vtype not in vehicle_info] + if missing_keys: + errors.append( + f"Missing vehicle types in vehicle_info: {missing_keys}. All must be present." + ) + + invalid_keys = [vtype for vtype in vehicle_info if vtype not in constants.vehicle_type_list] + if invalid_keys: + errors.append( + f"Invalid vehicle types in vehicle_info: {invalid_keys}. Allowed: {constants.vehicle_type_list}" + ) + + for vtype, count in vehicle_info.items(): + if not isinstance(count, (int, float)) or count < 0: + errors.append(f"Count for '{vtype}' must be a non-negative number.") + + # ----------------------------- + # Validate power_weight_ratio_pwr for HCV, MCV + # ----------------------------- + mcv_count = vehicle_info.get("mcv", 0) + hcv_count = vehicle_info.get("hcv", 0) + + if mcv_count > 0 or hcv_count > 0: + pwr = vehicle_input.get("power_weight_ratio_pwr") + if pwr is None: + errors.append( + "power_weight_ratio_pwr must be provided if LCV, HCV, or MCV count > 0." + ) + elif isinstance(pwr, dict): + # Check all relevant vehicle types have a numeric value > 0 + for vt in ["mcv", "hcv"]: + if vehicle_info.get(vt, 0) > 0: + if vt not in pwr or not isinstance(pwr[vt], (int, float)) or pwr[vt] <= 0: + errors.append( + f"power_weight_ratio_pwr for '{vt}' is required, must be a numeric value greater than 0, but the provided value is {value}." + ) + elif not isinstance(pwr, (int, float)) or pwr <= 0: + errors.append( + f"power_weight_ratio_pwr must be numeric (int/float) and > 0, or a dictionary keyed by vehicle type with values > 0." + ) + + + # ----------------------------- + # Return or raise errors + # ----------------------------- + if errors: + raise ValueError("\n".join(errors)) + + return True \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/output_builder.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/output_builder.py new file mode 100644 index 0000000..1cf1d0c --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/output_builder.py @@ -0,0 +1,114 @@ +from typing import Dict, Any + +def build_voc_output( + vt: str, + lane: str, + velocity: float, + petrol: float, + diesel: float, + SP_ET: float, + SP_IT: float, + ML: float, + TL: float, + EOL: float, + OL: float, + G: float, + FXC_ET: float, + FXC_IT: float, + DC_ET: float, + DC_IT: float, + PT: float, + crew: float, + CHC: float, + UPD: float +) -> Dict[str, Any]: + + def nn(x): + # Helper to ensure non-negative values + return max(x, 0) + + return { + "vehicle_type": vt, + "lane_type": lane, + "velocity": { + "value": nn(velocity), + "unit": "kmph" + }, + "VOC_summary": { + "distance_related": { + "fuel_consumption": { + "petrol": nn(petrol), + "diesel": nn(diesel), + "unit": "liters per 1000 km", + "iHTC": False + }, + "spare_parts": { + "ET": nn(SP_ET)/100, + "IT": nn(SP_IT)/100, + "unit": "Rs/km", + "iHTC": True + }, + "maintenance_labour": { + "value": nn(ML)/100, + "unit": "Rs/km", + "iHTC": False + }, + "tyre_life": { + "value": nn(TL), + "unit": "km/tyre", + "iHTC": False + }, + "engine_oil": { + "value": nn(EOL), + "unit": "liters per 1000 km", + "iHTC": False + }, + "other_oil": { + "value": nn(OL), + "unit": "liters per 10000 km", + "iHTC": False + }, + "grease": { + "value": nn(G), + "unit": "liters per 10000 km", + "iHTC": False + }, + }, + + "time_related": { + "fixed_cost": { + "ET": nn(FXC_ET), + "IT": nn(FXC_IT), + "unit": "Rs/km", + "iHTC": True + }, + "depreciation_cost": { + "ET": nn(DC_ET), + "IT": nn(DC_IT), + "unit": "Rs/km", + "iHTC": True + }, + "passenger_time_cost": { + "value": nn(PT), + "unit": "Rs/km", + "iHTC": False + }, + "crew_cost": { + "value": nn(crew), + "unit": "Rs/km", + "iHTC": False + }, + "commodity_holding_cost": { + "value": nn(CHC), + "unit": "Rs/km", + "iHTC": False + }, + }, + + "utilisation": { + "value": nn(UPD), + "iHTC": False + }, + "note": "All Values mentioned here are without WPI adjustments!" + } + } diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/post_processor.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/post_processor.py new file mode 100644 index 0000000..5ffe386 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/post_processor.py @@ -0,0 +1,317 @@ +import IRC_standards.IRCSP30_2019 as tableC1 +from voc.utils.constants import vehicle_type_list, petrolToDieselRatio +import json +import os +from typing import Any, Dict, Union + + +def calculate_total_cost(data: Dict[str, Any]) -> Dict[str, Any]: + """ + Calculate total cost for both distanceCost and timeCost in the data. + Updates totals based on IT/ET or value fields. + Returns a dictionary with both totals and vehicle-specific totals. + """ + total_cost: Dict[str, Any] = {} + + for cost_type in ["distanceCost", "timeCost"]: + if cost_type not in data: + continue + + total_cost[cost_type] = {"total": {"IT": 0.0, "ET": 0.0}} + + for vehicle_type, components in data[cost_type].items(): + if vehicle_type == "total": + continue + + total_cost[cost_type][vehicle_type] = {"IT": 0.0, "ET": 0.0} + + for comp_name, comp_values in components.items(): + if comp_name.startswith("total_"): + continue + + if isinstance(comp_values, dict): + if comp_values.get('iHTC', False): + total_cost[cost_type]["total"]["IT"] += comp_values.get("IT", 0) + total_cost[cost_type]["total"]["ET"] += comp_values.get("ET", 0) + total_cost[cost_type][vehicle_type]["IT"] += comp_values.get("IT", 0) + total_cost[cost_type][vehicle_type]["ET"] += comp_values.get("ET", 0) + else: + value = comp_values.get("value", 0) + total_cost[cost_type]["total"]["IT"] += value + total_cost[cost_type]["total"]["ET"] += value + total_cost[cost_type][vehicle_type]["IT"] += value + total_cost[cost_type][vehicle_type]["ET"] += value + elif isinstance(comp_values, (int, float)): + total_cost[cost_type]["total"]["IT"] += comp_values + total_cost[cost_type]["total"]["ET"] += comp_values + total_cost[cost_type][vehicle_type]["IT"] += comp_values + total_cost[cost_type][vehicle_type]["ET"] += comp_values + else: + continue + + total_cost[cost_type]["units"] = "Rs/km/veh" + + return total_cost + + +def getWPI(category, vehicle_type, wpi): + mapping = { + "small_cars": "Small Cars", + "big_cars": "Big Cars", + "two_wheelers": "Two Wheeler", + "buses": "Ordinary Buses", + "lcv": "LCV", + "hcv": "HCV", + "mcv": "MCV" + } + + vt = mapping.get(vehicle_type) + if vt is None: + raise ValueError(f"Invalid vehicle type: {vehicle_type}") + + WPI_data = wpi.get("WPI", {}) + + # 1️⃣ CATEGORY EXISTS AT TOP LEVEL (fuelCost, commodityHoldingCost, medicalCost, passengerCrewCost, votCost) + if category in WPI_data: + block = WPI_data[category] + + if isinstance(block, dict) and vt in block: + return block[vt] + + return block # entire block if not vehicle-specific + + # 2️⃣ CATEGORY INSIDE vehicleCost (tyreCost, spareParts, fixedDepreciation, propertyDamage) + vehicle_cost = WPI_data.get("vehicleCost", {}) + if category in vehicle_cost: + block = vehicle_cost[category] + return block.get(vt) + + # Category not found + raise ValueError(f"WPI category '{category}' not found for vehicle type '{vehicle_type}'.") + + +# ----------------- Helper functions ----------------- + +def per_km_cost(liters_per_unit: float, price_IT: float, price_ET: float, factor: float = 1000.0): + IT = (liters_per_unit * price_IT) / factor + ET = (liters_per_unit * price_ET) / factor + return IT, ET + + +def apply_wpi(cost: Dict[str, float], wpi_val: Union[float, Dict[str, Any], None]) -> dict[str, float | str | bool]: + # normalize wpi_val to a numeric multiplier + multiplier: float = 1.0 + if isinstance(wpi_val, (int, float)): + multiplier = float(wpi_val) + elif isinstance(wpi_val, dict): + # prefer common numeric keys + if "IT" in wpi_val and isinstance(wpi_val["IT"], (int, float)): + multiplier = float(wpi_val["IT"]) + elif "value" in wpi_val and isinstance(wpi_val["value"], (int, float)): + multiplier = float(wpi_val["value"]) + else: + # try to pick any numeric value from the dict + for v in wpi_val.values(): + if isinstance(v, (int, float)): + multiplier = float(v) + break + + return { + "IT": cost["IT"] * multiplier, + "ET": cost["ET"] * multiplier, + "unit": "Rs/km", + "iHTC": True + } + + +# ----------------- Main function ----------------- + +def post_process(outputFromVocOutputBuilder: Dict[str, Any], wpi: Dict[str, Any], debug: bool = False) -> Dict[str, Any]: + wpiAdjustedValues: Dict[str, Any] = {"distanceCost": {}, "timeCost": {}} + + for vt in vehicle_type_list: + if vt in outputFromVocOutputBuilder: + wpiAdjustedValues["distanceCost"][vt] = {} + + # ---------------- TYRE COST ---------------- + for vt in vehicle_type_list: + vdata = outputFromVocOutputBuilder.get(vt) + if not vdata or "VOC_summary" not in vdata: + continue + + tyre_life_km = vdata["VOC_summary"]["distance_related"]["tyre_life"]["value"] + tyre_info = tableC1.new_tyres_costs[vt] + + IT, ET = tyre_info["IT"], tyre_info["ET"] + num_tyres = tyre_info["num_of_wheels"] + + tyre_cost_per_km_wt = IT * num_tyres / tyre_life_km + tyre_cost_per_km_wot = ET * num_tyres / tyre_life_km + + vdata["VOC_summary"]["distance_related"]["tyre_life"]["tyre_cost_rs_per_km"] = { + "num_tyres": num_tyres, + "IT": tyre_cost_per_km_wt, + "ET": tyre_cost_per_km_wot, + "unit": "Rs/km", + "iHTC": True + } + + wpi_val = getWPI("tyreCost", vt, wpi) + wpiAdjustedValues["distanceCost"][vt]["tyreCost"] = apply_wpi( + {"IT": tyre_cost_per_km_wt, "ET": tyre_cost_per_km_wot}, wpi_val + ) + + # ---------------- FUEL, OILS, GREASE ---------------- + for vt in vehicle_type_list: + vdata = outputFromVocOutputBuilder.get(vt) + if not vdata or "VOC_summary" not in vdata: + continue + + dist = vdata["VOC_summary"]["distance_related"] + wpiAdjustedValues["distanceCost"].setdefault(vt, {}) + + # Fuel + fuel = dist.get("fuel_consumption") + if fuel: + wpi_block = getWPI("fuelCost", vt, wpi) + + petrol_cost = {"IT": 0, "ET": 0} + diesel_cost = {"IT": 0, "ET": 0} + + if fuel.get("petrol", 0) > 0: + liters_1000 = fuel["petrol"] + pw = tableC1.petroleum_products_costs["petrol"]["IT"] + pe = tableC1.petroleum_products_costs["petrol"]["ET"] + petrol_cost["IT"], petrol_cost["ET"] = per_km_cost(liters_1000, pw, pe) # type: ignore + fuel["petrol_cost_rs_per_km"] = { + **petrol_cost, "unit": "Rs/km", "WPI": wpi_block.get("Petrol"), "iHTC": False + } + + if fuel.get("diesel", 0) > 0: + liters_1000 = fuel["diesel"] + dw = tableC1.petroleum_products_costs["diesel"]["IT"] + de = tableC1.petroleum_products_costs["diesel"]["ET"] + diesel_cost["IT"], diesel_cost["ET"] = per_km_cost(liters_1000, dw, de) # type: ignore + fuel["diesel_cost_rs_per_km"] = { + **diesel_cost, "unit": "Rs/km", "WPI": wpi_block.get("Diesel"), "iHTC": False + } + + ratio = petrolToDieselRatio.get(vt, {"petrol": 0, "diesel": 0}) + wt = ratio["petrol"] * petrol_cost["IT"] + ratio["diesel"] * diesel_cost["IT"] + wot = ratio["petrol"] * petrol_cost["ET"] + ratio["diesel"] * diesel_cost["ET"] + + fuel["fuel_cost_rs_per_km_final"] = {"IT": wt, "ET": wot, "unit": "Rs/km", "iHTC": False} + wpiAdjustedValues["distanceCost"][vt]["fuelCost"] = { + "IT": ratio["petrol"] * petrol_cost["IT"] * wpi_block.get("Petrol") + + ratio["diesel"] * diesel_cost["IT"] * wpi_block.get("Diesel"), + "ET": ratio["petrol"] * petrol_cost["ET"] * wpi_block.get("Petrol") + + ratio["diesel"] * diesel_cost["ET"] * wpi_block.get("Diesel"), + "unit": "Rs/km", + "iHTC": True + } + + # Engine oil, other oil, grease + for oil_name in ["engine_oil", "other_oil", "grease"]: + if oil_name in dist: + liters = dist[oil_name]["value"] + factor = 1000 if oil_name == "engine_oil" else 10000 + pw = tableC1.petroleum_products_costs[oil_name]["IT"] + pe = tableC1.petroleum_products_costs[oil_name]["ET"] + IT, ET = per_km_cost(liters, pw, pe, factor) + + wpi_val = getWPI("fuelCost", vt, wpi) + if isinstance(wpi_val, dict): + wpi_val = wpi_val.get(oil_name.replace("_", " ").title()) + else: + wpi_val = 1.0 + + dist[oil_name][f"{oil_name}_cost_rs_per_km"] = {"IT": IT, "ET": ET, "unit": "Rs/km", "WPI": wpi_val, "iHTC": True} + wpiAdjustedValues["distanceCost"][vt][oil_name] = apply_wpi({"IT": IT, "ET": ET}, wpi_val) + + # Spare parts and maintenance labour + if "spare_parts" in dist: + wpi_val = getWPI("spareParts", vt, wpi) + dist["spare_parts"]["WPI"] = wpi_val + wpiAdjustedValues["distanceCost"][vt]["spare_parts"] = { + "IT": dist["spare_parts"]["IT"] * wpi_val, + "ET": dist["spare_parts"]["ET"] * wpi_val, + "unit": "Rs/km", + "iHTC": True + } + + if "maintenance_labour" in dist: + wpi_val = getWPI("spareParts", vt, wpi) + dist["maintenance_labour"]["WPI"] = wpi_val + wpiAdjustedValues["distanceCost"][vt]["maintenance_labour"] = { + "value": dist["maintenance_labour"]["value"] * wpi_val, + "unit": "Rs/km", + "iHTC": False + } + + # ---------------- TIME COST ---------------- + if "time_related" not in vdata["VOC_summary"]: + continue + + time_block = vdata["VOC_summary"]["time_related"] + wpiAdjustedValues["timeCost"][vt] = {} + + # Fixed and depreciation cost + for key in ["fixed_cost", "depreciation_cost"]: + if key in time_block: + wpi_val = getWPI("fixedDepreciation", vt, wpi) + time_block[key]["WPI"] = wpi_val + wpiAdjustedValues["timeCost"][vt][key] = { + "IT": time_block[key]["IT"] * wpi_val, + "ET": time_block[key]["ET"] * wpi_val, + "unit": time_block[key]["unit"], + "iHTC": time_block[key]["iHTC"] + } + + # Passenger and crew cost + for key, sub_key in [("passenger_time_cost", "Passenger Cost"), ("crew_cost", "Crew Cost")]: + if key in time_block: + wpi_val = getWPI("passengerCrewCost", vt, wpi).get(sub_key) + time_block[key]["WPI"] = wpi_val + wpiAdjustedValues["timeCost"][vt][key] = { + "value": time_block[key]["value"] * wpi_val, + "unit": time_block[key]["unit"], + "iHTC": time_block[key]["iHTC"] + } + + # Commodity holding cost + if "commodity_holding_cost" in time_block: + wpi_val = getWPI("commodityHoldingCost", vt, wpi) + time_block["commodity_holding_cost"]["WPI"] = wpi_val + wpiAdjustedValues["timeCost"][vt]["commodity_holding_cost"] = { + "value": time_block["commodity_holding_cost"]["value"] * wpi_val, + "unit": "Rs/km", + "iHTC": time_block["commodity_holding_cost"]["iHTC"] + } + + # Total time cost + total_IT, total_ET = 0, 0 + for cost in wpiAdjustedValues["timeCost"][vt].values(): + if "IT" in cost: + total_IT += cost["IT"] + if "ET" in cost: + total_ET += cost["ET"] + if "value" in cost: + total_IT += cost["value"] + total_ET += cost["value"] + + wpiAdjustedValues["timeCost"][vt]["total_time_cost"] = { + "IT": total_IT, + "ET": total_ET, + "unit": "Rs/km", + "iHTC": True + } + + summaryOfVOC = calculate_total_cost(wpiAdjustedValues) + + if debug: + os.makedirs("debug", exist_ok=True) + json.dump(outputFromVocOutputBuilder, open("debug/outputFromVocOutputBuilder.json", "w"), indent=4) + json.dump(wpiAdjustedValues, open("debug/wpiAdjustedValues.json", "w"), indent=4) + json.dump(summaryOfVOC, open("debug/summaryOfVOC.json", "w"), indent=4) + + return summaryOfVOC diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/pre_processor.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/pre_processor.py new file mode 100644 index 0000000..0388614 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/pre_processor.py @@ -0,0 +1,38 @@ +from typing import TypedDict, Tuple + + +class VehicleInput(TypedDict): + vehicle_type: str + carriageway_width: float + rg_roughness_factor: float + fl_fall_factor: float + rs_rise_factor: float + lane_type: str + rf_rise_and_fall_factor: float + power_weight_ratio_pwr: float | None + + +def extract_vehicle_inputs(vehicle_input: VehicleInput) -> Tuple[ + str, float, float, float, float, str, float +]: + """ + Extracts common vehicle input fields. + + Returns: + vt - vehicle type + W - carriageway width + RG - roughness factor + FL - fall factor + RS - rise factor + lane - lane type + RF - rise and fall factor + """ + vt: str = vehicle_input["vehicle_type"] + W: float = vehicle_input["carriageway_width"] + RG: float = vehicle_input["rg_roughness_factor"] + FL: float = vehicle_input["fl_fall_factor"] + RS: float = vehicle_input["rs_rise_factor"] + lane: str = vehicle_input["lane_type"] + RF: float = vehicle_input["rf_rise_and_fall_factor"] + + return vt, W, RG, FL, RS, lane, RF diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/big_cars.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/big_cars.py new file mode 100644 index 0000000..e57fa36 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/big_cars.py @@ -0,0 +1,107 @@ +from typing import Dict, Any, TypedDict +import IRC_standards.IRCSP30_2019 +from voc.utils.output_builder import build_voc_output +from voc.utils.pre_processor import VehicleInput, extract_vehicle_inputs + + +Vehicle = "big_cars" + + +def compute_voc(vehicle_input: VehicleInput) -> Dict[str, Any]: + vt, W, RG, FL, RS, lane, RF = extract_vehicle_inputs(vehicle_input) + NP: Dict[str, int] = IRC_standards.IRCSP30_2019.vehicle_costs[Vehicle] + + if vt == Vehicle: + # ----------------------------- + # SPEED FORMULA + # ----------------------------- + speed_map: Dict[str, float] = { + 'SL': 67.04 - 0.6984 * RF - 0.002956 * (RG - 2000), + 'IL': 73.82 - 0.7364 * RF - 0.002251 * (RG - 2000), + '2L': 81.92 - 0.7963 * RF - 0.001915 * (RG - 2000), + '4L': 100.625 - 0.394 * RF - 0.00330 * RG, + '6L': 104.159 - 0.398 * RF - 0.00333 * RG, + '8L': 107.743 - 0.402 * RF - 0.00337 * RG, + 'EW': 97.53 - 0.402 * RF - 0.00337 * RG + 0.729 * W + } + + V: float = speed_map.get(lane, 0.0) + + # ----------------------------- + # DISTANCE RELATED + # ----------------------------- + + # Fuel consumption + petrol: float = 30 + (844.085 / V) + 0.003*(V**2) + \ + 0.001*RG + 0.3414*RS - 0.2225*FL + diesel: float = 35 + (983.503 / V) + 0.003*(V**2) + \ + 0.002*RG + 0.339*RS - 0.4785*FL + + # Spare parts + SP_ET: float = (0.0045 * (RG - 2000) * 1e-5) * NP["ET"] + SP_IT: float = (0.0045 * (RG - 2000) * 1e-5) * NP["IT"] + + # Maintenance labour + ML: float = 1.79934 * SP_ET + + # Tyre life + TL: float = 68771 - 147.9*RF - 26.72*(RG/W) + + # Engine oil + EOL: float = 1.8807 + 0.036615*RF + 0.000578*(RG/W) + + # Other oil + OL: float = 1.631 + 0.05167*RF + 0.001867*(RG/W) + + # Grease + G: float = 2.816 + 0.2007*RF + + # ----------------------------- + # TIME RELATED + # ----------------------------- + + # Utilisation + UPD: float = 6.7378 * V + + # Fixed costs + FXC_ET: float = 395.65 / UPD + FXC_IT: float = 400.61 / UPD + + # Depreciation costs + DC_ET: float = 42.83 / UPD + DC_IT: float = 76.68 / UPD + + # Passenger time cost + if lane in ["SL", "IL"]: + PT: float = 244.07 / V + elif lane == "2L": + PT = 328.06 / V + else: + PT = 721.73 / V + + # Crew cost + crew: float = 0.0 + + # Commodity holding cost + CHC: float = 0.0 + + # ----------------------------- + # BUILD FINAL OUTPUT + # ----------------------------- + return build_voc_output( + vt=vt, lane=lane, + velocity=V, + petrol=petrol, diesel=diesel, + SP_ET=SP_ET, SP_IT=SP_IT, + ML=ML, TL=TL, + EOL=EOL, OL=OL, G=G, + FXC_ET=FXC_ET, FXC_IT=FXC_IT, + DC_ET=DC_ET, DC_IT=DC_IT, + PT=PT, crew=crew, CHC=CHC, + UPD=UPD + ) + + else: + raise NotImplementedError( + f"VOC computation for vehicle type '{vt}' is not implemented yet." + ) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/buses.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/buses.py new file mode 100644 index 0000000..05adfcc --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/buses.py @@ -0,0 +1,112 @@ +from typing import Dict, Any, TypedDict +import IRC_standards.IRCSP30_2019 +from voc.utils.output_builder import build_voc_output +from voc.utils.pre_processor import VehicleInput, extract_vehicle_inputs +import math + +Vehicle = "buses" + + +def compute_voc(vehicle_input: VehicleInput) -> Dict[str, Any]: + vt, W, RG, FL, RS, lane, RF = extract_vehicle_inputs(vehicle_input) + NP: Dict[str, int] = IRC_standards.IRCSP30_2019.vehicle_costs[Vehicle] + + if vt == Vehicle: + # ----------------------------- + # SPEED FORMULA + # ----------------------------- + speed_map: Dict[str, float] = { + 'SL': 47.25 - 0.3698 * RF - 0.00165 * (RG - 2000), + 'IL': 52.65 - 0.4031 * RF - 0.00123 * (RG - 2000), + '2L': 54.23 - 0.4111 * RF - 0.00098 * (RG - 2000), + '4L': 75.43 - 0.214 * RF - 0.00198 * RG, + '6L': 77.58 - 0.214 * RF - 0.00198 * RG, + '8L': 79.73 - 0.214 * RF - 0.00198 * RG, + 'EW': 71.13 - 0.214 * RF - 0.00198 * RG + 0.614 * W + } + + V: float = speed_map.get(lane, 0.0) + + # ----------------------------- + # DISTANCE RELATED + # ----------------------------- + + # Fuel consumption + petrol: float = 0 + diesel: float = 34.23 + (4054.42 / V) + 0.02149 * \ + (V ** 2) + 0.001246 * RG + \ + 3.4557 * RS - 1.8454 * FL + + # Spare parts + SP_ET: float = (math.exp(-9.7871 + 0.007373 * RF + + 0.0000723 * RG + 1.925 / W)) * NP["ET"] + SP_IT: float = (math.exp(-10.1126 + 0.007373 * RF + + 0.0000723 * RG + 1.925 / W)) * NP["IT"] + + # Maintenance labour + ML: float = 1.1781 * SP_ET + + # Tyre life + TL: float = 38519 - 389.52 * RF - 1.32 * RG + 983.829 * W + + # Engine oil + EOL: float = 0.4303 + 0.001494 * RF + 0.0007885 * (RG / W) + + # Other oil + OL: float = 3.3201 + 0.002889 * RF + 0.0008217 * RG - 0.3295 * W + + # Grease + G: float = 4.992 + 0.03376 * RF + 0.3634 * W + + # ----------------------------- + # TIME RELATED + # ----------------------------- + + # Utilisation + UPD: float = 22.7134 + 12.2569 * V + + # Fixed costs + FXC_ET: float = 772.89 / UPD + FXC_IT: float = 1415.09 / UPD + + # Depreciation costs + DC_ET: float = 221.00 / UPD + DC_IT: float = 355.71 / UPD + + # Passenger time cost + if lane in ['SL', 'IL']: + PT = 7297.63 / UPD + elif lane == '2L': + PT = 15509.80 / UPD + elif lane in ['4L', '6L', '8L']: + PT = 23721.98 / UPD + elif lane == 'EW': + PT = 28385.28 / UPD + else: + PT = 0 + + # Crew cost + crew: float = 3775.3 / UPD + + # Commodity holding cost + CHC: float = 0.0 + + # ----------------------------- + # BUILD FINAL OUTPUT + # ----------------------------- + return build_voc_output( + vt=vt, lane=lane, + velocity=V, + petrol=petrol, diesel=diesel, + SP_ET=SP_ET, SP_IT=SP_IT, + ML=ML, TL=TL, + EOL=EOL, OL=OL, G=G, + FXC_ET=FXC_ET, FXC_IT=FXC_IT, + DC_ET=DC_ET, DC_IT=DC_IT, + PT=PT, crew=crew, CHC=CHC, + UPD=UPD + ) + else: + raise NotImplementedError( + f"VOC computation for vehicle type '{vt}' is not implemented yet." + ) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/hcv.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/hcv.py new file mode 100644 index 0000000..bae9183 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/hcv.py @@ -0,0 +1,120 @@ +from typing import Dict, Any, TypedDict +import IRC_standards.IRCSP30_2019 +from voc.utils.output_builder import build_voc_output +from voc.utils.pre_processor import VehicleInput, extract_vehicle_inputs +import math + +Vehicle = "hcv" + + +def compute_voc(vehicle_input: VehicleInput) -> Dict[str, Any]: + vt, W, RG, FL, RS, lane, RF = extract_vehicle_inputs(vehicle_input) + NP: Dict[str, int] = IRC_standards.IRCSP30_2019.vehicle_costs[Vehicle] + + pwr = vehicle_input["power_weight_ratio_pwr"] + if pwr == None: + raise ValueError( + "Power to weight ratio (pwr) must be provided for HCV vehicles.") + + if vt == Vehicle: + # ----------------------------- + # SPEED FORMULA + # ----------------------------- + speed_map: Dict[str, float] = { + 'SL': 48.29 - 0.4306 * RF - 0.00086 * (RG - 2000), + 'IL': 53.12 - 0.4736 * RF - 0.00094 * (RG - 2000), + '2L': 56.52 - 0.5040 * RF - 0.00100 * (RG - 2000), + '4L': 75.15 - 0.6487 * RF - 0.001285 * RG, + '6L': 77.17 - 0.6487 * RF - 0.001285 * RG, + '8L': 79.19 - 0.6487 * RF - 0.001285 * RG, + 'EW': 71.11 - 0.6487 * RF - 0.001285 * RG + 0.577 * W + } + + V: float = speed_map.get(lane, 0.0) + + # ----------------------------- + # DISTANCE RELATED + # ----------------------------- + + # Fuel consumption + + petrol: float = 0 + diesel: float = 50 + (8049.955 / V) + 0.012 * (V ** 2) + \ + 0.005 * RG + 4.565 * RS - 4.904 * FL - 7.285 * (pwr) + + # Spare parts + SP_ET: float = (math.exp(-9.492638 + 0.0001413 * + RG + 3.493 / W)) * NP["ET"] + SP_IT: float = (math.exp(-9.492638 + 0.0001413 * + RG + 3.493 / W)) * NP["IT"] + + # Maintenance labour + ML: float = 0.7912 * SP_ET + + # Tyre life + TL: float = 24662 + 4205 * W - 413.6 * RF - 1.142 * RG + + # Engine oil + EOL: float = 1.0277 + 0.02495 * RF + 0.0001782 * (RG / W) + + # Other oil + OL: float = 5.1037 + 0.0002646 * RG + + # Grease + G: float = 0.9153 + 0.0707 * RF + 0.000627 * RG + + # ----------------------------- + # TIME RELATED + # ----------------------------- + + # Utilisation + UPD: float = 55.6719 + 4.22 * V + + # Fixed costs + FXC_ET: float = 924.28 / UPD + FXC_IT: float = 1056.82 / UPD + + # Depreciation costs + DC_ET: float = 154.84 / UPD + DC_IT: float = 256.80 / UPD + + # Passenger time cost + PT = 0 + + # Crew cost + crew: float = 1500 / UPD + + # Commodity holding cost + CHC: float = 0.0 + if lane in ['SL', 'IL']: + CHC = 182.79 / UPD + elif lane == '2L': + CHC = 218.75 / UPD + elif lane in ['4L', '6L', '8L']: + CHC = 1084.14 / UPD + elif lane == 'EW': + CHC = 1084.14 / UPD + else: + raise ValueError(f"Invalid lane type '{lane}' for HCV vehicle.") + + + # ----------------------------- + # BUILD FINAL OUTPUT + # ----------------------------- + return build_voc_output( + vt=vt, lane=lane, + velocity=V, + petrol=petrol, diesel=diesel, + SP_ET=SP_ET, SP_IT=SP_IT, + ML=ML, TL=TL, + EOL=EOL, OL=OL, G=G, + FXC_ET=FXC_ET, FXC_IT=FXC_IT, + DC_ET=DC_ET, DC_IT=DC_IT, + PT=PT, crew=crew, CHC=CHC, + UPD=UPD + ) + + else: + raise NotImplementedError( + f"VOC computation for vehicle type '{vt}' is not implemented yet." + ) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/lcv.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/lcv.py new file mode 100644 index 0000000..3cb1658 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/lcv.py @@ -0,0 +1,113 @@ +from typing import Dict, Any, TypedDict +import IRC_standards.IRCSP30_2019 +from voc.utils.output_builder import build_voc_output +from voc.utils.pre_processor import VehicleInput, extract_vehicle_inputs +import math + +Vehicle = "lcv" + + +def compute_voc(vehicle_input: VehicleInput) -> Dict[str, Any]: + vt, W, RG, FL, RS, lane, RF = extract_vehicle_inputs(vehicle_input) + NP: Dict[str, int] = IRC_standards.IRCSP30_2019.vehicle_costs[Vehicle] + + if vt == Vehicle: + # ----------------------------- + # SPEED FORMULA + # ----------------------------- + speed_map: Dict[str, float] = { + 'SL': 49.87 - 0.4447 * RF - 0.00088 * (RG - 2000), + 'IL': 53.70 - 0.4788 * RF - 0.00095 * (RG - 2000), + '2L': 57.41 - 0.5119 * RF - 0.00102 * (RG - 2000), + '4L': 74.897 - 0.163 * RF - 0.0031 * RG, + '6L': 77.036 - 0.163 * RF - 0.0031 * RG, + '8L': 79.174 - 0.163 * RF - 0.0031 * RG, + 'EW': 70.620 - 0.163 * RF - 0.0031 * RG + 0.611 * W + } + + V: float = speed_map.get(lane, 0.0) + + # ----------------------------- + # DISTANCE RELATED + # ----------------------------- + + # Fuel consumption + petrol: float = 0 + diesel: float = 22.504 + (1708.244 / V) + 0.02591 * \ + (V ** 2) + 0.001612 * RG + 5.6863 * RS - 0.8744 * FL + + # Spare parts + SP_ET: float = math.exp(-10.5615 + 0.000141 * + RG + 3.493 / W) * NP["ET"] + SP_IT: float = math.exp(-10.5615 + 0.000141 * + RG + 3.493 / W) * NP["IT"] + + # Maintenance labour + ML: float = 0.85773 * SP_ET + + # Tyre life + TL: float = 22382 + 3817 * W - 375.3 * RF - 1.037 * RG + + # Engine oil + EOL: float = 0.80679 + 0.019496 * RF + 0.0001297 * (RG / W) + + # Other oil + OL: float = 2.0415 + 0.0001058 * RG + + # Grease + G: float = 0.3661 + 0.0283 * RF + 0.000251 * RG + + # ----------------------------- + # TIME RELATED + # ----------------------------- + + # Utilisation + UPD: float = 28.807 + 2.1836 * V + + # Fixed costs + FXC_ET: float = 723.80 / UPD + FXC_IT: float = 829.56 / UPD + + # Depreciation costs + DC_ET: float = 120.90 / UPD + DC_IT: float = 173.51 / UPD + + # Passenger time cost + PT = 0 + + # Crew cost + crew: float = 900 / UPD + + # Commodity holding cost + CHC: float + if lane in ['SL', 'IL']: + CHC = 64.71 / UPD + elif lane == '2L': + CHC = 71.35 / UPD + elif lane in ['4L', '6L', '8L']: + CHC = 149.12 / UPD + elif lane == 'EW': + CHC = 149.12 / UPD + else: + raise ValueError(f"Invalid lane type: {lane}") + + # ----------------------------- + # BUILD FINAL OUTPUT + # ----------------------------- + return build_voc_output( + vt=vt, lane=lane, + velocity=V, + petrol=petrol, diesel=diesel, + SP_ET=SP_ET, SP_IT=SP_IT, + ML=ML, TL=TL, + EOL=EOL, OL=OL, G=G, + FXC_ET=FXC_ET, FXC_IT=FXC_IT, + DC_ET=DC_ET, DC_IT=DC_IT, + PT=PT, crew=crew, CHC=CHC, + UPD=UPD + ) + + else: + raise NotImplementedError( + f"VOC computation for vehicle type '{vt}' is not implemented yet." + ) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/mcv.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/mcv.py new file mode 100644 index 0000000..56975b9 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/mcv.py @@ -0,0 +1,114 @@ +from typing import Dict, Any, TypedDict +import IRC_standards.IRCSP30_2019 +from voc.utils.output_builder import build_voc_output +from voc.utils.pre_processor import VehicleInput, extract_vehicle_inputs +import math + +Vehicle = "mcv" + + +def compute_voc(vehicle_input: VehicleInput) -> Dict[str, Any]: + vt, W, RG, FL, RS, lane, RF = extract_vehicle_inputs(vehicle_input) + NP: Dict[str, int] = IRC_standards.IRCSP30_2019.vehicle_costs[Vehicle] + + pwr = vehicle_input["power_weight_ratio_pwr"] + if pwr == None: + raise ValueError( + "Power to weight ratio (pwr) must be provided for HCV vehicles.") + + if vt == Vehicle: + # ----------------------------- + # SPEED FORMULA + # ----------------------------- + speed_map: Dict[str, float] = { + 'SL': 38.27 - 0.3412 * RF - 0.00068 * (RG - 2000), + 'IL': 42.01 - 0.3753 * RF - 0.00074 * (RG - 2000), + '2L': 44.79 - 0.3994 * RF - 0.00079 * (RG - 2000), + '4L': 74.16 - 0.6405 * RF - 0.00128 * RG, + '6L': 76.60 - 0.6405 * RF - 0.00128 * RG, + '8L': 79.03 - 0.6405 * RF - 0.00128 * RG, + 'EW': 69.29 - 0.6405 * RF - 0.00128 * RG + 0.696 * W + } + + V: float = speed_map.get(lane, 0.0) + + # ----------------------------- + # DISTANCE RELATED + # ----------------------------- + + # Fuel consumption + petrol: float = 0 + diesel: float = 90 + (14489.919 / V) + 0.0216 * (V ** 2) + 0.01 * RG + 8.217 * RS - 8.8272 * FL - 13.113 * (pwr) + + # Spare parts + SP_ET: float = math.exp(-9.492638 + 0.0001413 * RG + 3.493 / W) * NP["ET"] + SP_IT: float = math.exp(-9.492638 + 0.0001413 * RG + 3.493 / W) * NP["IT"] + + # Maintenance labour + ML: float = 0.7912 * SP_ET + + # Tyre life + TL: float = 23726 + 4046 * W - 398 * RF - 1.0099 * RG + + # Engine oil + EOL: float = 1.3826 + 0.03348 * RF + 0.002319 * (RG / W) + + # Other oil + OL: float = 5.1037 + 0.0002646 * RG + + # Grease + G: float = 0.9153 + 0.0707 * RF + 0.000627 * RG + + # ----------------------------- + # TIME RELATED + # ----------------------------- + + # Utilisation + UPD: float = 77.7233 + 5.8915 * V + + # Fixed costs + FXC_ET: float = 1238.28 / UPD + FXC_IT: float = 1479.30 / UPD + + # Depreciation costs + DC_ET: float = 238.54 / UPD + DC_IT: float = 425.84 / UPD + + # Passenger time cost + PT: float = 0 + + # Crew cost + crew: float = 1800 / UPD + + # Commodity holding cost + CHC: float + if lane in ['SL', 'IL']: + CHC = 0 + raise Warning("Multi Axle Heavy Commercial Vehicles (MCVs) vehicles are not typically used in 'SL' or 'IL' lanes. Setting CHC to 0.") + elif lane == '2L': + CHC = 409.28 / UPD + elif lane in ['4L', '6L', '8L', 'EW']: + CHC = 1707.37 / UPD + else: + raise ValueError(f"Invalid lane type '{lane}' for Multi Axle Heavy Commercial Vehicles (MCVs) vehicle.") + + # ----------------------------- + # BUILD FINAL OUTPUT + # ----------------------------- + return build_voc_output( + vt=vt, lane=lane, + velocity=V, + petrol=petrol, diesel=diesel, + SP_ET=SP_ET, SP_IT=SP_IT, + ML=ML, TL=TL, + EOL=EOL, OL=OL, G=G, + FXC_ET=FXC_ET, FXC_IT=FXC_IT, + DC_ET=DC_ET, DC_IT=DC_IT, + PT=PT, crew=crew, CHC=CHC, + UPD=UPD + ) + + else: + raise NotImplementedError( + f"VOC computation for vehicle type '{vt}' is not implemented yet." + ) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/small_cars.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/small_cars.py new file mode 100644 index 0000000..889c903 --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/small_cars.py @@ -0,0 +1,89 @@ +from typing import Dict, Any, TypedDict +import IRC_standards.IRCSP30_2019 +from voc.utils.output_builder import build_voc_output +from voc.utils.pre_processor import VehicleInput, extract_vehicle_inputs + + +Vehicle = "small_cars" + +def compute_voc(vehicle_input: VehicleInput) -> Dict[str, Any]: + vt, W, RG, FL, RS, lane, RF = extract_vehicle_inputs(vehicle_input) + NP: Dict[str, Any] = IRC_standards.IRCSP30_2019.vehicle_costs[Vehicle] + + if vt == Vehicle: + # ----------------------------- + # SPEED FORMULA + # ----------------------------- + speed_map: Dict[str, float] = { + 'SL': 66.44 - 0.6922 * RF - 0.002874 * (RG - 2000), + 'IL': 73.16 - 0.7298 * RF - 0.002231 * (RG - 2000), + '2L': 81.19 - 0.7892 * RF - 0.001891 * (RG - 2000), + '4L': 100.625 - 0.394 * RF - 0.00330 * RG, + '6L': 101.065 - 0.386 * RF - 0.00323 * RG, + '8L': 103.517 - 0.386 * RF - 0.00323 * RG, + 'EW': 93.71 - 0.386 * RF - 0.00323 * RG + 0.701 * W + } + + V: float = speed_map.get(lane, 0.0) + + # ----------------------------- + # DISTANCE RELATED + # ----------------------------- + + petrol: float = 30 + (844.085 / V) + 0.003 * (V ** 2) + 0.001 * RG + 0.3414 * RS - 0.2225 * FL + diesel: float = 35 + (983.503 / V) + 0.003 * (V ** 2) + 0.002 * RG + 0.339 * RS - 0.4785 * FL + fuel: float = 0.7 * petrol + 0.3 * diesel + + SP_ET: float = (0.0075 * (RG - 2000) * (10 ** -5)) * NP["ET"] + SP_IT: float = (0.0075 * (RG - 2000) * (10 ** -5)) * NP["IT"] + + ML: float = 1.79934 * SP_ET + TL: float = 68771 - 147.9 * RF - 26.72 * (RG / W) + EOL: float = 1.8807 + 0.036615 * RF + 0.000578 * (RG / W) + OL: float = 1.631 + 0.05167 * RF + 0.001867 * (RG / W) + G: float = 2.816 + 0.2007 * RF + + # ----------------------------- + # TIME RELATED + # ----------------------------- + UPD: float = 6.7127 * V + FXC_ET: float = 395.65 / UPD + FXC_IT: float = 400.61 / UPD + DC_ET: float = 42.83 / UPD + DC_IT: float = 76.68 / UPD + + if lane in ["SL", "IL"]: + PT: float = 244.07 / V + elif lane == "2L": + PT: float = 328.06 / V + elif lane in ["4L", "6L", "8L"]: + PT: float = 498.65 / V + elif lane == 'EW': + PT: float = 721.73 / V + else: + PT: float = 0.0 + + crew: float = 0.0 + + CHC: float = 0.0 + + # ----------------------------- + # BUILD FINAL OUTPUT + # ----------------------------- + return build_voc_output( + vt=vt, lane=lane, + velocity=V, + petrol=petrol, diesel=diesel, + SP_ET=SP_ET, SP_IT=SP_IT, + ML=ML, TL=TL, + EOL=EOL, OL=OL, G=G, + FXC_ET=FXC_ET, FXC_IT=FXC_IT, + DC_ET=DC_ET, DC_IT=DC_IT, + PT=PT, crew=crew, CHC=CHC, + UPD=UPD + ) + + else: + raise NotImplementedError( + f"VOC computation for vehicle type '{vt}' is not implemented yet." + ) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/two_wheeler.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/two_wheeler.py new file mode 100644 index 0000000..0f503be --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/vehicle_types/two_wheeler.py @@ -0,0 +1,107 @@ +from typing import Dict, Any, TypedDict +import IRC_standards.IRCSP30_2019 +from voc.utils.output_builder import build_voc_output +from voc.utils.pre_processor import VehicleInput, extract_vehicle_inputs + +Vehicle = "two_wheelers" + + +def compute_voc(vehicle_input: VehicleInput) -> Dict[str, Any]: + vt, W, RG, FL, RS, lane, RF = extract_vehicle_inputs(vehicle_input) + vt: str = vehicle_input["vehicle_type"] + NP: Dict[str, Any] = IRC_standards.IRCSP30_2019.vehicle_costs[Vehicle] + + if vt == Vehicle: + # ----------------------------- + # SPEED FORMULA + # ----------------------------- + speed_map: Dict[str, float] = { + 'SL': 52.91 - 0.6922 * RF - 0.002874 * (RG - 2000), + 'IL': 58.86 - 0.7298 * RF - 0.002231 * (RG - 2000), + '2L': 59.71 - 0.7892 * RF - 0.001891 * (RG - 2000), + '4L': 78.57 - 0.7235 * RF - 0.001729 * RG, + '6L': 81.35 - 0.7235 * RF - 0.001729 * RG, + '8L': 82.73 - 0.7235 * RF - 0.001729 * RG, + 'EW': 77.19 - 0.7235 * RF - 0.001729 * RG + 0.396 * (W if W else 0) + } + + V: float = speed_map.get(lane, 0.0) + + # ----------------------------- + # DISTANCE RELATED + # ----------------------------- + + # Fuel consumption + petrol: float = 2.704 + (439.656 / V) + 0.00349 * (V ** 2) + 0.000157 * RG + 0.3642 * RS - 0.2709 * FL + diesel: float = 0 + + # Spare parts + SP_ET: float = ((-55.879 + 0.024 * RG) * (10 ** -5)) * NP["ET"] + SP_IT: float = ((-55.879 + 0.024 * RG) * (10 ** -5)) * NP["IT"] + + # Maintenance labour + ML: float = 0.5498 * SP_ET + + # Tyre life + TL: float = 47340 - 101.8 * RF - 18.39 * (RG / W) + + # Engine oil + EOL: float = 0.405 + 0.007899 * RF + 0.000125 * (RG / W) + + # Other oil + OL: float = 0.0 + + # Grease + G: float = 0.0 + + # ----------------------------- + # TIME RELATED + # ----------------------------- + + # Utilisation + UPD: float = 2.119 * V + + # Fixed costs + FXC_ET: float = 24.32 / UPD + FXC_IT: float = 24.86 / UPD + + # Depreciation costs + DC_ET: float = 4.26 / UPD + DC_IT: float = 5.85 / UPD + + # Passenger time cost + if lane in ['SL', 'IL']: + PT = 49.28 / V + elif lane == '2L': + PT = 70.29 / V + elif lane in ['4L', '6L', '8L', 'EW']: + PT = 70.77 / V + else: + PT = 0 + + # Crew cost + crew: float = 0.0 + + # Commodity holding cost + CHC: float = 0.0 + + # ----------------------------- + # BUILD FINAL OUTPUT + # ----------------------------- + return build_voc_output( + vt=vt, lane=lane, + velocity=V, + petrol=petrol, diesel=diesel, + SP_ET=SP_ET, SP_IT=SP_IT, + ML=ML, TL=TL, + EOL=EOL, OL=OL, G=G, + FXC_ET=FXC_ET, FXC_IT=FXC_IT, + DC_ET=DC_ET, DC_IT=DC_IT, + PT=PT, crew=crew, CHC=CHC, + UPD=UPD + ) + + else: + raise NotImplementedError( + f"VOC computation for vehicle type '{vt}' is not implemented yet." + ) diff --git a/src/osbridgelcca/desktop_app/widgets/utils/value_of_travel_time.sql b/src/osbridgelcca/desktop_app/widgets/utils/value_of_travel_time.sql new file mode 100644 index 0000000..4dfebbc --- /dev/null +++ b/src/osbridgelcca/desktop_app/widgets/utils/value_of_travel_time.sql @@ -0,0 +1,143 @@ + + +-- 2) Property damage (manufacture of parts and accessories for motor vehicles) +CREATE TABLE IF NOT EXISTS property_damage_index ( + year INTEGER PRIMARY KEY, + small_car REAL, + big_car REAL, + two_wheeler REAL, + ordinary_bus REAL, + deluxe_bus REAL, + lcv REAL, + hcv REAL, + mcv REAL +); + +INSERT INTO property_damage_index VALUES +(2019, 113.2,113.2,113.2,113.2,113.2,113.2,113.2,113.2), +(2020, 115.5,115.5,115.5,115.5,115.5,115.5,115.5,115.5), +(2021, 120.6,120.6,120.6,120.6,120.6,120.6,120.6,120.6), +(2022, 128.5,128.5,128.5,128.5,128.5,128.5,128.5,128.5), +(2023, 128.2,128.2,128.2,128.2,128.2,128.2,128.2,128.2), +(2024, 129.0,129.0,129.0,129.0,129.0,129.0,129.0,129.0); + + + +-- 4) Fuel, Engine oil, Other oil, Grease cost indices (note: values are indices; original units in source: petrol/diesel/engine oil are per-litre and grease per-kg) +CREATE TABLE IF NOT EXISTS fuel_and_oil_index ( + year INTEGER PRIMARY KEY, + petrol REAL, -- per-litre index + diesel REAL, -- per-litre index + engine_oil REAL, -- per-litre index + other_oil REAL, -- per-litre index + grease REAL -- per-kg index +); + +INSERT INTO fuel_and_oil_index VALUES +(2019, 85.4, 94.4, 131.2, 92.5, 92.5), +(2020, 74.2, 79.4, 134.0, 78.1, 78.1), +(2021, 109.9, 114.7, 157.8, 113.8, 113.8), +(2022, 159.8, 183.5, 174.7, 168.2, 168.2), +(2023, 158.9, 174.2, 188.0, 160.1, 160.1), +(2024, 154.3, 167.4, 190.2, 156.8, 156.8); + +-- 5) Tyre cost index (tyre cost for each vehicle type) +CREATE TABLE IF NOT EXISTS tyre_cost_index ( + year INTEGER PRIMARY KEY, + small_car REAL, + big_car REAL, + two_wheeler REAL, + ordinary_bus REAL, + deluxe_bus REAL, + lcv REAL, + hcv REAL, + mcv REAL +); + +INSERT INTO tyre_cost_index VALUES +(2019, 99.2, 99.2, 104.0, 97.5, 97.5, 97.5, 97.5, 97.5), +(2020, 98.7, 98.7, 102.0, 96.1, 96.1, 96.1, 96.1, 96.1), +(2021, 102.8,102.8,105.9,103.2,103.2,103.2,103.2,103.2), +(2022, 109.9,109.9,116.4,110.5,110.5,110.5,110.5,110.5), +(2023, 111.5,111.5,119.8,114.4,114.4,114.4,114.4,114.4), +(2024, 111.5,111.5,117.9,114.1,114.1,114.1,114.1,114.1); + +-- 6) Spare part new price index (manufacture of parts and accessories for motor vehicles) +CREATE TABLE IF NOT EXISTS spare_part_index ( + year INTEGER PRIMARY KEY, + small_car REAL, + big_car REAL, + two_wheeler REAL, + ordinary_bus REAL, + deluxe_bus REAL, + lcv REAL, + hcv REAL, + mcv REAL +); + +INSERT INTO spare_part_index VALUES +(2019,113.2,113.2,113.2,113.2,113.2,113.2,113.2,113.2), +(2020,115.5,115.5,115.5,115.5,115.5,115.5,115.5,115.5), +(2021,120.6,120.6,120.6,120.6,120.6,120.6,120.6,120.6), +(2022,128.5,128.5,128.5,128.5,128.5,128.5,128.5,128.5), +(2023,128.2,128.2,128.2,128.2,128.2,128.2,128.2,128.2), +(2024,129.0,129.0,129.0,129.0,129.0,129.0,129.0,129.0); + +-- 7) Fixed and depreciation cost index (manufacture of motor vehicles, trailers and semi-trailers) +CREATE TABLE IF NOT EXISTS fixed_depreciation_index ( + year INTEGER PRIMARY KEY, + small_car REAL, + big_car REAL, + two_wheeler REAL, + ordinary_bus REAL, + deluxe_bus REAL, + lcv REAL, + hcv REAL, + mcv REAL +); + +INSERT INTO fixed_depreciation_index VALUES +(2019,113.8,113.8,113.8,113.8,113.8,113.8,113.8,113.8), +(2020,116.9,116.9,116.9,116.9,116.9,116.9,116.9,116.9), +(2021,121.1,121.1,121.1,121.1,121.1,121.1,121.1,121.1), +(2022,127.1,127.1,127.1,127.1,127.1,127.1,127.1,127.1), +(2023,128.0,128.0,128.0,128.0,128.0,128.0,128.0,128.0), +(2024,129.6,129.6,129.6,129.6,129.6,129.6,129.6,129.6); + +-- 8) Commodity holding cost (FUEL & POWER) - same values across vehicle types in the source +CREATE TABLE IF NOT EXISTS commodity_holding_cost_index ( + year INTEGER PRIMARY KEY, + small_car REAL, + big_car REAL, + two_wheeler REAL, + ordinary_bus REAL, + deluxe_bus REAL, + lcv REAL, + hcv REAL, + mcv REAL +); + +INSERT INTO commodity_holding_cost_index VALUES +(2019,101.7,101.7,101.7,101.7,101.7,101.7,101.7,101.7), +(2020,93.3,93.3,93.3,93.3,93.3,93.3,93.3,93.3), +(2021,116.1,116.1,116.1,116.1,116.1,116.1,116.1,116.1), +(2022,155.2,155.2,155.2,155.2,155.2,155.2,155.2,155.2), +(2023,152.7,152.7,152.7,152.7,152.7,152.7,152.7,152.7), +(2024,150.4,150.4,150.4,150.4,150.4,150.4,150.4,150.4); + +-- 9) Passenger and Crew cost (years with passenger and crew cost indices) +CREATE TABLE IF NOT EXISTS passenger_crew_cost_index ( + year INTEGER PRIMARY KEY, + passenger_cost REAL, + crew_cost REAL +); + +INSERT INTO passenger_crew_cost_index VALUES +(2019, 138.58, 118.07), +(2020, 147.91, 131.77), +(2021, 155.33, 145.91), +(2022, 166.94, 159.05), +(2023, 176.38, 160.28), +(2024, 184.27, 164.18); + + From 9f23259180d469c1c3a434bf1b87a2e1b09fed98 Mon Sep 17 00:00:00 2001 From: mhsuhail00 Date: Sat, 13 Dec 2025 05:10:12 +0530 Subject: [PATCH 22/22] Restructured VOT calculations --- src/osbridgelcca/desktop_app/.gitignore | 2 +- .../desktop_app/widgets/utils/IRC_SP_30.py | 3 +- .../desktop_app/widgets/utils/core/main.py | 15 ++++++---- .../widgets/utils/core/voc/core.py | 6 ++-- .../utils/core/voc/utils/input_validation.py | 4 +-- .../desktop_app/widgets/utils/database.py | 30 +++++++++++++++++++ 6 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/osbridgelcca/desktop_app/.gitignore b/src/osbridgelcca/desktop_app/.gitignore index 9006474..a99dd99 100644 --- a/src/osbridgelcca/desktop_app/.gitignore +++ b/src/osbridgelcca/desktop_app/.gitignore @@ -1,5 +1,5 @@ # Python bytecode and cache -__pycache__/ +**__pycache__/ *.py[cod] *$py.class diff --git a/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py b/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py index a1df10d..d22f1f7 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/IRC_SP_30.py @@ -1,6 +1,5 @@ import pandas as pd -from data import * -import json +from .data import * class IRC_SP_30: def __init__(self): diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/main.py b/src/osbridgelcca/desktop_app/widgets/utils/core/main.py index d35e71d..abadab0 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/core/main.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/main.py @@ -1,5 +1,5 @@ -from voc import core -import voc.congestion.core as co +from .voc import core +from .voc.congestion import core as congestion_core vc = 0.8854 wpi = {'year': 2024, @@ -106,6 +106,11 @@ } } -a = core.main(vehicle_input, wpi) -calculate_total_adjusted_costs = co.calculate_total_adjusted_costs(a, vc, vehicle_input) -print(calculate_total_adjusted_costs) +def calc_voc(inputs: dict, + all_wpi: dict, + vehicle_cost: float) -> dict: + """ + return calculate_total_adjusted_costs + """ + val = core.main(inputs, all_wpi) + return congestion_core.calculate_total_adjusted_costs(val, vehicle_cost, vehicle_input) \ No newline at end of file diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/core.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/core.py index 2ac8edc..0ce635d 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/core.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/core.py @@ -1,6 +1,6 @@ -from voc.utils.input_validation import validate_input -from voc.vehicle_types import big_cars, buses, hcv, lcv, mcv, small_cars, two_wheeler -import voc.utils.post_processor as pp +from .utils.input_validation import validate_input +from .vehicle_types import big_cars, buses, hcv, lcv, mcv, small_cars, two_wheeler +from .utils import post_processor as pp import sys # Map vehicle_info keys to their model modules diff --git a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/input_validation.py b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/input_validation.py index 5bb0ca5..90a0842 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/input_validation.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/core/voc/utils/input_validation.py @@ -1,5 +1,5 @@ -from voc.utils import carriage_way_standards -import voc.utils.constants as constants +from utils import carriage_way_standards +from utils import constants def validate_input(vehicle_input): errors = [] diff --git a/src/osbridgelcca/desktop_app/widgets/utils/database.py b/src/osbridgelcca/desktop_app/widgets/utils/database.py index 69e01e1..faa6d52 100644 --- a/src/osbridgelcca/desktop_app/widgets/utils/database.py +++ b/src/osbridgelcca/desktop_app/widgets/utils/database.py @@ -11,6 +11,8 @@ from .data import * from .IRC_SP_30 import IRC_SP_30 +# from .core.main import calc_voc + class DatabaseManager: """Database manager for Structure Works Data""" @@ -1037,6 +1039,34 @@ def vot_per_year(self) -> float: #==========3. VOT-End============================ + def total_road_user_cost(self) -> float: + vot = self.vot_per_year() + accident_cost = self.accident_related_cost() + + + ui_inputs = { + "vehicle_info": self.daily_average_traffic_data, + # "carriageway_width": 10, ### ONLY REQUIRED WHEN "lane_type" = "EW" + "rg_roughness_factor": 2000, + "fl_fall_factor": 0, + "rs_rise_factor": 0, + "lane_type": "2L", + "power_weight_ratio_pwr": { + "mcv": 8, + "hcv": 7.22 + } + } + + + + voc = calc_voc( + inputs=ui_inputs, + all_wpi=self.irc_sp_30.getWPI(), + vehicle_cost=23 + ) + + + #==========IRC-Road_User-Cost-End====================

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 literal 0 HcmV?d00001 diff --git a/__pycache__/main_template.cpython-312.pyc b/__pycache__/main_template.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f00829eade4dd80e6142641d0861f109775d126b GIT binary patch literal 19956 zcmeG^ZEO@tc0I-)ZM$ucKNf#r%=imzj0bEe34B|)BvR@xjYxhe zQsys5sg75<8d+wYE4`YR(<{;Ei|zdBOmuZg_jemv33dcq?= z+z{E}-{BJSgtr8_;6p(!R2m;{GiCGdTrBDopJSmj-zb=v3?&pJZ$dMwCeEv|c}3M0 zN1~xfPz%MPhBR?J7!O5f#un5Y3ZNHFj7*b?f{7EcsAiOrWIU(^r-E?>xSg7gMLR2i zZe)Hw9GW({lun!+jomsET#PMfM)AZ&<(76_4rxZ|#N@OZ3x`KkC5VOj;IyKi(v%1m zK&9PvnR6*t^f+F}`vf$5? zT^|bmZHsxG+YISa=$&9F`feyH$8JvNIf#oOk6QocX@emA5l}C=<|sc0+CwWJuY`Kb zN{(t6Lk}$~m!Q!RCd?H&am>7&M=_W4mh$9n@4qPss?R|qX45R$G;)4DR4;GSN-5UT zHo4%T)uZKnt&GCw%4v?JOZl{Imk=%0Drt>e$l%bwY6@qmT$F+F2CJbtZ9C1))zKVF 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 literal 0 HcmV?d00001 diff --git a/__pycache__/title_bar.cpython-312.pyc b/__pycache__/title_bar.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6426f355b5566c8c51e75c53ff4b1fbe3af0ed51 GIT binary patch literal 8565 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 BrlSA= literal 0 HcmV?d00001 diff --git a/app.py b/app.py new file mode 100644 index 0000000..3a3fa13 --- /dev/null +++ b/app.py @@ -0,0 +1,21 @@ +from PySide6.QtWidgets import QApplication, QMainWindow +from PySide6.QtCore import QCoreApplication, Qt +from main_template import UiMainWindow +import sys + +class MyMainWindow(QMainWindow): + """ + The main application window that uses a custom title bar. + """ + def __init__(self): + super().__init__() + self.ui = UiMainWindow() + self.ui.setupUi(self) + +if __name__ == "__main__": + # Enable mnemonics (underlined shortcuts) for menu items + QCoreApplication.setAttribute(Qt.AA_DontShowIconsInMenus, False) + app = QApplication(sys.argv) + window = MyMainWindow() + window.show() + sys.exit(app.exec()) \ No newline at end of file diff --git a/main_template.py b/main_template.py new file mode 100644 index 0000000..4c794c2 --- /dev/null +++ b/main_template.py @@ -0,0 +1,386 @@ +from PySide6.QtCore import (QSize, Qt, QPropertyAnimation, QEasingCurve) +from PySide6.QtGui import (QAction,QFont, QFontDatabase, QIcon) +from PySide6.QtWidgets import (QApplication, QHBoxLayout, QTextEdit, QScrollArea, QSpacerItem, QSizePolicy, + QMenu, QMenuBar, QPushButton, QWidget, QLabel, QVBoxLayout, QGridLayout, QLineEdit, QComboBox) + +from widgets.title_bar import CustomTitleBar + +class UiMainWindow(object): + def setupUi(self, MainWindow): + if not MainWindow.objectName(): + MainWindow.setObjectName(u"MainWindow") + + # Load and set the Alata Regular font + font_id = QFontDatabase.addApplicationFont("resources/AlataRegular.ttf") + if font_id != -1: + font_family = QFontDatabase.applicationFontFamilies(font_id)[0] + app_font = QFont(font_family, 10) + QApplication.setFont(app_font) + else: + print("Failed to load Alata font") + + MainWindow.setWindowTitle("Custom Title Bar App") + MainWindow.setWindowFlags(Qt.WindowType.FramelessWindowHint) + screen = QApplication.primaryScreen().geometry() + x = (screen.width()*1//8) + y = (screen.height()*1//8) + MainWindow.setGeometry(x, y, screen.width()*3//4, screen.height()*3//4) + + # Set window stylesheet + MainWindow.setStyleSheet(""" + QMainWindow { + border: 1px solid #285A23; + } + QMenuBar { + background-color: #FAFAFA; + border-bottom: 1px solid #d0d0d0; + border-left: 1px solid #285A23; + border-right: 1px solid #285A23; + } + QMenuBar::item { + padding: 4px 10px; + background-color: transparent; + border-bottom: 2px solid #FAFAFA; + margin: 2px; + } + QMenuBar::item:selected { + border-bottom: 2px solid #806C6C; + } + QMenu { + background-color: #f0f0f0; + border: 1px solid #d0d0d0; + } + QMenu::item { + padding: 4px 4px 4px 12px; + text-align: left; + color: #514E4E; + } + QMenu::item:selected { + background-color: #e0e0e0; + } + QMenu::separator { + height: 1px; + background-color: #d0d0d0; + margin: 4px 0px; + } + QMenu::icon { + padding-left: 5px; + width: 16px; + height: 16px; + } + QMenu::indicator { + width: 16px; + height: 16px; + } + QMenu::right-arrow { + margin-right: 5px; + } + """) + + # Create a central widget and main layout for the window + self.central_widget = QWidget() + MainWindow.setCentralWidget(self.central_widget) + main_layout = QVBoxLayout(self.central_widget) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + + # Add the custom title bar to the top of the main layout + self.title_bar = CustomTitleBar(MainWindow) + main_layout.addWidget(self.title_bar) + + # Create and add menu bar directly to the central widget's main_layout + # DO NOT use MainWindow.setMenuBar() when using FramelessWindowHint + self.menubar = QMenuBar() + self.menubar.setObjectName(u"menubar") + main_layout.addWidget(self.menubar) # This is the crucial line for custom frame + + # Create menus + self.menuFile = QMenu("&File", self.menubar) + self.menuHome = QMenu("&Home", self.menubar) + self.menuReport = QMenu("&Report", self.menubar) + self.menuHelp = QMenu("&Help", self.menubar) + + # Add menus to menubar + self.menubar.addMenu(self.menuFile) + self.menubar.addMenu(self.menuHome) + self.menubar.addMenu(self.menuReport) + self.menubar.addMenu(self.menuHelp) + + # Create and add actions to File menu with icons + self.actionNew = QAction(QIcon("resources/new.svg"), "New", MainWindow) + self.actionOpen = QAction(QIcon("resources/open.svg"), "Open", MainWindow) + self.actionSave = QAction(QIcon("resources/save.svg"), "Save", MainWindow) + self.actionSaveAs = QAction(QIcon("resources/save_as.svg"), "Save As...", MainWindow) + self.actionCreateCopy = QAction(QIcon("resources/create_copy.svg"), "Create a Copy", MainWindow) + self.actionPrint = QAction(QIcon("resources/print.svg"), "Print", MainWindow) + self.actionRename = QAction(QIcon("resources/rename.svg"), "Rename", MainWindow) + self.actionExport = QAction(QIcon("resources/export.svg"), "Export", MainWindow) + self.actionVersionHistory = QAction(QIcon("resources/version_history.svg"), "Version History", MainWindow) + self.actionInfo = QAction(QIcon("resources/info.svg"), "Info", MainWindow) + + # Add actions to File menu + self.menuFile.addAction(self.actionNew) + self.menuFile.addAction(self.actionOpen) + self.menuFile.addSeparator() + self.menuFile.addAction(self.actionSave) + self.menuFile.addAction(self.actionSaveAs) + self.menuFile.addAction(self.actionCreateCopy) + self.menuFile.addAction(self.actionPrint) + self.menuFile.addSeparator() + self.menuFile.addAction(self.actionRename) + self.menuFile.addAction(self.actionExport) + self.menuFile.addAction(self.actionVersionHistory) + self.menuFile.addAction(self.actionInfo) + + # Create and add actions to Help menu with icons + self.actionDocumentation = QAction(QIcon("resources/contact.svg"), "Contact us", MainWindow) + self.actionFeedback = QAction(QIcon("resources/feedback.svg"), "Feedback", MainWindow) + self.actionVideoTutorial = QAction(QIcon("resources/video_tutorial.svg"), "Video Tutorials", MainWindow) + self.actionJoinCommunity = QAction(QIcon("resources/join_community.svg"), "Join our Community", MainWindow) + + # Add actions to Help menu + self.menuHelp.addAction(self.actionDocumentation) + self.menuHelp.addAction(self.actionFeedback) + self.menuHelp.addSeparator() + + self.menuHelp.addAction(self.actionVideoTutorial) + self.menuHelp.addAction(self.actionJoinCommunity) + + # Main content area below the menubar + self.main_content_area = QWidget() + # This widget needs to stretch to fill the remaining space + main_layout.addWidget(self.main_content_area, 1) # Add stretch factor of 1 + self.main_content_area.setObjectName("main_content_area") + self.main_content_area.setStyleSheet(""" + #main_content_area { + background-color: #FAFAFA; + border-left: 1px solid #285A23; + border-right: 1px solid #285A23; + border-bottom: 1px solid #285A23; + } + QLabel { + color: #9F8888; + font-size: 14px; + border: none; + padding: 5px; + } + QPushButton { + background-color: #EDEDED; + border: 1px solid #d0d0d0; + padding: 6px 16px; + color: #514E4E; + } + QPushButton:hover { + background: qlineargradient( + x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #BBBBBB, + stop: 0.26 #E8E8E8, + stop: 1 #EDEDED + ); + border-color: #806C6C; + } + QPushButton:pressed { + background-color: #d0d0d0; + } + """) + + # Main vertical layout for content inside main_content_area + content_layout = QVBoxLayout(self.main_content_area) + content_layout.setContentsMargins(0,0,0,0) + content_layout.setSpacing(0) + + # Create horizontal layout for buttons + button_layout = QHBoxLayout() + button_layout.setSpacing(0) # Add spacing between all buttons + button_layout.setContentsMargins(5, 5, 5, 5) # Add some margin around the layout + + # ------------------------------------------------------------ + + # Add buttons to left container + self.edit_button = QPushButton() + self.edit_button.setObjectName(u"edit_button") + self.edit_button.setIcon(QIcon("resources/edit_button.png")) + self.edit_button.setFixedSize(60, 30) + self.edit_button.setIconSize(QSize(25, 25)) + self.edit_button.setStyleSheet(""" + QPushButton { + border-radius: 5px; + background-color: #EDEDED; + border: 1px solid #BBBBBB; + margin-right: 20px; + margin-left: 10px; + } + QPushButton:hover { + background-color: #FAFAFA; + border: 1px solid #888888; + } + """) + button_layout.addWidget(self.edit_button) + + self.file_button= QPushButton() + self.file_button.setObjectName(u"file_button") + self.file_button.setFixedSize(50, 30) + self.file_button.setIcon(QIcon("resources/file_button.png")) + self.file_button.setIconSize(QSize(30, 30)) + self.file_button.setStyleSheet(""" + QPushButton { + border-radius: 5px; + background-color: #EDEDED; + border: 1px solid #BBBBBB; + margin-right: 20px; + } + QPushButton:hover { + border: 1px solid #888888; + background-color: #FAFAFA; + } + """) + button_layout.addWidget(self.file_button) + + # Create save menu button + self.save_button = QPushButton() + self.save_button.setObjectName(u"save_button") + self.save_button.setFixedSize(40, 30) + self.save_button.setIcon(QIcon("resources/save_button.png")) + self.save_button.setIconSize(QSize(25, 25)) + self.save_button.setStyleSheet(""" + QPushButton { + border-radius: 5px; + background-color: #EDEDED; + border: 1px solid #BBBBBB; + } + QPushButton:hover { + background-color: #FAFAFA; + border: 1px solid #888888; + } + QPushButton::menu-indicator { + image: url(resources/arrow_down.png); + width: 8px; + height: 30px; + subcontrol-position: right center; + subcontrol-origin: padding; + margin-right: 2px; + margin-left: 2px; + border-left: 1px solid #BBBBBB; + } + """) + + # Create save menu + self.save_menu = QMenu(self.save_button) + self.save_menu.setStyleSheet(""" + QMenu { + background-color: #EDEDED; + border: 1px solid #BBBBBB; + padding: 2px; + color: #9F8888; + } + + QMenu::item { + padding: 2px 2px; + margin: 0px; + border: none; + min-height: 18px; + font-size: 11px; + } + + QMenu::item:selected { + background-color: #FAFAFA; + } + + QMenu::icon { + width: 0px; + } + """) + self.save_action = QAction("Save", self.save_menu) + self.save_as_action = QAction("Save As", self.save_menu) + self.save_menu.addAction(self.save_action) + self.save_menu.addAction(self.save_as_action) + self.save_button.setMenu(self.save_menu) + + button_layout.addWidget(self.save_button) + + # ------------------------------------------------------------ + + button_layout.addStretch() + + # Add label + self.windows = QLabel("Windows:") + button_layout.addWidget(self.windows) + + # add buttons to the right + self.tutorial_tab = QPushButton("Tutorials") + self.project_details_tab = QPushButton("Project Details") + self.results_tab = QPushButton("Results") + self.compare = QPushButton("Compare") + + # Add buttons to layout + button_layout.addWidget(self.tutorial_tab) + button_layout.addWidget(self.project_details_tab) + button_layout.addWidget(self.results_tab) + button_layout.addWidget(self.compare) + + button_layout.addStretch() + + # Add the button layout to the main content layout + content_layout.addLayout(button_layout) + + # ------------------------------------------------------------ + body_widget = QWidget() + body_widget.setObjectName("body_widget") + body_widget.setStyleSheet(""" + #body_widget { + border-top: 1px solid #d0d0d0; + } + """) + # ------------------------------------------------------------ + body_layout = QHBoxLayout(body_widget) + body_layout.setSpacing(40) + + # Placeholders for dynamic widgets + self.left_panel_placeholder = QWidget() + self.left_panel_placeholder.setLayout(QVBoxLayout()) + self.right_panel_placeholder = QWidget() + self.right_panel_placeholder.setLayout(QVBoxLayout()) + body_layout.addWidget(self.left_panel_placeholder, 1) + body_layout.addWidget(self.right_panel_placeholder, 4) + content_layout.addWidget(body_widget) + + # Store references to current widgets + self.current_left_widget = None + self.current_right_widget = None + + # Button click handlers + def show_tutorial_widget(): + if self.current_left_widget: + self.left_panel_placeholder.layout().removeWidget(self.current_left_widget) + self.current_left_widget.setParent(None) + self.current_left_widget = TutorialWidget() + self.left_panel_placeholder.layout().addWidget(self.current_left_widget) + self.current_left_widget.closed.connect(lambda: self.remove_left_widget()) + + def show_project_details_widget(): + if self.current_right_widget: + self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) + self.current_right_widget.setParent(None) + self.current_right_widget = ProjectDetailsWidget() + self.right_panel_placeholder.layout().addWidget(self.current_right_widget) + self.current_right_widget.closed.connect(lambda: self.remove_right_widget()) + + def remove_right_widget(): + if self.current_right_widget: + self.right_panel_placeholder.layout().removeWidget(self.current_right_widget) + self.current_right_widget.setParent(None) + self.current_right_widget = None + self.remove_right_widget = remove_right_widget + + def remove_left_widget(): + if self.current_left_widget: + self.left_panel_placeholder.layout().removeWidget(self.current_left_widget) + self.current_left_widget.setParent(None) + self.current_left_widget = None + self.remove_left_widget = remove_left_widget + + self.tutorial_tab.clicked.connect(show_tutorial_widget) + self.project_details_tab.clicked.connect(show_project_details_widget) + + # Remove expand_general_area and input_button_toggle methods from UiMainWindow + \ No newline at end of file diff --git a/resources/AlataRegular.ttf b/resources/AlataRegular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8533e4deb1bea99927c2b98564d70a7fe5fee1e4 GIT binary patch literal 348308 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