0

This is a problem with creating a tkinter notebook in a method in a class.

This question is solved by removing master, which is not my problem: Python Tkinter - How add a notebook class into a tk.toplevel?.

This question is about adding images, and it doesn't have the same structure (from which I might have been able to infer the solution): Python Tkinter Notebook widget.

When I create the notebook from __init__, the notebook opens just fine.

import tkinter as tk
from tkinter import ttk

class Stakeholders(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
    
        notebook = ttk.Notebook(self.master)
        notebook.pack(pady=10, expand=True)

        # create frames
        frame1 = ttk.Frame(notebook, width=400, height=280)
        frame2 = ttk.Frame(notebook, width=400, height=280)
        
        frame1.pack(fill='both', expand=True)
        frame2.pack(fill='both', expand=True)
        
        # add frames to notebook
        notebook.add(frame1, text='Tab 1')
        notebook.add(frame2, text='Tab 2')

def create_window():
    root = tk.Tk()
    root.geometry('400x300')
    root.title('Stakeholders')
    
    Stakeholders(root)
    root.mainloop()
    
if __name__ == "__main__":
    create_window()

When I put the notebook in a method of the class, the window opens but there is no notebook, no tabs.

import tkinter as tk
from tkinter import ttk

class Stakeholders(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.make_notebook()
        
    def make_notebook(self):
        self.notebook = ttk.Notebook(self)
        self.notebook.pack(pady=10, expand=True)
        
        frame1 = ttk.Frame(self.notebook, width=300, height=300)
        frame2 = ttk.Frame(self.notebook, width=300, height=300)
        
        frame1.pack(fill='both', expand=True)
        frame2.pack(fill='both', expand=True)

        self.notebook.add(frame1, text='Owner')
        self.notebook.add(frame2, text='Architect')

def create_window():
    root = tk.Tk()
    root.geometry('400x300')
    root.title('Stakeholders')
    
    Stakeholders(root)
    root.mainloop()
    
if __name__ == "__main__":
    create_window()
8
  • 1
    You need to call a layout function on instance of the class Stakeholders for your second code. Commented May 23, 2024 at 23:28
  • Doesn't .pack() layout where each Frame and the Notebook goes? Commented May 23, 2024 at 23:34
  • 1
    For your second example, the notebook is created as child of Stakeholder, but instance of Stakeholder is invisible because no layout function is called on it. Commented May 24, 2024 at 0:05
  • Why is notebook created as a child of Stakeholder? It's created in a method of the class. It's not a subclass or a child class. Commented May 24, 2024 at 0:21
  • 1
    self.master is the parent of self. It is the different. Commented May 24, 2024 at 1:03

1 Answer 1

1

You were very close, but need a few minor changes

import tkinter as tk
from tkinter import ttk


class Stakeholders(tk.Frame):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        notebook = ttk.Notebook(self)  # you want 'self' here, not self.master
        notebook.pack(pady=10, expand=True)

        # create frames
        frame1 = ttk.Frame(notebook, width=400, height=280)
        frame2 = ttk.Frame(notebook, width=400, height=280)

        frame1.pack(fill='both', expand=True)
        frame2.pack(fill='both', expand=True)

        # add frames to notebook
        notebook.add(frame1, text='Tab 1')
        notebook.add(frame2, text='Tab 2')


def create_window() -> None:
    root = tk.Tk()
    root.geometry('400x300')
    root.title('Stakeholders')
    
    # instantiate your Stakeholders class
    stakeholders = Stakeholders(root)
    # add it to the UI via 'pack' or another layout manager (I tend to prefer pack)
    stakeholders.pack(expand=True, fill='both')
     
    root.mainloop()


if __name__ == "__main__":
    create_window()
  1. You generally want your widgets to be children of the class they're in (i.e. master=self) rather than children of that class's parent (i.e. master=self.master)

  2. Calling a layout manager (e.g. pack, grid, or place) on your Stakeholders instance was the real missing piece - this is what adds it to the UI

    2a. Note that you could also use Stakeholders(root).pack(...). But it's a good idea to get in the habit of instantiating your widgets separately from layout management, because pack, grid, and place all return None. This means that when you do something like stakeholders = Stakeholders(root).pack(), stakeholders evaluates to None (and not your widget!) which can cause headaches down the line if you're not careful

Sign up to request clarification or add additional context in comments.

2 Comments

2a is really good advice. I hadn't realized that. Thanks. I noticed that you removed the method def make_notebook(self), and put those contents in the __init__. Why is that? What's the benefit of that move?
@DavidCollins it trips up a lot of people coming to tkinter, so I figured it was worth mentioning! Also, most of this code if from your 1st example, so I just worked with that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.