Problem - I have a Python function that reads values from an Excel sheet, performs some calculations, and writes back the data to the Excel sheet. Let's say this entire function takes 1 minute to complete. Now, I have converted my Python file to an .exe file and I am calling this .exe file through a VBA macro, and it is running.
Need Help - Once my code starts running, I need a stop button on the sg window (which I have created) that will stop my running code immediately. Is it possible to stop the Python code externally?
# Create GUI window
window = sg.Window("Peak Shave Simualtion", layout ,icon="C:\Env\Profit_calculator\Tricera-logo.ico") # main window configuration
progress_bar = window['-PROGRESS-']
# Event loop
while True:
    event, values = window.read()
    if event == sg.WIN_CLOSED: # if closed or click on cancel button window will close
        break
    elif event == 'Process': 
        file_path = sys.argv[1]
        start_time = time.time()
        Python_profitcalculator_simulation(file_path)
        end_time = time.time()
        sg.popup(f"Processing complete!\nSimulation total time: {(f'{((end_time - start_time)/60):.2f} minutes' if (end_time - start_time) >= 60 else f'{(end_time - start_time):.2f} seconds')}")
        break
    elif event == 'Stop':
        exit()
        break
# Close GUI window
window.close()
In above code - I tried to create a stop button and when I click on 'Process' my sg.window is freeze and i can not press on button until it completes. So, basically stop button is useless.
Updated - I tried to used flag
import time
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
from peak_shave.peak_shave_reader import Peakshave_Reader
from Seb.profit_calculator_dataclass_neu import *
from peak_shave.raw_data_setup_profit_calculator import Raw_data_from_Peak_shave, peak_shave_final_data
import PySimpleGUI as sg
from tkinter.filedialog import askopenfilename
import sys
import threading
import pythoncom
# Define a flag to indicate whether the function should stop running or not
stop_flag = False
def Python_profitcalculator_simulation(excel_path):
    global stop_flag
    progress_bar.update(10) # 10% of progress for funtion loads
    excel_workbook = Peakshave_Reader(excel_path)
    progress_bar.update(20) # 20% of progress for excel sheet load
    # Reading Peak_shave excel
    Pv_Profile_Raw = Raw_data_from_Peak_shave.PV_profile_raw(excel_workbook)
    Load_Profile_raw = Raw_data_from_Peak_shave.Load_profile_raw(excel_workbook)
    Investtabelle_Raw = Raw_data_from_Peak_shave.Investtabelle_raw_data(excel_workbook)
    battery_raw_Eingabe_Übersicht_sheet = Raw_data_from_Peak_shave.Raw_data_from_Eingabe_Übersicht_sheet(excel_workbook)
    battery_raw_Simulation_sheet = Raw_data_from_Peak_shave.Raw_data_from_Simulation_sheet(excel_workbook)
    battery_raw_AdvanceSettingsDatabase_sheet = Raw_data_from_Peak_shave.Raw_data_from_AdvanceSettingsDatabase_sheet(excel_workbook)
    battery_raw_Wirtschaftlichkeitsmatrix_sheet = Raw_data_from_Peak_shave.Raw_data_from_Wirtschaftlichkeitsmatrix_sheet(excel_workbook)
    Wirtschaftlichkeitsmatrix_Raw = Raw_data_from_Peak_shave.wirtschaftlichkeitsmatrix_sheet_battery_configuration_values(excel_workbook)
    # Convertion of values in SI unit
    PV_Profile = peak_shave_final_data.PVprofile_conversion_setup(Pv_Profile_Raw)
    Load_Profile = peak_shave_final_data.Loadprofile_conversion_setup(Load_Profile_raw)
    Eingabe_Übersicht_sheet = peak_shave_final_data.Eingabe_Übersicht_conversion(battery_raw_Eingabe_Übersicht_sheet)
    Simulation_sheet = peak_shave_final_data.Simulation_convertsion(battery_raw_Simulation_sheet)
    AdvanceSettingsDatabase_sheet = peak_shave_final_data.AdvanceSettingsDatabase_conversion(battery_raw_AdvanceSettingsDatabase_sheet)
    Wirtschaftlichkeitsmatrix_sheet = peak_shave_final_data.Wirtschaftlichkeitsmatrix_conversion(battery_raw_Wirtschaftlichkeitsmatrix_sheet)
    Wirtschaftlichkeitsmatrix_battery_configuration = peak_shave_final_data.Wirtschaftlichkeitsmatrix_battery_configuration_values(Wirtschaftlichkeitsmatrix_Raw)
    #profile setup for simulation
    p_pv = np.array(PV_Profile['PV_profile'])
    p_load = np.array(Load_Profile['Load_profile'])*-1
    
    #general input
    technical_input = dict(
    p_cut_consumption_W = Eingabe_Übersicht_sheet.P_cut_consumption_W,
    PV_W_peak = Eingabe_Übersicht_sheet.PV_Pinstall,
    cp_rate = Eingabe_Übersicht_sheet.Cp_rate,
    bat_capacity_E_Wh = Eingabe_Übersicht_sheet.Battery_energy_nominal,
    DoD_pu = Simulation_sheet.DoD,
    p_reserve_W = Eingabe_Übersicht_sheet.Battery_Pmax_reserve,
    efficiency = Eingabe_Übersicht_sheet.Battery_efficiency,
    SOC_target_pu = Eingabe_Übersicht_sheet.Battery_soll_soc_1,
    SOC_reserve_pu = Eingabe_Übersicht_sheet.Battery_reserve_soc_1
    )
    eco_input = dict(
    cost_pv_per_kWp = Eingabe_Übersicht_sheet.Investment_cost_PV,
    cost_bat = Eingabe_Übersicht_sheet.Investment_cost_battery,
    equity = Eingabe_Übersicht_sheet.Equity_share,
    duration_funding_years = Eingabe_Übersicht_sheet.Financing_period,
    duration_operation_years = Eingabe_Übersicht_sheet.Observation_period
    )
    general = {
    'technical_input' : technical_input,
    'eco_input' : eco_input
    }
    #advanced input
    tech_param = dict(
    SOC_start_pu= AdvanceSettingsDatabase_sheet.Battery_soc_start,
    degradation = AdvanceSettingsDatabase_sheet.Degredation,
    pv_deg_per_year = AdvanceSettingsDatabase_sheet.PV_degradation_per_year, #/a
    bat_deg_per_year = AdvanceSettingsDatabase_sheet.Battery_degradation
    )
    maintanance_spare = dict(
    maintained = AdvanceSettingsDatabase_sheet.Maintained,
    pv_maintain_per_akWp = AdvanceSettingsDatabase_sheet.Maintenance_cost_PV, 
    bat_maintain_per_akWh = AdvanceSettingsDatabase_sheet.Maintenance_cost_battery, 
    pv_spare_per_akWp = AdvanceSettingsDatabase_sheet.PV_spare_per_kWp,
    bat_spare_per_akWh = AdvanceSettingsDatabase_sheet.Bat_spare_per_kWh
    )
    insurance = dict(
    pv_insured = AdvanceSettingsDatabase_sheet.Consider_insurance_PV,
    pv_insurance_per_akWp = AdvanceSettingsDatabase_sheet.PV_insurance, #/kWp * a
    bat_insured = AdvanceSettingsDatabase_sheet.Consider_insurance_bat,
    bat_insurance_per_akWh = AdvanceSettingsDatabase_sheet.Battery_insurance #/kWp * a
    )
    finance = dict(
    borrowed_interest = AdvanceSettingsDatabase_sheet.Borrowing_rate
    )
    tax = dict(
    corporate_tax = AdvanceSettingsDatabase_sheet.Corporate_tax,
    trade_tax = AdvanceSettingsDatabase_sheet.Trade_tax
    )
    discount = dict(
    discounted = AdvanceSettingsDatabase_sheet.Consider_discounting,
    discounting = AdvanceSettingsDatabase_sheet.Discount
    )
    price_per_unit = dict(
    cost_current_jbd_below_per_kWh = AdvanceSettingsDatabase_sheet.Electricity_price_jbd_above_per_kWp,
    cost_current_jbd_above_per_kWh = AdvanceSettingsDatabase_sheet.Electricity_price_jbd_below_per_kWp,
    cost_power_jbd_below_per_kW = Eingabe_Übersicht_sheet.Power_price_jbd_above_per_kWp,
    cost_power_jbd_above_per_kW = Eingabe_Übersicht_sheet.Power_price_jbd_below_per_kWp,
    revenue_injected_current_per_kWh = Eingabe_Übersicht_sheet.Feedin_with_PPA_Direct_feedin_PV,
    staggering_current_per_year = AdvanceSettingsDatabase_sheet.Electricity_price_increase_rate
    )
    lease = dict(
    leased = AdvanceSettingsDatabase_sheet.Consider_lease,
    needed_space_pv_ha_per_MWp = AdvanceSettingsDatabase_sheet.PV_space_requirement,
    needed_space_bat_ha_per_MWh = AdvanceSettingsDatabase_sheet.Battery_space_requirement,
    lease_pv_per_ha = AdvanceSettingsDatabase_sheet.PV_lease,
    lease_bat_per_ha = AdvanceSettingsDatabase_sheet.Battery_lease
    )
    advanced = {
    'tech_param': tech_param,
    'maintanance_spare' : maintanance_spare,
    'insurance' : insurance,
    'finance' : finance,
    'tax' : tax,
    'discount' : discount,
    'price_per_unit' : price_per_unit,
    'lease' : lease
    }
    #other input not important
    other_input = dict(
    abschreibung_pv = 25_000,
    abschreibung_bat = 13_750.66
    )
    profit_calc_params = {
    'general' : general,
    'advanced' : advanced,
    'other_input' : other_input
    }
    
    W_matrix = {'energy_column_1': Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_1,
    'energy_column_2' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_2,
    'energy_column_3': Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_3,
    'energy_column_4' :Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_4,
    'energy_column_5': Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_5,
    'energy_column_6': Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_6,
    'energy_column_7' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_7,
    'energy_column_8' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_8,
    'energy_column_9' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_9,
    'energy_column_10' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_10}
    W_matrix_bool = {'energy_column_1': Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_1_bool,
    'energy_column_2' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_2_bool,
    'energy_column_3': Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_3_bool,
    'energy_column_4' :Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_4_bool,
    'energy_column_5': Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_5_bool,
    'energy_column_6': Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_6_bool,
    'energy_column_7' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_7_bool,
    'energy_column_8' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_8_bool,
    'energy_column_9' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_9_bool,
    'energy_column_10' : Wirtschaftlichkeitsmatrix_battery_configuration.energy_column_10_bool}
    W_matrix_C_rate = {'C_Rate_row_1' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_1,
    'C_Rate_row_2' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_2,
    'C_Rate_row_3' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_3,
    'C_Rate_row_4' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_4,
    'C_Rate_row_5' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_5}
    W_matrix_C_rate_bool = {'C_Rate_row_1' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_1_bool,
    'C_Rate_row_2' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_2_bool,
    'C_Rate_row_3' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_3_bool,
    'C_Rate_row_4' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_4_bool,
    'C_Rate_row_5' : Wirtschaftlichkeitsmatrix_battery_configuration.C_Rate_row_5_bool}
    # Investtabelle battery cost setup
    bats = []
    for key in W_matrix:
        if W_matrix_bool[key] == 'berechnen':
            bats.append(W_matrix[key])
    rates = []
    for key in W_matrix_C_rate:
        if W_matrix_C_rate_bool[key] == 'berechnen':
            rates.append(W_matrix_C_rate[key])
    investtable = pd.DataFrame(index = rates, columns = bats)
    for bat in bats:
        for rate in rates:
            if Eingabe_Übersicht_sheet.Battery_type == 'Generisch':
                battery_type_key = 'PB300'
            else:
                battery_type_key = Eingabe_Übersicht_sheet.Battery_type
            try:
                investtable[bat][rate] = Investtabelle_Raw[Eingabe_Übersicht_sheet.Battery_module][battery_type_key][Eingabe_Übersicht_sheet.Installation_site]['energy'+'_'+str(int(bat/1000))+'_C_rate_'+str(rate)]
            except KeyError: 
                investtable[bat][rate]= 0
                print("KeyError:", rate)
    
    battery_type = Eingabe_Übersicht_sheet.Battery_type
    SOC_res1 = Eingabe_Übersicht_sheet.Battery_reserve_soc_1
    SOC_res2 = Eingabe_Übersicht_sheet.Battery_reserve_soc_2
    SOC1_max = Wirtschaftlichkeitsmatrix_sheet.Soll_soc_1_max
    SOC1_min = Wirtschaftlichkeitsmatrix_sheet.Soll_soc_1_min
    SOC2_max = Wirtschaftlichkeitsmatrix_sheet.Soll_soc_2_max
    SOC2_min = Wirtschaftlichkeitsmatrix_sheet.Soll_soc_2_min
    SOC_step_percent = Wirtschaftlichkeitsmatrix_sheet.Step_size_soc
    accuracy = Wirtschaftlichkeitsmatrix_sheet.Break_off_precision_peakshaving
    finanzierung = Wirtschaftlichkeitsmatrix_sheet.Financing
    
    SOC_start = AdvanceSettingsDatabase_sheet.Battery_soc_start
    year_list = get_year_list(Eingabe_Übersicht_sheet.Battery_soll_soc_2_start,Eingabe_Übersicht_sheet.Battery_soll_soc_2_end)
    timestep =0.25 #=15min
    
    progress_bar.update(50) # 50% of progress for pre process done and simulation is starting
    # Simulation
    amount_SOCs = Wirtschaftlichkeitsmatrix_sheet.Number_of_SOCs
    algorithm = Wirtschaftlichkeitsmatrix_sheet.Algorithm_for_12_SOCs
    simulation = Wirtschaftlichkeitsmatrix_sheet.Year_simulation_of_the_best_system
    if amount_SOCs == 2:
        w_matrix, wartung = w_matrix_modular_excel(p_load, p_pv,0,SOC_res1, SOC_res2, SOC_start, SOC1_min, SOC1_max,  SOC2_min, SOC2_max,SOC_step_percent, accuracy, Wirtschaftlichkeitsmatrix_sheet.Consideration_case,finanzierung,bats,rates, profit_calc_params, investtable, battery_type,year_list, timestep)
    else:
        if algorithm == 'dispatcher':
            algo = 1
        elif algorithm == 'brute-force':
            algo = 2
        else: 
            algo = 3
        w_matrix = w_matrix_modular_monthly(algo, p_load, p_pv, SOC_start, SOC1_min, SOC1_max,  SOC_step_percent, Wirtschaftlichkeitsmatrix_sheet.Consideration_case,finanzierung,bats,rates, profit_calc_params, investtable, battery_type, timestep)
    
    system,bat_capacity_E,c_rate = get_most_profitable_system(w_matrix)
    progress_bar.update(80) # 80% of progress for Simulation done and writing results
    if simulation == 'EXCEL' and amount_SOCs == 2:
        Peakshave_Reader.write_Best_system_to_excel(excel_workbook,number_of_SOCs=amount_SOCs,matrix_data=system,bat_energie=bat,c_rate=c_rate)
        Peakshave_Reader.write_matrixdata_SOCs_to_excel(excel_workbook,number_of_SOCs=amount_SOCs,Profit_calculator_matrix_result=w_matrix)
        ##just write the data back,  no simulatioon
    elif simulation == 'BETSI':
        pass
        #results = GlobalFactory
        #write it back
    else:
        results = do_simulation_with_numba(p_load, p_pv, system, bat_capacity_E, c_rate, battery_type, SOC_start, year_list)
        Peakshave_Reader.write_Best_system_to_excel(excel_workbook,number_of_SOCs=amount_SOCs,matrix_data=system,bat_energie=bat,c_rate=c_rate)
        Peakshave_Reader.write_data_to_excel(excel_workbook,data=results)
        Peakshave_Reader.write_matrixdata_SOCs_to_excel(excel_workbook,number_of_SOCs=amount_SOCs,Profit_calculator_matrix_result=w_matrix)
        #write it back  
    progress_bar.update(100) # 100% process completed
    while True:
        # Check the stop flag and break out of the loop if it's set to True
        if stop_flag:
            break
