Python Forum
Excel isnt working properly after python function is started - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Excel isnt working properly after python function is started (/thread-42060.html)



Excel isnt working properly after python function is started - IchNar - May-01-2024

I have a Python application that can create a graph based on input that the user enters into a form(GUI) that is created via Tkinter. This application is actually made as an Excel addin. It works by creating a custom addin via xlwings and creating a custom ribbon with a button via RibbonXEditor. When the user presses this button, the python function show_form is launched via the VBA function, which actually displays the form. And now the problem is that when the form is displayed, then Excel does not work properly, it is not possible to write in the cells, I have to close the form and only then it works normally, is there any way to fix it?Here is for example the VBA code than is launching the python function.
Sub show_form(control As IRibbonControl)
Dim mymodule As String
mymodule = Left(ThisWorkbook.Name, _
        (InStrRev(ThisWorkbook.Name, ".", -1, vbTextCompare) - 1))
RunPython "import " & mymodule & ";" & _
    mymodule & ".show_form()"
And here is the my_show function:
def show_form():
 
    window = tk.Tk()
    window.title("Calculation and Display of p-value")
 
    window.resizable(width=False, height=False)
    frame = tk.Frame(window)
    frame.pack(padx=10, pady=10)
 
 
    # First Section: Distribution Selection
    distribution_frame = tk.LabelFrame(frame, text="Distribution Selection")
    distribution_frame.grid(row=0, column=0, padx=20, pady=10, sticky="news")
    distribution_label = tk.Label(distribution_frame, text="Distribution: ")
    distribution_label.grid(row=0, column=0, padx=20, pady=20)
    distribution_select = ttk.Combobox(distribution_frame, values=["t-distribution", "F-distribution", "chi-square distribution", "z-distribution(0,1)"], state="readonly")
    distribution_select.grid(row=0, column=1, padx=(0,20), pady=10)
 
    # Second Section: Test Statistic Value
    value_frame = tk.LabelFrame(frame, text="Test Statistic Value")
    value_frame.grid(row=2, column=0, padx=20, pady=10, sticky="news")
 
    # Third Section: Test Parameters
    parameters_frame = tk.LabelFrame(frame, text="Statistical Test Parameters")
    parameters_frame.grid(row=1, column=0, padx=20, pady=(0,5), sticky="news")  
 
 
    def update_parameters(event):
        global value_number, parameter1_number, parameter2_number
        # Clearing previous elements
        for widget in parameters_frame.winfo_children():
            widget.destroy()
 
 
        # Creating new elements based on selection in combobox
        if distribution_select.get() == "t-distribution":
            parameter1_label = tk.Label(parameters_frame, text="n₁: ")
            parameter1_label.grid(row=0, column=0, padx=(20,5), pady=10, sticky='w') 
            parameter1_number = tk.Entry(parameters_frame)
            parameter1_number.grid(row=0, column=1, padx=(0,20), pady=10, sticky="e")
            value_label = tk.Label(value_frame, text="t₀: ")
            value_label.grid(row=0, column=0, padx=20, pady=20)
            value_number = tk.Entry(value_frame)
            value_number.grid(row=0, column=1, padx=(0,20), pady=10)
        elif distribution_select.get() == "F-distribution":
            parameter1_label = tk.Label(parameters_frame, text="n₁: ")
            parameter1_label.grid(row=0, column=0, padx=(20,5), pady=10, sticky='w') 
            parameter1_number = tk.Entry(parameters_frame)
            parameter1_number.grid(row=0, column=1, padx=(0,20), pady=10)
            parameter2_label = tk.Label(parameters_frame, text="n₂: ")
            parameter2_label.grid(row=1, column=0, padx=(20,5), pady=10, sticky='w') 
            parameter2_number = tk.Entry(parameters_frame)
            parameter2_number.grid(row=1, column=1, padx=(0,20), pady=10)
            value_label = tk.Label(value_frame, text="F₀: ")
            value_label.grid(row=0, column=0, padx=20, pady=20)
            value_number = tk.Entry(value_frame)
            value_number.grid(row=0, column=1, padx=(0,20), pady=10)
        elif distribution_select.get() == "chi-square distribution":
            parameter1_label = tk.Label(parameters_frame, text="n₁: ")
            parameter1_label.grid(row=0, column=0, padx=(20,5), pady=10, sticky='w') 
            parameter1_number = tk.Entry(parameters_frame)
            parameter1_number.grid(row=0, column=1, padx=(0,20), pady=10)
            value_label = tk.Label(value_frame, text="x²₀: ")
            value_label.grid(row=0, column=0, padx=20, pady=20)
            value_number = tk.Entry(value_frame)
            value_number.grid(row=0, column=1, padx=(0,20), pady=10)
        elif distribution_select.get() == "z-distribution(0,1)":
            value_label = tk.Label(value_frame, text="z₀: ")
            value_label.grid(row=0, column=0, padx=20, pady=20)
            value_number = tk.Entry(value_frame)
            value_number.grid(row=0, column=1, padx=(0,20), pady=10)
 
 
 
 
    # Call the update_parameters function when there is a change in the combobox
    distribution_select.bind("<<ComboboxSelected>>", update_parameters)
 
    # Third Section: Test Type
    type_frame = tk.LabelFrame(frame, text="Test Type")
    type_frame.grid(row=3, column=0, padx=20, pady=(0,5), sticky="news")
 
    left_sided_value = tk.IntVar()
    right_sided_value = tk.IntVar()
    two_sided_value = tk.IntVar()
    alpha_value = tk.IntVar()
    test_type = tk.IntVar()
 
    def set_test_type():
        if test_type.get() == 1:
            left_sided_value.set(1)
            right_sided_value.set(0)
            two_sided_value.set(0)
        elif test_type.get() == 2:
            left_sided_value.set(0)
            right_sided_value.set(1)
            two_sided_value.set(0)
        else:
            left_sided_value.set(0)
            right_sided_value.set(0)
            two_sided_value.set(1)
 
    test_type.set(1)  # Sets the default value to left-sided test
    set_test_type() 
 
    left_sided_button = tk.Radiobutton(type_frame, text="Left-sided", variable=test_type, value=1, command=set_test_type)
    left_sided_button.grid(row=0, column=0, padx=(20, 5), pady=10, sticky='w')
 
    right_sided_button = tk.Radiobutton(type_frame, text="Right-sided", variable=test_type, value=2, command=set_test_type)
    right_sided_button.grid(row=1, column=0, padx=(20, 5), pady=10, sticky='w')
 
    two_sided_button = tk.Radiobutton(type_frame, text="Two-sided", variable=test_type, value=3, command=set_test_type)
    two_sided_button.grid(row=2, column=0, padx=(20, 5), pady=10, sticky='w')
 
 
 
 
    def update_alpha_widgets():
        global alpha_number
        if alpha_value.get() == 1:
            alpha_label = tk.Label(alpha_frame, text="Alpha value: ")
            alpha_label.grid(row=1, column=0, padx=(20,5), pady=10, sticky='w') 
            alpha_number = tk.Entry(alpha_frame)
            alpha_number.grid(row=1, column=1, padx=(0,20), pady=10)
 
 
        else:
            for widget in alpha_frame.winfo_children():
                widget.destroy()
            alpha_button = tk.Checkbutton(alpha_frame, text="Show Alpha", variable=alpha_value, onvalue=1, offvalue=0, command=update_alpha_widgets)
            alpha_button.grid(row=0, column=0, padx=(20,5), pady=10, sticky='w')
 
 
 
    # Fourth Section
    alpha_frame = tk.LabelFrame(frame, text="Alpha")
    alpha_frame.grid(row=4, column=0, padx=20, pady=(0,5), sticky="news")
 
    alpha_button = tk.Checkbutton(alpha_frame, text="Show Alpha", variable=alpha_value, onvalue=1, offvalue=0, command=update_alpha_widgets)
    alpha_button.grid(row=0, column=0, padx=(20,5), pady=10, sticky='w')
    def check_values():
 
        # Checking if all values are defined
 
        if distribution_select.get() == "t-distribution":
                if len(parameter1_number.get()) == 0 or len(value_number.get()) == 0:
                    messagebox.showerror("Error", "Parameter or test value is not provided")
                elif not parameter1_number.get().isnumeric() or not is_float(value_number.get()):
                    messagebox.showerror("Error", "Parameter and test value cannot contain letters")
                else:
                    if alpha_value.get() == 1:
                        if len(alpha_number.get()) == 0:
                            messagebox.showerror("Error","Alpha value must be provided")
                        elif not is_float(alpha_number.get()):
                            messagebox.showerror("Error","Alpha value cannot contain letters")
                        elif float(alpha_number.get()) <= 0 or float(alpha_number.get()) > 1:
                            messagebox.showerror("Error","Alpha value must be between 0 and 1")
                        else:
                            p_from_t(float(value_number.get()),int(parameter1_number.get()),left_sided_value.get(),right_sided_value.get(),two_sided_value.get(),float(alpha_number.get()))
                    else:
                        p_from_t(float(value_number.get()),int(parameter1_number.get()),left_sided_value.get(),right_sided_value.get(),two_sided_value.get(),None)
 
        elif distribution_select.get() == "z-distribution(0,1)":
            if len(value_number.get()) == 0:
                    messagebox.showerror("Error", "Test value is not provided")
            elif not is_float(value_number.get()):
                messagebox.showerror("Error", "Test value cannot contain letters")
            else:
                if alpha_value.get() == 1:
                        if len(alpha_number.get()) == 0:
                            messagebox.showerror("Error","Alpha value must be provided")
                        elif not is_float(alpha_number.get()):
                            messagebox.showerror("Error","Alpha value cannot contain letters")
                        elif float(alpha_number.get()) <= 0 or float(alpha_number.get()) > 1:
                            messagebox.showerror("Error","Alpha value must be between 0 and 1")
                        else:
                            p_from_z(float(value_number.get()),left_sided_value.get(),right_sided_value.get(),two_sided_value.get(),float(alpha_number.get()))
                else:
                    p_from_z(float(value_number.get()),left_sided_value.get(),right_sided_value.get(),two_sided_value.get(),None)
 
        elif distribution_select.get() == "F-distribution":
            if len(parameter1_number.get()) == 0 or len(value_number.get()) == 0 or len(parameter2_number.get()) == 0:
                    messagebox.showerror("Error", "Parameter or test value is not provided")
            elif not parameter1_number.get().isnumeric() or not is_float(value_number.get()) or not parameter2_number.get().isnumeric():
                messagebox.showerror("Error", "Parameter and test value cannot contain letters")
            else:
                if alpha_value.get() == 1:
                        if len(alpha_number.get()) == 0:
                            messagebox.showerror("Error","Alpha value must be provided")
                        elif not is_float(alpha_number.get()):
                            messagebox.showerror("Error","Alpha value cannot contain letters")
                        elif float(alpha_number.get()) <= 0 or float(alpha_number.get()) > 1:
                            messagebox.showerror("Error","Alpha value must be between 0 and 1")
                        else:
                            p_from_f(float(value_number.get()),int(parameter1_number.get()),int(parameter2_number.get()),left_sided_value.get(),right_sided_value.get(),two_sided_value.get(),float(alpha_number.get()))
                else:
                    p_from_f(float(value_number.get()),int(parameter1_number.get()),int(parameter2_number.get()),left_sided_value.get(),right_sided_value.get(),two_sided_value.get(),None)
 
        elif distribution_select.get() == "chi-square distribution":
            if len(parameter1_number.get()) == 0 or len(value_number.get()) == 0:
                    messagebox.showerror("Error", "Parameter or test value is not provided")
            elif not parameter1_number.get().isnumeric() or not is_float(value_number.get()):
                messagebox.showerror("Error", "Parameter and test value cannot contain letters")
            else:
                if alpha_value.get() == 1:
                        if len(alpha_number.get()) == 0:
                            messagebox.showerror("Error","Alpha value must be provided")
                        elif not is_float(alpha_number.get()):
                            messagebox.showerror("Error","Alpha value cannot contain letters")
                        elif float(alpha_number.get()) <= 0 or float(alpha_number.get()) > 1:
                            messagebox.showerror("Error","Alpha value must be between 0 and 1")
                        else:
                            p_from_chi_square(float(value_number.get()),int(parameter1_number.get()),left_sided_value.get(),right_sided_value.get(),two_sided_value.get(),float(alpha_number.get()))
                else:
                    p_from_chi_square(float(value_number.get()),int(parameter1_number.get()),left_sided_value.get(),right_sided_value.get(),two_sided_value.get(),None)
 
 
    button = tk.Button(frame, text="Generate", height=4, width=8, command=check_values)
    button.grid(row=5, column=0, padx=20, pady=20)
 
    window.mainloop()
Sorry for messy code


RE: Excel isnt working properly after python function is started - deanhystad - May-01-2024

When excel executes a Python script, it waits for the script to complete. Your script doesn't complete until the window is closed. This blocks excel from running. There are multiple ways to solve this problem. but the easiest might be to run the window script as a subprocess.

This is my window file (window.py)
import tkinter as tk

root = tk.Tk()
tk.Button(root, text="Close", command=root.destroy).pack(padx=100, pady=100)
root.mainloop()
I can do that by running the window as a subprocess.

This is a python script that runs window.py as a subprocess.
import subprocess

subprocess.Popen(["python", "window.py"])
print("I am done")
Your spreadsheet calls the launch script. The lauch script starts a process to run the window.py script. Because the launch script uses Popen withou a wait(), it doesn't wait for the window to close, and doesn't block the spreadsheet.
You'll need to use absolute paths for python.exe and window.py.


RE: Excel isnt working properly after python function is started - IchNar - May-01-2024

Thanks it works well, but i have one question. In the launch_script is it possible to use relative path? I used it with os but it didnt work well.