Search code examples
pythontkinter

Update entry widget value that has StringVar based on another StringVar value inside new window


so like the title says i want to update one StringVar variable based on value from another StringVar variable in tkinter that are in widow separate from the initial one as you will see. I know the code will be messy but bear with it. Initially we create couple of buttons that generate sql scripts that i hardcoded. When i press a button a new window opens where i enter information about a database name, table name, column name... When i enter correct table name in entry2 widget it prints correct columns from the database in function show_columns in string variable str. Let's assume the database name is DATABASE. What i want is to put content of str variable into column variable that's binded to entry3 widget. How do i do that?

def show_columns(table):
    query = f"select * from [DATABASE].[dbo].[{table}] where 1=0"
    cursor = conn.cursor()
    str = ''
    try:
        cursor.execute(query)
        column_names = [column[0] for column in cursor.description]
        for column in column_names: str += f"[{column}],"
        print(str)
    except:
        print('Error!')



def open_window(text, button):
    # Create a new window
    new_window = Toplevel()

    # Set the window title and size
    new_window.title(text)
    new_window.geometry("600x460")
    new_window.config(bg="light gray")
    new_window.resizable(False, False)

    database = StringVar()
    table = StringVar()
    column = StringVar()


    # Create some text fields
    label1 = Label(new_window, text="Database name:").grid(row=0, column=0, pady=5)
    entry1 = Entry(new_window, textvariable=database).grid(row=0, column=1, padx=5, pady=5)

    label2 = Label(new_window, text="Table name:").grid(row=1, column=0, pady=5)
    entry2 = Entry(new_window, textvariable=table).grid(row=1, column=1, padx=5, pady=5)

    label3 = Label(new_window, text="Column name:").grid(row=2, column=0, pady=5)
    entry3 = Entry(new_window, textvariable=column).grid(row=2, column=1, padx=5, pady=5)
    
    table.trace_add('write', lambda name, index, mode, sv=table: show_columns(sv.get()))




window = Tk()

#code for connecting to database


#Set the window title, size and oclor
window.title('SQL script generator')
window.geometry("800x600")
window.config(bg="gray")
window.resizable(False, False)

command_names = ['Delete duplicates while keeping the original', 
                 'Delete duplicates without keeping the original',
                 'Retrive all data from table',
                 'Check for duplicates',
                 'Delete table',
                 'Copy data from one table to another',
                 'Update values',
                 'Add new column to exisitng table']


canvas = Canvas(window)
canvas.pack(side='left', fill='both', expand=True)


# Create two frames
search_frame = Frame(canvas)
search_frame.pack(side='top')
buttons_frame = Frame(canvas)
buttons_frame.pack(side='bottom')
canvas.create_window((0, 0), window=buttons_frame, anchor='nw')


i = 0
for i, text in enumerate(command_names):
    Button(buttons_frame, text=command_names[i], width=40, height=2, command=lambda text = text, index = i: open_window(text, index)).grid(row=i+1, column=0, pady=5, padx=5)
    i += 1


buttons_frame.update_idletasks()
window.mainloop()