def run_simulation(excel_path):
    global stop_flag
    start_time = time.time()
    # Create a new thread to run the simulation function
    simulation_thread = threading.Thread(target=Python_profitcalculator_simulation, args=(excel_path,))
    simulation_thread.start()
    # Wait for the simulation to complete or for the stop button to be pressed
    while simulation_thread.is_alive():
        event, values = window.read()
        if event == sg.WIN_CLOSED or event == 'Cancel':
            break
        elif event == 'Stop':
            stop_flag = True
            break
    # Calculate the simulation time and show a message
    end_time = time.time()
    if not stop_flag:
        sg.popup(f"Processing complete!\nSimulation total time: {(f'{((end_time - start_time)/60):.2f} minutes' if (end_time - start_time) >= 60 else f'{(end_time - start_time):.2f} seconds')}")
# Define GUI layout
layout = [[sg.Text("Python Peak Shave Simulation:")], # For title
        [sg.ProgressBar(100, orientation='h', size=(20, 20), key='-PROGRESS-')], # progress bar configuration
        [sg.Button("Process"), sg.Button("Stop")]] # Stop button configuration 
# Create GUI window
window = sg.Window("Peak Shave Simulation", layout, icon="C:\Env\Profit_calculator\Tricera-logo.ico") # main window configuration
progress_bar = window['-PROGRESS-']
# Event loop
event, values = window.read()
if event == sg.WIN_CLOSED : # if closed or click on cancel button window will close
    window.close()
