-1

I would like to click a button and view its content on the rest of the screen (where the gray color is), but through the frame, not across the canvas.

I would need 2 examples, but apply to my code please:

  • example 1: click on button 1 and color the page (as in the classic use in these frames) with the code written in the same single file
  • example 2: click on the button and import an external py file as a module, then the contents of the external file will open completely inside the gray screen

I would need these two simple examples, because for each frame (therefore for each button) I will have a long enough code and I would like to manage it in an orderly and clean way. So I would like to test both examples, but applied to my code.

I've seen examples on the web and on StackOverflow before, but I couldn't apply myself to my code. Can you please help me use my code?

(my code below)

enter image description here

from tkinter import messagebox
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image

root = tk.Tk()
root.title("xxxx")
root.geometry("1920x1080+0+0")
root.config(bg="#f0f0f0")
root.state("normal")

topbar = tk.Frame(root, background="#e10a0a", height=43)
topbar.pack(fill='x') # use pack() instead of place()

leftbar = tk.Frame(root, width=250, background="white")
leftbar.pack(side='left', fill='y') # use pack() instead of place()
leftbar.pack_propagate(0) # disable size auto-adjustment

def clicked(btn):
    for w in leftbar.winfo_children():
        w['bg'] = 'white' if w is not btn else 'yellow'

button1 = tk.PhotoImage(file="/image.png")
btn1 = tk.Button(leftbar, image=button1, borderwidth=0, highlightthickness=0,
                 bg="white", text="aaaaa", foreground='green', compound='left', anchor='w')
btn1.pack(fill='x') # use pack() instead of place()
btn1['command'] = lambda: clicked(btn1)

button2 = tk.PhotoImage(file="/image.png")
btn2 = tk.Button(leftbar, image=button2, borderwidth=0, highlightthickness=0,
                 bg="white", text="bbbbb", foreground='green', compound='left', anchor='w')
btn2.pack(fill='x') # use pack() instead of place()
btn2['command'] = lambda: clicked(btn2)

root.mainloop()
Erling Olsen
  • 1
  • 4
  • 15

2 Answers2

2

I would suggest you create a separate class by subclassing tk.Frame and implement the logic of tab change there.

import tkinter as tk
from PIL import ImageTk, Image

class TabBar(tk.Frame):

    def __init__(self, *args, **kwargs):
        super(TabBar, self).__init__(*args, **kwargs)

        self.side_bar = tk.Frame(self, background="white")
        # self.side_bar.place(relx=0, rely=0, relheight=1, relwidth=0.2)
        self.side_bar.pack(side="left", fill="both")

        self.tabs = {}
        self.active_tab = None

        self.active_color = "yellow"
        self.default_color = "grey"

    def addTab(self, name, widget, image: ImageTk.PhotoImage=None):
        
        btn = tk.Button(self.side_bar, text=name, image=image, 
                    compound="left", command=lambda: self.setActive(name),
                    bg=self.default_color, width=20, relief="flat")
        btn.pack(fill="x", anchor="n")

        self.tabs[name] = [btn, widget]
        
        if self.active_tab is None:
            self.setActive(name)
    
    def setActive(self, name):
        if self.active_tab:
            # self.tabs[self.active_tab][1].place_forget()
            self.tabs[self.active_tab][1].pack_forget()
            self.tabs[self.active_tab][0].config(bg=self.default_color)
        
        self.tabs[name][0].config(bg=self.active_color)
        # self.tabs[name][1].place(relx=0.2, rely=0, relheight=1, relwidth=1)
        self.tabs[name][1].pack(side="left", expand=True, fill="both")
        
        self.active_tab = name

    def removeTab(self, name):
        self.tabs.pop(name)

In the above code, you can add tabs by using the addTab method, the name parameter should be unique for each tab as the name is being used as the key in the dictionary. Optionally you can pass PhotoImage instance to image parameter to display the image.

You can put the above class in the same file or a different file and import it.

usage example:

root = tk.Tk()
root.config(bg="#f0f0f0")

imag = Image.open("image.png")
imagtk = ImageTk.PhotoImage(imag)

tab_bar = TabBar(root)
tab_bar.pack(expand=True, fill="both")


frame = tk.Frame(tab_bar)
button = tk.Button(frame, text="Click Me")
button.pack()

tab_bar.addTab("aaaa", tk.Label(tab_bar, text="contents"), imagtk)
tab_bar.addTab("bbbb", frame)

root.mainloop()

