0

I am creating a GUI using tkinter based on the structure described here. I have some tabs that look identical but with different variables. So I decided to define a class for tabs and add them to the main window. I am going to configure some widgets in one tab from another tab. In line 11, a function is defined that when a button in tab_2 is clicked, tab_1's button background color changes to green. Whereas its working, I have two question:

  1. Is it possible not to define channel_1 as an attribute of main_window? I think there must be better way to do so, specifically, if the GUI is going to be used as module (then main_window will not be defined).

  2. Is it possible to know which tab is open, so when button in each tab is clicked, configurations in the other one changes only?

import tkinter as tk
from tkinter import ttk

class Channel(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        self.btn = tk.Button(self.parent, text = 'click me', command = self.change_green)
        self.btn.pack()
    def change_green(self):
        main_window.channel_1.btn.config(bg = 'green') # line 11


class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        self.tab_control = ttk.Notebook(self.parent)
        self.tab_1 = ttk.Frame(self.tab_control)
        self.tab_2 = ttk.Frame(self.tab_control)
        self.tab_control.add(self.tab_1, text = 'tab 1')
        self.tab_control.add(self.tab_2, text = 'tab 2')
        self.tab_control.pack(fill = 'both', expand = 1)

        self.channel_1 = Channel(self.tab_1)
        self.channel_2 = Channel(self.tab_2)

if __name__ == "__main__":
    root = tk.Tk()
    main_window = MainApplication(root)    # <<<< here defined main_window
    main_window.pack(side="top", fill="both", expand=True)
    root.mainloop()
Khani
  • 21
  • 7
  • if you add first channel as parameter in second channel then it may have access without main_window - `Channel(self.tab_2, self.channel_1)` The same way you could connect other channels. – furas Jul 21 '19 at 14:22
  • Maybe instead of channels you would create class `MyTab` and gives first tab as parameter in second tab. Then Notebook should also gives you info which tab is active – furas Jul 21 '19 at 14:27
  • @furas But `change_green` is a method of `Channel`, `self.tab_2` is a Frame object without methods and attributes needed for the task. – Khani Jul 21 '19 at 14:33
  • as for me you should create class MyTab instead of Channel. And every tab should have all its widgets in MyTab, not in external class Channel. – furas Jul 21 '19 at 14:40

1 Answers1

1

I would create class MyTab and keep its widgets in this class, not in channel. It can also keep access to other tab(s) to button in one tab can change color in other tab.

Using tab's parent (self.master) I can get active tab, list of all tabs and activate other tab.

import tkinter as tk
from tkinter import ttk


class MyTab(tk.Frame):

    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        #self.master = master #  super() already set it

        self.btn = tk.Button(self, text='click me', command=self.change_green)
        self.btn.pack()

        self.other_tab = None # default value at start

    def change_green(self):
        if self.other_tab:

            # change color in other tab
            self.other_tab.btn.config(bg = 'green')

            # get active tab ID
            print('active tab ID:', self.master.select())

            # get button in active tab
            active_tab = root.nametowidget(self.master.select())
            print('active tab - btn text:', active_tab.btn['text'])

            # get all tabs
            print('all tabs:', self.master.children.items())

            # set other tab as active
            self.master.select(self.other_tab)

class MainApplication(tk.Frame):

    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        #self.master = master #  super() already set it

        self.tab_control = ttk.Notebook(self.master)

        self.tab_1 = MyTab(self.tab_control)
        self.tab_2 = MyTab(self.tab_control)

        self.tab_1.other_tab = self.tab_2
        self.tab_2.other_tab = self.tab_1

        self.tab_control.add(self.tab_1, text = 'tab 1')
        self.tab_control.add(self.tab_2, text = 'tab 2')
        self.tab_control.pack(fill = 'both', expand = 1)

if __name__ == "__main__":
    root = tk.Tk()
    main_window = MainApplication(root)
    main_window.pack(side="top", fill="both", expand=True)
    root.mainloop()
furas
  • 134,197
  • 12
  • 106
  • 148
  • Thank you very much. The reason you could change `tab_1 = ttk.Frame(tab_control)` with `tab_1 = MyTab(tab_control)` is because `MyTab` inherits from `tk.Frame`? Thats the reason @Brian Oakley suggested to inherit so? – Khani Jul 21 '19 at 15:44
  • you can use any widget as tab - `tab_3 = tk.Button(...)` - so I could use any widget to create `MyTab` but `Frame` seems the most useful to create own widget which will keep many other widgets. – furas Jul 21 '19 at 15:51