buckpy-dev 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
buckpy/buckpy.py ADDED
@@ -0,0 +1,132 @@
1
+ """
2
+ This is the main module of BuckPy.
3
+ """
4
+ from . import buckpy_gui
5
+ from . import buckpy_preprocessing_legacy
6
+ from . import buckpy_preprocessing_current
7
+ from . import buckpy_solver
8
+ from . import buckpy_postprocessing
9
+ from . import buckpy_visualisation
10
+
11
+ def main() -> None:
12
+ """
13
+ Run the full BuckPy workflow from GUI-provided inputs.
14
+
15
+ The function opens the BuckPy GUI, reads user selections, validates required
16
+ inputs, parses scenario identifiers, and executes the processing pipeline for
17
+ each scenario.
18
+
19
+ Returns
20
+ -------
21
+ None
22
+
23
+ Notes
24
+ -----
25
+ Workflow:
26
+ 1. Launch GUI and collect configuration.
27
+ 2. Validate working directory and input file selection.
28
+ 3. Parse scenario IDs from comma-separated text.
29
+ 4. Run legacy or current preprocessing depending on Excel format.
30
+ 5. Run the solver.
31
+ 6. Run postprocessing and visualization.
32
+
33
+ Side Effects:
34
+ 1. Starts a GUI event loop.
35
+ 2. Reads input files from the selected working directory.
36
+ 3. Writes output artifacts through postprocessing and visualization modules.
37
+ 4. Prints progress messages to stdout.
38
+ """
39
+
40
+ # Load user inputs from the GUI interface
41
+ gui = buckpy_gui.GUI()
42
+ gui.root.mainloop()
43
+
44
+ # Retrieve user selections from the GUI
45
+ user_config = {
46
+ "excel_format": gui.excel_format,
47
+ "work_dir": gui.work_dir,
48
+ "input_file_name": gui.input_file_name,
49
+ "pipeline_id": gui.pipeline_id,
50
+ "scenario_id": gui.scenario_id,
51
+ "bl_verbose": gui.bl_verbose,
52
+ "output_combination": gui.output_combination,
53
+ }
54
+
55
+ # Check if the user selected a file and provided required info
56
+ if not user_config["work_dir"] or not user_config["input_file_name"]:
57
+ print('No input file selected. Exiting.')
58
+ return
59
+
60
+ # Parse scenario IDs as a list of integers
61
+ scenario_list_id = [
62
+ int(s.strip()) for s in user_config["scenario_id"].split(',') if s.strip().isdigit()
63
+ ]
64
+
65
+ # Print start message
66
+ print('====================== Start Processing ========================')
67
+
68
+ for scenario_id in scenario_list_id:
69
+
70
+ # Print current scenario being processed
71
+ print(f'== Processing Pipeline: {user_config["pipeline_id"]}, Scenario ID: {scenario_id}')
72
+
73
+ # Load and preprocess scenario data from the input Excel file
74
+ if user_config["excel_format"] == "Legacy":
75
+ np_distr, np_scen, np_ends, df_scen, df_route, df_pp = buckpy_preprocessing_legacy.run(
76
+ user_config["work_dir"],
77
+ user_config["input_file_name"],
78
+ user_config["pipeline_id"],
79
+ scenario_id,
80
+ user_config["bl_verbose"]
81
+ )
82
+ else:
83
+ preprocessor = buckpy_preprocessing_current.PreProcessor(
84
+ user_config["work_dir"],
85
+ user_config["input_file_name"],
86
+ user_config["pipeline_id"],
87
+ scenario_id,
88
+ user_config["bl_verbose"]
89
+ )
90
+ np_scen, np_distr, np_ends, df_scen, df_route, df_pp = preprocessor.run()
91
+
92
+ # Run BuckPy solver for deterministic and Monte Carlo simulations
93
+ df_pp_plot, df_vap_plot, df_pp_buckle_prop = buckpy_solver.exec_buckpy(
94
+ np_scen,
95
+ np_distr,
96
+ np_ends,
97
+ df_scen["Simulations"].values[0],
98
+ df_scen["Friction Sampling"].values[0],
99
+ user_config["bl_verbose"]
100
+ )
101
+
102
+ # Post-process simulation results and generate summary outputs
103
+ buckpy_postprocessing.pp_buckpy(
104
+ user_config["work_dir"],
105
+ user_config["input_file_name"],
106
+ df_scen,
107
+ df_route,
108
+ df_pp,
109
+ df_pp_plot,
110
+ df_vap_plot,
111
+ df_pp_buckle_prop,
112
+ user_config["output_combination"],
113
+ user_config["bl_verbose"],
114
+ user_config["excel_format"]
115
+ )
116
+
117
+ # Generate additional plots and visualizations for BuckPy results
118
+ buckpy_visualisation.plot_buckpy(
119
+ user_config["work_dir"],
120
+ user_config["input_file_name"],
121
+ df_scen,
122
+ df_route,
123
+ df_pp,
124
+ user_config["bl_verbose"]
125
+ )
126
+
127
+ # Print end message
128
+ print('======================= End Processing =========================')
129
+
130
+ if __name__ == '__main__':
131
+ # Run the main function to execute the BuckPy workflow
132
+ main()
buckpy/buckpy_gui.py ADDED
@@ -0,0 +1,359 @@
1
+ '''
2
+ This module contains the GUI of BuckPy.
3
+ '''
4
+
5
+ import os
6
+ import tkinter as tk
7
+ from tkinter import ttk
8
+ from tkinter import font
9
+ from tkinter import filedialog
10
+ import pandas as pd
11
+ from importlib.resources import files, as_file
12
+ from pathlib import Path
13
+
14
+ class GUI:
15
+ '''
16
+ BuckPy Graphical User Interface (GUI) class.
17
+
18
+ This class creates and manages the main window for user interaction,
19
+ allowing users to select input files, enter pipeline and scenario IDs,
20
+ choose options, and view scenario data in a table. It handles all
21
+ user input validation and provides a structured interface for running
22
+ BuckPy workflows.
23
+ '''
24
+
25
+ def __init__(self):
26
+ '''
27
+ Initialize the BuckPy GUI.
28
+
29
+ Sets up the main window, configures fonts and geometry, creates
30
+ top and bottom frames, and initializes all widgets for user input.
31
+ '''
32
+ # Store the root window
33
+ self.root = tk.Tk()
34
+
35
+ # Set window title
36
+ self.root.title('BuckPy')
37
+
38
+ # Set window icon
39
+ try:
40
+ logo_res = files("buckpy").joinpath("_static", "logo.png")
41
+ with as_file(logo_res) as logo_path:
42
+ self.root.iconphoto(False, tk.PhotoImage(file=str(logo_path)))
43
+ except (ModuleNotFoundError, FileNotFoundError, OSError, PermissionError, tk.TclError):
44
+ try:
45
+ fallback = Path(__file__).with_name("_static") / "logo.png"
46
+ if fallback.exists():
47
+ self.root.iconphoto(False, tk.PhotoImage(file=str(fallback)))
48
+ except (OSError, PermissionError, tk.TclError):
49
+ pass
50
+
51
+ # Update geometry info
52
+ self.root.update_idletasks()
53
+ window_width = 1366
54
+ window_height = 768
55
+ screen_width = self.root.winfo_screenwidth()
56
+ screen_height = self.root.winfo_screenheight()
57
+ x = (screen_width // 2) - (window_width // 2)
58
+ y = (screen_height // 2) - (window_height // 2)
59
+ self.root.geometry(f'{window_width}x{window_height}+{x}+{y}')
60
+
61
+ # Set modern default font
62
+ default_font = font.nametofont('TkDefaultFont')
63
+ default_font.configure(family='Segoe UI', size=11)
64
+ self.root.option_add('*Font', default_font)
65
+
66
+ # Create top and bottom frames for layout
67
+ self.top_frame = tk.Frame(self.root, padx=20, pady=20)
68
+ self.top_frame.grid(row=0, column=0, sticky='ew')
69
+ self.bottom_frame = tk.Frame(self.root, padx=20)
70
+ self.bottom_frame.grid(row=1, column=0, sticky='nsew')
71
+
72
+ # Add a bottom padding frame for spacing
73
+ self.bottom_padding = tk.Frame(self.root, height=20)
74
+ self.bottom_padding.grid(row=2, column=0, sticky='ew')
75
+
76
+ # Configure grid weights for resizing behavior
77
+ self.root.grid_columnconfigure(0, weight=1)
78
+ self.root.grid_rowconfigure(1, weight=1)
79
+ self.root.grid_rowconfigure(2, weight=0)
80
+
81
+ # Set minimum sizes and weights for columns in the top frame
82
+ self.top_frame.grid_columnconfigure(1, minsize=200)
83
+ self.top_frame.grid_columnconfigure(3, minsize=100)
84
+ self.top_frame.grid_columnconfigure(5, minsize=200, weight=1)
85
+
86
+ # Make the Treeview expand in the bottom frame
87
+ self.bottom_frame.grid_rowconfigure(0, weight=1)
88
+ self.bottom_frame.grid_columnconfigure(0, weight=1)
89
+
90
+ # Initialize file path variables
91
+ self.work_dir = None
92
+ self.input_file_name = None
93
+ self.tree = None
94
+
95
+ # Add all widgets to the GUI
96
+ self.create_widgets()
97
+
98
+ # Initialize variables to store user selections
99
+ self.scen_df: pd.DataFrame | None = None
100
+ self.excel_format: str | None = None
101
+ self.pipeline_id: str | None = None
102
+ self.scenario_id: str | None = None
103
+ self.bl_verbose: str | None = None
104
+ self.output_combination: str | None = None
105
+
106
+ def create_widgets(self):
107
+ '''
108
+ Create and place all widgets in the GUI.
109
+
110
+ Adds labels, entry fields, buttons, comboboxes, and separators
111
+ to the top frame for user input and configuration.
112
+ '''
113
+ # Initialize row index for grid placement
114
+ self.irow_tree = 0
115
+
116
+ # Add a horizontal separator at the top
117
+ separator_horizontal = tk.Frame(self.top_frame, height=1, bg='#000000')
118
+ separator_horizontal.grid(row=self.irow_tree, column=0, columnspan=7, sticky='ew')
119
+
120
+ # Add header labels for Parameter, Input Entry, and Comments
121
+ self.irow_tree += 1
122
+ self.excel_label = tk.Label(self.top_frame, text='Parameter', anchor='w', font=('Segoe UI', 11, 'bold'))
123
+ self.excel_label.grid(row=self.irow_tree, column=1, sticky='ew', padx=10, pady=5, ipadx=10, ipady=3)
124
+ self.excel_label = tk.Label(self.top_frame, text='Input Entry', anchor='center', font=('Segoe UI', 11, 'bold'), width=10)
125
+ self.excel_label.grid(row=self.irow_tree, column=3, sticky='ew', padx=10, pady=5, ipadx=10, ipady=3)
126
+ self.excel_label = tk.Label(self.top_frame, text='Comments', anchor='w', font=('Segoe UI', 11, 'bold'))
127
+ self.excel_label.grid(row=self.irow_tree, column=5, sticky='ew', padx=10, pady=5, ipadx=10, ipady=3)
128
+
129
+ # Add a horizontal separator below the headers
130
+ self.irow_tree += 1
131
+ separator_horizontal = tk.Frame(self.top_frame, height=1, bg='#000000')
132
+ separator_horizontal.grid(row=self.irow_tree, column=0, columnspan=7, sticky='ew')
133
+
134
+ # Add output combination combobox row
135
+ self.irow_tree += 1
136
+ self.bl_output_combination_label1 = tk.Label(self.top_frame, text='Select Excel input file format', anchor='w')
137
+ self.bl_output_combination_label1.grid(row=self.irow_tree, column=1, sticky='w', padx=10, pady=5, ipadx=10, ipady=3)
138
+ self.bl_output_combination_list1 = ['Current', 'Legacy']
139
+ self.combobox_bl_output_combination1 = ttk.Combobox(self.top_frame, values=self.bl_output_combination_list1, justify='center', width=10)
140
+ self.combobox_bl_output_combination1.set(self.bl_output_combination_list1[0])
141
+ self.combobox_bl_output_combination1.grid(row=self.irow_tree, column=3, sticky='nsew', padx=10, pady=5, ipadx=10, ipady=3)
142
+ self.excel_format = self.combobox_bl_output_combination1.get()
143
+
144
+ # Add Excel file selection row
145
+ self.irow_tree += 1
146
+ self.excel_label = tk.Label(self.top_frame, text='Select Excel input file:', anchor='w')
147
+ self.excel_label.grid(row=self.irow_tree, column=1, sticky='ew', padx=10, pady=5, ipadx=10, ipady=3)
148
+ self.excel_button = tk.Button(self.top_frame, text='Open', command=self.open_file, anchor='center', width=10)
149
+ self.excel_button.grid(row=self.irow_tree, column=3, sticky='ew', padx=10, pady=5, ipadx=10)
150
+ self.excel_label = tk.Label(self.top_frame, text='Excel input file name', anchor='w')
151
+ self.excel_label.grid(row=self.irow_tree, column=5, sticky='ew', padx=10, pady=5, ipadx=10, ipady=3)
152
+
153
+ # Add Pipeline ID entry row
154
+ self.irow_tree += 1
155
+ self.pipeline_id_label = tk.Label(self.top_frame, text='Pipeline ID:', anchor='w')
156
+ self.pipeline_id_label.grid(row=self.irow_tree, column=1, sticky='w', padx=10, pady=5, ipadx=10, ipady=3)
157
+ self.pipeline_id_entry = tk.Entry(self.top_frame, justify='center', width=10)
158
+ self.pipeline_id_entry.grid(row=self.irow_tree, column=3, sticky='nsew', padx=10, pady=5, ipadx=10, ipady=3)
159
+ self.pipeline_id_entry.insert(0, 'Empty')
160
+ self.pipeline_id = self.pipeline_id_entry.get()
161
+
162
+ # Add Scenario IDs entry row
163
+ self.irow_tree += 1
164
+ self.scenario_id_label = tk.Label(self.top_frame, text='Scenario IDs (comma-separated):', anchor='w')
165
+ self.scenario_id_label.grid(row=self.irow_tree, column=1, sticky='w', padx=10, pady=5, ipadx=10, ipady=3)
166
+ self.scenario_id_entry = tk.Entry(self.top_frame, justify='center', width=10)
167
+ self.scenario_id_entry.grid(row=self.irow_tree, column=3, sticky='nsew', padx=10, pady=5, ipadx=10, ipady=3)
168
+ self.scenario_id_entry.insert(0, 'e.g. 1,2,3')
169
+ self.scenario_id = self.scenario_id_entry.get()
170
+
171
+ # Add verbose output combobox row
172
+ self.irow_tree += 1
173
+ self.bl_verbose_label = tk.Label(self.top_frame, text='Enable verbose output', anchor='w')
174
+ self.bl_verbose_label.grid(row=self.irow_tree, column=1, sticky='w', padx=10, pady=5, ipadx=10, ipady=3)
175
+ self.bl_verbose_list = ['True', 'False']
176
+ self.combobox_bl_verbose = ttk.Combobox(self.top_frame, values=self.bl_verbose_list, justify='center', width=10)
177
+ self.combobox_bl_verbose.grid(row=self.irow_tree, column=3, sticky='nsew', padx=10, pady=5, ipadx=10, ipady=3)
178
+ self.combobox_bl_verbose.set(self.bl_verbose_list[0])
179
+ self.bl_verbose = self.combobox_bl_verbose.get()
180
+
181
+ # Add output combination combobox row
182
+ self.irow_tree += 1
183
+ self.bl_output_combination_label2 = tk.Label(self.top_frame, text='Extract extended results', anchor='w')
184
+ self.bl_output_combination_label2.grid(row=self.irow_tree, column=1, sticky='w', padx=10, pady=5, ipadx=10, ipady=3)
185
+ self.bl_output_combination_list2 = ['True', 'False']
186
+ self.combobox_bl_output_combination2 = ttk.Combobox(self.top_frame, values=self.bl_output_combination_list2, justify='center', width=10)
187
+ self.combobox_bl_output_combination2.grid(row=self.irow_tree, column=3, sticky='nsew', padx=10, pady=5, ipadx=10, ipady=3)
188
+ self.combobox_bl_output_combination2.set(self.bl_output_combination_list2[-1])
189
+ self.output_combination = self.combobox_bl_output_combination2.get()
190
+
191
+ # Add OK button row
192
+ self.irow_tree += 1
193
+ self.bl_verbose_label = tk.Label(self.top_frame, text='Run all scenarios', anchor='w')
194
+ self.bl_verbose_label.grid(row=self.irow_tree, column=1, sticky='w', padx=10, pady=5, ipadx=10, ipady=3)
195
+ self.ok_button = tk.Button(self.top_frame, text='OK', command=self.close_app, width=10)
196
+ self.ok_button.grid(row=self.irow_tree, column=3, sticky='nsew', padx=10, pady=5, ipadx=10)
197
+
198
+ # Add a horizontal separator at the bottom
199
+ self.irow_tree += 1
200
+ separator_horizontal = tk.Frame(self.top_frame, height=1, bg='#000000')
201
+ separator_horizontal.grid(row=self.irow_tree, column=0, columnspan=7, sticky='ew')
202
+
203
+ # Add vertical separators between columns
204
+ separator = tk.Frame(self.top_frame, width=1, bg='#000000')
205
+ separator.grid(row=0, column=0, rowspan=self.irow_tree+1, sticky='ns', padx=0, pady=0)
206
+ separator = tk.Frame(self.top_frame, width=1, bg='#000000')
207
+ separator.grid(row=0, column=2, rowspan=self.irow_tree+1, sticky='ns', padx=0, pady=0)
208
+ separator = tk.Frame(self.top_frame, width=1, bg='#000000')
209
+ separator.grid(row=0, column=4, rowspan=self.irow_tree+1, sticky='ns', padx=0, pady=0)
210
+ separator = tk.Frame(self.top_frame, width=1, bg='#000000')
211
+ separator.grid(row=0, column=6, rowspan=self.irow_tree+1, sticky='ns', padx=0, pady=0)
212
+
213
+ def open_file(self):
214
+ '''
215
+ Open a file dialog for the user to select an Excel input file.
216
+
217
+ Updates the displayed file name and triggers loading of scenario
218
+ data into the table if a valid file is selected.
219
+ '''
220
+ # Open a file dialog for Excel files
221
+ file_path = filedialog.askopenfilename(
222
+ initialdir=os.getcwd(),
223
+ filetypes=[('Excel files', '*.xlsx *.xls *.xlsm')],
224
+ title='Select Excel input file'
225
+ )
226
+
227
+ # If the user cancels the dialog, exit the method
228
+ if not file_path:
229
+ return
230
+
231
+ # Store the directory of the selected file
232
+ self.work_dir = os.path.dirname(file_path)
233
+
234
+ # Store the file name of the selected file
235
+ self.input_file_name = os.path.basename(file_path)
236
+
237
+ # Update the label to display the selected file name
238
+ self.excel_label.config(text=self.input_file_name)
239
+
240
+ # Load the scenario data into the Treeview
241
+ self.setup_treeview()
242
+
243
+ def setup_treeview(self):
244
+ '''
245
+ Set up and populate the Treeview widget with scenario data.
246
+
247
+ Reads the selected Excel file, configures the table columns and
248
+ headings, and inserts scenario data rows for user review.
249
+ '''
250
+ # Read the 'Scenario' sheet from the selected Excel file into a DataFrame
251
+ self.scen_df = pd.read_excel(rf'{self.work_dir}/{self.input_file_name}', sheet_name='Scenario')
252
+ columns = self.scen_df.columns.tolist()
253
+
254
+ # Convert columns from 2 to (last-2) to integers
255
+ int_cols = self.scen_df.columns[1:-2]
256
+ self.scen_df[int_cols] = (
257
+ self.scen_df[int_cols]
258
+ .apply(pd.to_numeric, errors="coerce")
259
+ .fillna(0)
260
+ .astype("Int64")
261
+ )
262
+
263
+ # Create the Treeview widget with columns from the DataFrame
264
+ self.tree = ttk.Treeview(self.bottom_frame, columns=self.scen_df.columns.tolist(), show='headings')
265
+ self.tree.grid(row=0, column=0, sticky='nsew')
266
+
267
+ # Configure Treeview and heading styles
268
+ style = ttk.Style()
269
+ style.theme_use('default')
270
+ style.configure('Treeview.Heading', font=('Segoe UI', 10, 'bold'), padding=[0, 10], background='#f5f5f5')
271
+ style.configure('Treeview',font=('Segoe UI', 10), rowheight=30, borderwidth=1, relief='solid', background='#f5f5f5', fieldbackground='#f5f5f5')
272
+
273
+ # Set up column headings and widths
274
+ for i, col in enumerate(columns):
275
+ # Last column: left-aligned, stretchable
276
+ if i == len(columns) - 1:
277
+ self.tree.heading(col, text=col, anchor='w')
278
+ self.tree.column(col, stretch=True, anchor='w')
279
+ # Second and third last columns: center-aligned, fixed width
280
+ elif (i == len(columns) - 2) or (i == len(columns) - 3):
281
+ self.tree.heading(col, text=col, anchor='center')
282
+ self.tree.column(col, width=140, stretch=False, anchor='center')
283
+ # All other columns: center-aligned, smaller fixed width
284
+ else:
285
+ self.tree.heading(col, text=col, anchor='center')
286
+ self.tree.column(col, width=100, stretch=False, anchor='center')
287
+
288
+ # Insert each row of the DataFrame into the Treeview
289
+ for _, row in self.scen_df.iterrows():
290
+ self.tree.insert('', 'end', values=list(row))
291
+
292
+ # Reset other relevant fields
293
+ self.pipeline_id_entry.delete(0, tk.END)
294
+ self.scenario_id_entry.delete(0, tk.END)
295
+ self.pipeline_id_entry.insert(0, self.scen_df['Pipeline'].iloc[0])
296
+ self.scenario_id_entry.insert(0, self.scen_df['Scenario'].iloc[0])
297
+
298
+ def close_app(self):
299
+ '''
300
+ Validate user input and close the GUI if all inputs are valid.
301
+
302
+ Checks that required fields are filled and formatted correctly.
303
+ If validation passes, stores user selections and closes the window.
304
+ Otherwise, displays appropriate warning or error messages.
305
+ '''
306
+ # Get the current values from the entry widgets
307
+ pipeline_id_value = self.pipeline_id_entry.get()
308
+ scenario_id_value = self.scenario_id_entry.get()
309
+
310
+ # Check if work_dir and input_file_name are set
311
+ if not self.work_dir or not self.input_file_name:
312
+ tk.messagebox.showwarning('Input Required', 'Please select a valid Excel input file.')
313
+ return
314
+
315
+ # Check for empty or placeholder values
316
+ if not pipeline_id_value or pipeline_id_value == 'Empty' or \
317
+ not scenario_id_value or scenario_id_value == 'e.g. 1,2,3':
318
+ tk.messagebox.showwarning('Input Required', 'Please enter valid Pipeline ID and Scenario IDs.')
319
+ return
320
+
321
+ # Optional: Check if Pipeline ID is a string
322
+ if pipeline_id_value.isdigit():
323
+ tk.messagebox.showwarning('Invalid Input', 'Pipeline ID should be a string.')
324
+ return
325
+
326
+ # Optional: Check if Scenario IDs are comma-separated numbers
327
+ scenario_ids = [s.strip() for s in scenario_id_value.split(',')]
328
+ if not all(s.isdigit() for s in scenario_ids):
329
+ tk.messagebox.showwarning('Invalid Input', 'Scenario IDs should be comma-separated numbers (e.g. 1,2,3).')
330
+ return
331
+
332
+ # Additional checks: Ensure pipeline_id_value and scenario_id_value(s) exist in the DataFrame
333
+ if self.scen_df is not None:
334
+ # Check if the Pipeline ID exists in the DataFrame
335
+ if pipeline_id_value not in self.scen_df['Pipeline'].astype(str).unique():
336
+ tk.messagebox.showwarning('Invalid Input', f'Pipeline ID "{pipeline_id_value}" not found in the input file.')
337
+ return
338
+ # Check if each Scenario ID exists in the DataFrame
339
+ scenario_unique = set(self.scen_df['Scenario'].astype(str).unique())
340
+ invalid_scenarios = [sid for sid in scenario_ids if sid not in scenario_unique]
341
+ if invalid_scenarios:
342
+ tk.messagebox.showwarning(
343
+ 'Invalid Input',
344
+ f'Scenario ID(s) {", ".join(invalid_scenarios)} not found in the input file.'
345
+ )
346
+ return
347
+
348
+ try:
349
+ # Store the validated values in instance variables
350
+ self.excel_format = self.combobox_bl_output_combination1.get()
351
+ self.pipeline_id = pipeline_id_value
352
+ self.scenario_id = scenario_id_value
353
+ self.bl_verbose = self.combobox_bl_verbose.get()
354
+ self.output_combination = self.combobox_bl_output_combination2.get()
355
+ # Close the GUI window
356
+ self.root.destroy()
357
+ except tk.TclError as e:
358
+ # Show an error message if something unexpected happens
359
+ tk.messagebox.showerror('Error', f'An unexpected error occurred: {e}')