(note: You need to pass tab_bar as parent to your widgets the widgets that you want to place inside the tab)

Output:

enter image description here

Art
  • 2,836
  • 4
  • 17
  • 34
  • Thank you very much for the reply. I tried quickly and I get the NameError: name 'ImageTk' is not defined. Now I'm going to sleep when it's late, then I'll better read your answer tomorrow. However I didn't want buttons like you used them, but I wanted something more graphically modern like I did in the code. Can the effect of the button be removed? That is, use something that doesn't look like a button? I read tomorrow calmly, Thanks – Erling Olsen Nov 24 '21 at 00:08
  • @heovan1999 You just need to import ImageTk from PIL and you can always customize the buttons, by adding relief styles, backgrounds etc. – Art Nov 24 '21 at 01:29
1

You can use a ttk.Notebook to achieve what you want:

import tkinter as tk
from tkinter import ttk
from PIL import ImageTk

# define classes for each page

class Page1(tk.Frame):
    def __init__(self, master, **kw):
        super().__init__(master, **kw)
        tk.Label(self, text='Hello', font='Arial 64 bold').pack(fill='both', expand=1)

class Page2(tk.Frame):
    def __init__(self, master, **kw):
        super().__init__(master, **kw)
        tk.Label(self, text='Python is awesome', font='Times 24 bold', bg=self['bg']).pack()
        self.logo = ImageTk.PhotoImage(file='images/python-logo.png')
        tk.Label(self, image=self.logo, bg=self['bg']).pack(fill='both', expand=1)

root = tk.Tk()
root.geometry('640x480')

topbar = tk.Frame(root, bg='#e10a0a', height=43)
topbar.pack(fill='x')

style = ttk.Style()
style.theme_use('default') # select a theme that allows configuration of ttk.Notebook
# put the tabs at the left with white background
style.configure('TNotebook', tabposition='wn', background='white', tabmargins=0)
# configure tab with white background initially, yellow background when selected
style.configure('TNotebook.Tab', background='white', width=10, focuscolor='yellow', borderwidth=0)
style.map('TNotebook.Tab', background=[('selected', 'yellow')])

nb = ttk.Notebook(root)
nb.pack(fill='both', expand=1)

img = ImageTk.PhotoImage(file='images/div2.png')

page1 = Page1(nb)
page2 = Page2(nb, bg='pink', bd=0)

nb.add(page1, text='aaaaa', image=img, compound='left')
nb.add(page2, text='bbbbb', image=img, compound='left')

root.mainloop()

Output:

enter image description here


You can also put the class definitions of each page to external files, for example Page1 in page1.py and Page2 in page2.py. Then import them into main script:

import tkinter as tk
from tkinter import ttk
from PIL import ImageTk
from page1 import Page1
from page2 import Page2

root = tk.Tk()
root.geometry('640x480')

topbar = tk.Frame(root, bg='#e10a0a', height=43)
topbar.pack(fill='x')

style = ttk.Style()
style.theme_use('default') # select a theme that allows configuration of ttk.Notebook
# put the tabs at the left with white background
style.configure('TNotebook', tabposition='wn', background='white', tabmargins=0)
# configure tab with white background initially, yellow background when selected
style.configure('TNotebook.Tab', background='white', width=10, focuscolor='yellow', borderwidth=0)
style.map('TNotebook.Tab', background=[('selected', 'yellow')])

nb = ttk.Notebook(root)
nb.pack(fill='both', expand=1)

img = ImageTk.PhotoImage(file='images/div2.png')

page1 = Page1(nb)
page2 = Page2(nb, bg='pink', bd=0)

nb.add(page1, text='aaaaa', image=img, compound='left')
nb.add(page2, text='bbbbb', image=img, compound='left')

root.mainloop()

page1.py

import tkinter as tk

class Page1(tk.Frame):
    def __init__(self, master, **kw):
        super().__init__(master, **kw)
        tk.Label(self, text='Hello', font='Arial 64 bold').pack(fill='both', expand=1)

page2.py

import tkinter as tk
from PIL import ImageTk

class Page2(tk.Frame):
    def __init__(self, master, **kw):
        super().__init__(master, **kw)
        tk.Label(self, text='Python is awesome', font='Times 24 bold', bg=self['bg']).pack()
        self.logo = ImageTk.PhotoImage(file='images/python-logo.png')
        tk.Label(self, image=self.logo, bg=self['bg']).pack(fill='both', expand=1)
acw1668
  • 40,144
  • 5
  • 22
  • 34