Search code examples
pythontkinterttk

Accessing variables from other tab in notebook


Tab1 and Tab2 of the notebook

Hi all, I was unsuccessful with my last question in this group because my code was too long. I have now brought my code to a minimum and added a screenshot. I hope this time everything is clear and not confusing. My question is: how can I check the variable "val" in Tab1 whether it is smaller than 5. If not, the frame and button in Tab1 must be gray and disabled. The button must not do anything. The problem is that I have defined "val" as global, but it still brings the error message that "val" is unknown. Any idea why "val" is not known in Tab1?

# Filename: Tab1.py
from tkinter import *

def gen_t1(frame):
    global val
  
    f = LabelFrame(frame, text='f', bg='lightgreen')
    f.pack(expand=True, fill='both')

    b1 = Button(f, text='B1').pack()

##    if val < 5:
##        f_enabled = 'disabled'
##        f['bg'] = 'lightgray'
##    else:
##        f_enabled = 'normal'
##        f['bg'] = 'lightgreen'

# Filename: Tab2.py
from tkinter import *

def gen_t2(frame):
    def getValue(event):
        global val
        val = ent.get()
        print(val)

    lbl = Label(frame, text='Val').pack()

    ent = Entry(frame)
    ent.pack()
    ent.insert(0, '2')
  
    ent.bind('<Return>', getValue)

# Filename: TabTop
from tkinter import *
from tkinter import ttk

from Tab1 import gen_t1
from Tab2 import gen_t2

firstTime1 = 1
firstTime2 = 1
root = Tk()
root.title('Tab-Tester')
root.geometry('300x200')

nb = ttk.Notebook(root)
tab1 = Frame(nb)
tab2 = Frame(nb)
  
nb.add(tab1, text ='Tab 1')
nb.add(tab2, text ='Tab 2')
nb.pack(expand = 1, fill ="both")

def on_tab_change(event):
    global firstTime1, firstTime2
  
    tab = event.widget.tab('current','text')
    if tab == 'Tab 1':
        print('Tab 1')
        if firstTime1 == 1:
            gen_t1(tab1)
            firstTime1 = 0
    elif tab == 'Tab 2':
        print('Tab 2')
        if firstTime2 == 1:
            gen_t2(tab2)
            firstTime2 = 0
nb.bind('<<NotebookTabChanged>>', on_tab_change)

root.mainloop()

Solution

  • I think you should use the object-oriented way to build your GUI. From what I understood of your situation, here's my suggested solution:

    Tab1.py

    import tkinter as tk
    from tkinter import ttk
    
    class Button_page(ttk.Frame):
        """This will contain what is going to be shown on the first tab."""
    
        def __init__(self, variable_to_check, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            self.variable_to_check = variable_to_check
            self.variable_to_check.trace('w', lambda *args: self.update_background())
    
            self.columnconfigure(0, weight=1)
            self.rowconfigure(0, weight=1)
    
            self.internal_style = ttk.Style()
            self.internal_style.configure('Tab1.TLabelframe', background='lightgreen')
            self.internal_style.configure('Tab1.TLabelframe.Label', background='lightgreen')
    
            self.f = ttk.LabelFrame(self, text='f', style='Tab1.TLabelframe')
            self.f.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))
            self.f.columnconfigure(0, weight=1)
            self.f.rowconfigure(0, weight=0)
    
            self.b1 = ttk.Button(self.f, text='B1')
            self.b1.grid(column=0, row=0, sticky=(tk.N,))
    
        def update_background(self):
            new_value_string = self.variable_to_check.get()
            try:
                new_value = int(new_value_string)
            except (ValueError):
                new_value = 0
    
            if new_value < 5:
                self.internal_style.configure('Tab1.TLabelframe', background='lightgray')
                self.internal_style.configure('Tab1.TLabelframe.Label', background='lightgray')
                self.b1.config(state=tk.DISABLED)
            else:
                self.internal_style.configure('Tab1.TLabelframe', background='lightgreen')
                self.internal_style.configure('Tab1.TLabelframe.Label', background='lightgreen')
                self.b1.config(state=tk.NORMAL)
               
    

    Tab2.py

    import tkinter as tk
    from tkinter import ttk
    
    class Entry_page(ttk.Frame):
        """This will contain what is going to be shown on the second tab."""
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            self.entry_value = tk.StringVar()
    
            self.columnconfigure(0, weight=1)
            self.rowconfigure(0, weight=0)
            self.rowconfigure(1, weight=0)
    
            value_label = ttk.Label(self, text='Val')
            value_label.grid(column=0, row=0)
    
            value_entry = ttk.Entry(self, textvariable=self.entry_value)
            value_entry.grid(column=0, row=1)
    

    TabTop.py

    import tkinter as tk
    from tkinter import ttk
    
    import Tab1
    import Tab2
    
    root = tk.Tk()
    root.title('Tab-Tester')
    root.geometry('300x200')
    
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)
    
    nb = ttk.Notebook(root)
    t2 = Tab2.Entry_page(nb)
    t1 = Tab1.Button_page(variable_to_check=t2.entry_value, master=nb)
    t2.entry_value.set(2)
    
    nb.add(t1, text='Tab 1')
    nb.add(t2, text='Tab 2')
    nb.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))
    nb.columnconfigure(0, weight=1)
    nb.rowconfigure(0, weight=1)
    
    root.mainloop()
    

    To synchronize the information between the two tabs, you need a tkinter variable instance that you connect between the ttk.Entry and a callback function that will be triggered when the content of the ttk.Entry's is changed.

    I know there's not a lot of documentation on tkinter. I would recommend :


    We could go even more object-oriented:

    TabTop2.py

    import tkinter as tk
    from tkinter import ttk
    
    import Tab1
    import Tab2
    
    class Tab_pages_application(tk.Tk):
    
        def __init__(self, title, size, value_page, action_page, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            self.title(title)
            self.geometry(size)
    
            self.columnconfigure(0, weight=1)
            self.rowconfigure(0, weight=1)
    
            nb = ttk.Notebook(self)
            t2 = value_page(nb)
            t1 = action_page(variable_to_check=t2.entry_value, master=nb)
            t2.entry_value.set(2)
    
            nb.add(t1, text='Tab 1')
            nb.add(t2, text='Tab 2')
            nb.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))
            nb.columnconfigure(0, weight=1)
            nb.rowconfigure(0, weight=1)
    
    if __name__ == '__main__':
        root = Tab_pages_application('Tab-Tester', '300x200', Tab2.Entry_page, Tab1.Button_page)
        root.mainloop()
    

    Is that what your tried to do?