elif event == 'Process': 
    excel_path = sys.argv[1]
    if excel_path == '':
        sg.popup('Please select an Excel file.')
    else:
        run_simulation(excel_path)
elif event == 'Stop':
    # This event will only be triggered if the simulation is running
    stop_flag = True
    window.close()
    exit()
I added thread and flag but, i dont know what i am doing wrong. I am getting this error
  File "C:\Python395\lib\threading.py", line 980, in _bootstrap_inner
    self.run()
  File "C:\Python395\lib\threading.py", line 917, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Env\profit_calculator_2.py", line 257, in Python_profitcalculator_simulation
    Peakshave_Reader.write_Best_system_to_excel(excel_workbook,number_of_SOCs=amount_SOCs,matrix_data=system,bat_energie=bat,c_rate=c_rate)
  File "C:\Env\peak_shave\peak_shave_reader.py", line 151, in write_Best_system_to_excel
    app = xw.App(visible=False)
  File "C:\Env\Profit_Calculator_env\lib\site-packages\xlwings\main.py", line 294, in __init__
    self.impl = engines.active.apps.add(
  File "C:\Env\Profit_Calculator_env\lib\site-packages\xlwings\main.py", line 203, in add
    return App(impl=self.impl.add(**kwargs))
  File "C:\Env\Profit_Calculator_env\lib\site-packages\xlwings\_xlwindows.py", line 497, in add
    return App(spec=spec, add_book=add_book, xl=xl, visible=visible)
  File "C:\Env\Profit_Calculator_env\lib\site-packages\xlwings\_xlwindows.py", line 521, in __init__
    self._xl = COMRetryObjectWrapper(DispatchEx("Excel.Application"))
  File "C:\Env\Profit_Calculator_env\lib\site-packages\win32com\client\__init__.py", line 145, in DispatchEx
    dispatch = pythoncom.CoCreateInstanceEx(
pywintypes.com_error: (-2147221008, 'CoInitialize wurde nicht aufgerufen.', None, None)
