Title: Sharing Variables between Thread for TKinter application
Tags: python-3.x,multithreading,tkinter

Question: I am fairly new to coding and just started writing a Python application with TKinter which calls some Powershell scripts.
I created an OOP program that has two threads when the Start button is pressed. The problem is that I will need to share variables between the two threads (one running the GUI to keep it responsive and another one running the function that calls the powershell scripts a certain number of times).
Is there any ways that I could achieve this? Here below is my code:
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
#import subprocess
import datetime
#import os
import socket
#import sys
import threading


root = Tk()
root.geometry("400x350")
root.title("GSS Reboot Controller V0.1")

loops = 0
variable = False

selected_power = StringVar()



class GUI:

    def __init__(self, master):
        
        global selected_power

        # Frame for start button
        self.frame1 = Frame(root,padx=0,pady=5)
        self.frame1.pack(side="bottom",padx=5,pady=5)
        self.frame2 = LabelFrame(root,
                            text="IP Address Power Switch",
                            padx=5,pady=5)
        self.frame2.pack(side = "top",padx=5,pady=5)
        self.frame3 = LabelFrame(root,
                            text="IP Adress of Simulator",
                            padx=5, pady=5)
        self.frame3.pack(side="top")
        frame4 = LabelFrame(root,
                            text="Power Switch to Select",
                            padx=5,pady=5)
        frame4.pack(side="top",padx=5,pady=5)


        # Start button
        self.start = Button(self.frame1,text="Start",
                    background="Green",
                    font="Ariel 12",
                    command=threading.Thread(
                            target=self.powershell_scripts).start())
        self.start.grid(column=0,row=1,padx=10)

        # Stop button
        self.stop = Button(self.frame1,text="Stop",
                    font="Ariel 12",background="Red",
                    command=self.stop_infinitescript)
        self.stop.grid(column=2,row=1,padx=10)

        # Generate report button
        self.Report = Button(self.frame1,text="Generate Report",
                        font="Ariel 12",command=self.generate_report)
        self.Report.grid(column=1,row=1,padx=10)

        # Entry box for the IP Address of the Power Switch
        self.power_ip = Entry(self.frame2,width=20,font="Ariel 18")
        self.power_ip.grid(row=0,column=0,padx=5,pady=5)

        self.power_ip_set = Button(self.frame2,text="Set",
                            font="Ariel 12",
                            command=self.write_ip_power_file)
        self.power_ip_set.grid(row=0,column=1,padx=5,pady=5)

        # Entry box for the IP adress of the Windows side
        self.windows_ip = Entry(self.frame3,width=20,font="Ariel 18")
        self.windows_ip.grid(row=0,column=0,padx=5,pady=5)

        self.windows_ip_set = Button(self.frame3,text="Set",
                                font="Ariel 12",
                                command=self.write_ip_windows)
        self.windows_ip_set.grid(row=0,column=1,padx=5,pady=5)

        ################# ComboBox for controlling which powershell script to call #################

        # Defining Tuple of variable options that will give the desired options
        self.switches = ("Power Switch 1", "Power Switch 2", "Power Switch 3", "Power Switch 4")

        # Defining the combobox that will have the selected_Power textvariable global variable
        self.Switch_selected = ttk.Combobox(frame4,width=20,font="Ariel 18",textvariable=selected_power)

        # Inserting the values in the Combobox
        self.Switch_selected["values"] = self.switches

        self.Switch_selected.grid(row=0,column=0,padx=5,pady=5)

        # Entry Box to get number of powercycles
        self.frame5 = Frame(root)
        self.frame5.pack(side="top",padx=5, pady=5)

        self.iteration_number = Label(self.frame5,text="Desired number of powercycles: ")
        self.iteration_number.grid(row=0, column=0)

        self.num_of_powercycles = IntVar()
        self.num_powercycles = Entry(self.frame5,width= 4,font="Ariel 18",textvariable=self.num_of_powercycles)
        self.num_powercycles.grid(row=0,column=1)

        self.infinite_button = Button(self.frame5,text='\u221e', font="Calibri 12",command=self.infinite_powershellcall)
        self.infinite_button.grid(row=0, column=2, padx=2)


    def write_ip_windows(self):

        self.IP_windows = self.windows_ip.get() 

        # Check that the IP adress is valid
        try:
            socket.inet_aton(self.IP_windows)
            self.f = open(r"IP_GSS_Win.txt","w")
            self.f.write(self.IP_windows)
            self.f.close()
            self.l_win = Label(self.frame3,font="Ariel 18",text=u'\u2713',
                        fg="Green")
            self.l_win.grid(row=0,column=2)

        except OSError:
            self.l_win = Label(self.frame3,font="Ariel 18",text=u'\u274C',
                        fg="Red")
            self.l_win.grid(row=0,column=2)  
            messagebox.showerror(title="IP adress not valid", 
                                message= f"""The entered IP power adress is {self.IP_windows}\n
    This is not valid! It should be in the format of nnn.nnn.nnn.nnn without spaces and with the points.""")


    # Functions to write IP address into text file
    def write_ip_power_file(self):

        self.IP_Power = self.power_ip.get() 

        # Check that the IP adress is valid
        try:
            socket.inet_aton(self.IP_Power)
            self.f = open(r"IP_Power.txt","w")
            self.f.write(self.IP_Power)
            self.f.close()
            self.l_power = Label(self.frame2,font="Ariel 18",text=u'\u2713',
                        fg="Green")
            self.l_power.grid(row=0,column=2)
        except OSError:

            self.l_power = Label(self.frame2,font="Ariel 18",text=u'\u274C',
                        fg="Red")
            self.l_power.grid(row=0,column=2)
            messagebox.showerror(title="IP adress not valid", 
                                message= f"""The entered IP power adress is {self.IP_Power}\n
    This is not valid! It should be in the format of nnn.nnn.nnn.nnn without spaces and with the points.""")

    # Function to generate report.txt
    def generate_report(self):

        global loops

        self.f = open(r"C:\results.txt", "w+")
        self.f.write("The number of successfull reboots were:")
        self.f.write("".format(loops))
        self.f.write("The time of the last event is:")
        self.f.write("".format(datetime.datetime.now()))
        self.f.close()

    # Function to stop the infinite function call
    def stop_infinitescript(self):

        global loops

        print(f"The script has done: {loops} amount of total power cycles")


    # "Infinite" function to run Powershell script
    def powershell_scripts(self):

        global loops
        global selected_power

        print("Starting Powershell Script!")

        print(selected_power.get())
    #     if selected_power.get() == "Power Switch 1":
    #         for x in range(0,self.num_of_powercycles.get()):
    #             subprocess.call(["powershell", ".\Script_Power_1.ps1"], stdout=sys.stdout)
    #             loops += 1

    #     elif selected_power.get() == "Power Switch 2":
    #         for x in range(0,self.num_of_powercycles.get()):
    #             subprocess.call(["powershell", ".\Script_Power_2.ps1"], stdout=sys.stdout)

    #     elif selected_power.get() == "Power Switch 3":
    #         for x in range(0,self.num_of_powercycles.get()):
    #             subprocess.call(["powershell", ".\Script_Power_3.ps1"], stdout=sys.stdout)

    #     elif selected_power.get() == "Power Switch 4":
    #         for x in range(0,self.num_of_powercycles.get()):
    #             subprocess.call(["powershell", ".\Script_Power_4.ps1"], stdout=sys.stdout)

    #     else:
    #         messagebox.showerror(title="No power switch selected",
    #         message="""Please select a power switch from the dropdown.\n
    # Otherwise the utility does not know which unit to power off.""")
    #         return
        
    #     print("Reboots completed: ",loops)

    # Inifinite Powershell Call
    def infinite_powershellcall(self):

        pass


#Creating an instance of the GUI Class
GUI(root)

# Entering the infinite loop 
root.mainloop()```

Thank you in advance for any precious help!
P.S. Any advice to improve this program are more than welcome :)
In the Powershell_script function, I am trying to call an object from the GUI, but I get that this variable does not exist as this function is called on a different thread to stop the GUI from freezing whilst the PowerShell scripts are running.

Answer: As mentioned by Matteo Gala, command=threading.Thread(target=self.powershell_scripts).start() will execute the powershell_scripts on initialization of the button. To remidy this define it as a lambda function as so command= lambda: threading.Thread(target=self.powershell_scripts).start().
Also it is bad practice to import * from a package because you could end up using an unexpected method. It is best to import only the methods you need from a given package, in your case from tkinter import Frame, Label, LabelFrame, Entry, Button, Combobox, Intvar. This may seem like a pain, but it will reduce the likelihood of you calling an unexpected method and will make your application conciderably lighter, specificly if you chose to compile it with Pyinstaller or Buildozer.