Solution

  • I took the liberty of translating your code into an OOP approach. In my opinion, this is easier to read, and you can use instance variables to exchange values between methods within a class without passing them directly. For example, you could introduce the variables database, table and column within __init__ with the prefix self and then within show_columns simply set the variable with self.column.set(str).

    Attention, in my code I have replaced str with output, as str is already used in the standard (type). This version is shown in the method show_columns_v2.

    You can also pass entry3 into the show_columns method and then insert the result into the entry using the insert method. Note here that you have assigned the variable entry3 with the return value of the grid method, not with the entry. In my code I fixed this by first creating entry3 and then applying the grid method to it.

    Note: Since I don't have your database, I have set the variable or inserted the value into the entry in the except bracket.

    Here is my code:

    from tkinter import Tk, Toplevel, Label, Entry, Canvas, Frame, Button, StringVar
    
    
    class GUI(Tk):
        def __init__(self):  # create own GUI class which inherits from Tk → window is now self
            super().__init__()
            # Set the window title, size and oclor
            self.title('SQL script generator')
            self.geometry("800x600")
            self.config(bg="gray")
            self.resizable(False, False)
    
            command_names = ['Delete duplicates while keeping the original',
                             'Delete duplicates without keeping the original',
                             'Retrive all data from table',
                             'Check for duplicates',
                             'Delete table',
                             'Copy data from one table to another',
                             'Update values',
                             'Add new column to exisitng table']
    
            canvas = Canvas(self)
            canvas.pack(side='left', fill='both', expand=True)
    
            # Create two frames
            search_frame = Frame(canvas)
            search_frame.pack(side='top')
            buttons_frame = Frame(canvas)
            buttons_frame.pack(side='bottom')
            canvas.create_window((0, 0), window=buttons_frame, anchor='nw')
    
            # Create instance variables
            self.database = StringVar(self, value='')
            self.table = StringVar(self, value='')
            self.column = StringVar(self, value='')
    
            # Create Buttons with loop
            for i, text in enumerate(command_names):
                Button(buttons_frame, text=command_names[i], width=40, height=2,
                       command=lambda: self.open_window(text)).grid(row=i + 1, column=0, pady=5, padx=5)
    
        def open_window(self, text):
            # Create a new window
            new_window = Toplevel()
    
            # Set the window title and size
            new_window.title(text)
            new_window.geometry("600x460")
            new_window.config(bg="light gray")
            new_window.resizable(False, False)
    
            # Create some text fields
            Label(new_window, text="Database name:").grid(row=0, column=0, pady=5)
            entry1 = Entry(new_window, textvariable=self.database)
            entry1.grid(row=0, column=1, padx=5, pady=5)
    
            Label(new_window, text="Table name:").grid(row=1, column=0, pady=5)
            entry2 = Entry(new_window, textvariable=self.table)
            entry2.grid(row=1, column=1, padx=5, pady=5)
    
            Label(new_window, text="Column name:").grid(row=2, column=0, pady=5)
            entry3 = Entry(new_window, textvariable=self.column)
            entry3.grid(row=2, column=1, padx=5, pady=5)
    
            #self.table.trace_add('write', lambda name, index, mode, entry=entry3: self.show_columns(sv.get(), entry))
            # version to set by changing self.column directly
            self.table.trace_add('write', lambda name, index, mode: self.show_columns_v2())
    
        # version without instance vaiable by inserting to entry directly
        def show_columns(self, entry3):
            table = self.table.get()
            query = f"select * from [DATABASE].[dbo].[{table}] where 1=0"
            output = ''
            try:
                cursor = conn.cursor()
                cursor.execute(query)
                column_names = [column[0] for column in cursor.description]
                for column in column_names:
                    output += f"[{column}],"
                print(output)
            except:
                print(f'Error for Table {table}!')
                # test to set entry
                output = 'test'
                entry3.insert(0, output)
    
        # version 2 setting StringVar directly
        def show_columns_v2(self):
            table = self.table.get()
            query = f"select * from [DATABASE].[dbo].[{table}] where 1=0"
            output = ''
            try:
                cursor = conn.cursor()
                cursor.execute(query)
                column_names = [column[0] for column in cursor.description]
                for column in column_names:
                    output += f"[{column}],"
                print(output)
            except:
                print(f'Error for Table {table}!')
                # test to set entry
                output = 'test'
                self.column.set(output)
    
    
    if __name__ == '__main__':
        gui = GUI()
        gui.mainloop()
    
    

    By the way: maybe you should not trigger the show_columns method via table.trace_add('write'..., unless you copy the table name into this field. Because if you type the name, the function is triggered with every letter. Instead, you can also insert a button such as 'Search columns', which then triggers the show_columns method.