Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding notebook tabs in tkinter - how do I do it with a class-based structure? (Python 3)

I wrote a tkinter application that had widgets displayed on two frames, similar to this example, which successfully runs.

from tkinter import *
from tkinter import ttk

root = Tk()
root.title("Example")

notebook = ttk.Notebook(root)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="Frame One")
notebook.add(frame2, text="Frame Two")
notebook.pack()

#(The labels are examples, but the rest of the code is identical in structure). 

labelA = ttk.Label(frame1, text = "This is on Frame One")
labelA.grid(column=1, row=1)

labelB = ttk.Label(frame2, text = "This is on Frame Two")
labelB.grid(column=1, row=1)

root.mainloop()

I decided that I should try to restructure the program to use a class (which I'm admittedly not very familiar with). However, I'm unsure what I should do to allow the widgets to appear on different frames (everything else works okay). For instance, the following produces a "TypeError: init() takes from 1 to 2 positional arguments but 3 were given." So presumably I'd need to initialise with an extra argument, but I'm not sure how the notebook would be worked into that, or if that's even the approach I should be taking. (The program will run if the "frame1" and "frame2" arguments are removed from the labels, it will, however, display the same thing on both frames).

from tkinter import *
from tkinter import ttk

class MainApplication(ttk.Frame):
    def __init__(self, parent):
        ttk.Frame.__init__(self, parent)

        self.labelA = ttk.Label(self, frame1, text = "This is on Frame One")
        self.labelA.grid(column=1, row=1)

        self.labelB = ttk.Label(self, frame2, text = "This is on Frame Two")
        self.labelB.grid(column=1, row=1)

root = Tk()
root.title("Example")
notebook = ttk.Notebook(root)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="Frame One")
notebook.add(frame2, text="Frame Two")
notebook.pack()
MainApplication(root).pack()
root.mainloop()

I'm interested in a solution, but I'm also interested in learning what the class is doing differently compared to the standalone widgets.

like image 539
Plato's Cave Avatar asked Jul 10 '26 06:07

Plato's Cave


2 Answers

This would be one way to generalize the application as a class. You want to eliminate the repeated code.

from tkinter import *
from tkinter import ttk

class Notebook:

    def __init__(self,title):
        self.root = Tk()
        self.root.title(title)
        self.notebook = ttk.Notebook(self.root)

    def add_tab(self,title,text):
        frame = ttk.Frame(self.notebook)
        self.notebook.add(frame,text=title)
        label = ttk.Label(frame,text=text)
        label.grid(column=1,row=1)
        self.notebook.pack()

    def run(self):
        self.root.mainloop()

nb = Notebook('Example')
nb.add_tab('Frame One','This is on Frame One')
nb.add_tab('Frame Two','This is on Frame Two')
nb.run()
like image 96
Mark Tolonen Avatar answered Jul 17 '26 23:07

Mark Tolonen


I suggest you split the different Frames within notebook into separate files. I used from tab2 import * because I wanted this to remain in the namespace without adding the file/class prefix ie. I didn't want to write: tab1.Tab1(notebook)

main.py

import tkinter as tk
from tkinter import ttk

from tab1 import *
from tab2 import *    

class MainApplication(tk.Frame):
  def __init__(self, parent, *args, **kwargs):
    tk.Frame.__init__(self, parent, *args, **kwargs)

    notebook = ttk.Notebook(parent)

    Tab1frame = Tab1(notebook)
    Tab2frame = Tab2(notebook)

    notebook.add(Typ1frame, text='TAB1')
    notebook.add(Typ2frame, text='TAB2')
    notebook.pack()

if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

tab1.py

import tkinter as tk
from tkinter import ttk

class Typ14(tk.Frame):
  def __init__(self, parent, *args, **kwargs):
    tk.Frame.__init__(self, parent, *args, **kwargs)
    shell_frame=tk.LabelFrame(self, text="Sample Label Frame", padx=5,pady=5)
    shell_frame.grid(row=0,column=0,padx=5,pady=5)
like image 45
machaniv Avatar answered Jul 17 '26 23:07

machaniv