Home > Net >  How can I add custom Frame objects to custom ttk Notebook in Tkinter/python3?
How can I add custom Frame objects to custom ttk Notebook in Tkinter/python3?

Time:06-10

I am creating a Tkinter/Python3 application where the main window inherits from Notebook (i need tabs), and each tab should be a custom class inheriting from Frame (I would then dynamically use matplotlib to create custom graphs). Unfortunately I don't seem to be able to have Notebook accept my custom Frames. Following very reduced snippet of code:

#!/usr/bin/env python3

from tkinter import *
from tkinter.ttk import Notebook

class MyFrame1(Frame):
    def __init__(self, master=None, mytext=""):
        super().__init__(master)
        self.create_widgets(mytext)

    def create_widgets(self, mytext):
        self.label =  Label(self.master, text=mytext, anchor=W)
        # this is not placed relative to the Frame, but to the
        # master
        # 1. How I get the relative coordinates inside the frame
        #    to be 10, 10 of the frame area?
        self.label.place(x=10, y=10, width=128, height=24)

class MyNotebook(Notebook):
    def __init__(self, master=None):
        super().__init__(master)
        self.create_widgets()

    def create_widgets(self):
        self.f1 = MyFrame1(self, "abc")
        # once the UI is drawn, the label "def" seems to overlay
        # "abc" even when "f1" is selected
        # 2. Why is self.f2 always shown even when self.f1 is
        #    selected?
        self.f2 = MyFrame1(self, "def")
        self.add(self.f1, text="f1")
        self.add(self.f2, text="f2")
        # Without this command nothing gets drawn
        # 3. Why is this? Is this equivalent of 'pack' but for 
        #    pixel driven layout?
        self.place(width=640, height=480)

def main():
    root = Tk()
    root.minsize(640, 480)
    root.geometry("640x480")
    app = MyNotebook(master=root)
    # this works as intended the label is indeed placed
    # in the frame at 10, 10
    #app = MyFrame1(master=root, mytext="123abc")
    app.mainloop()
    return None

if __name__ == "__main__":
    main()

As per comments I have the following main question: why aren't my custom instances of MyFrame1 properly displayed inside MyNotebook?

Sub questions:

  1. How can I get relative coordinate areas of where the frame is located when place my elements (in this case a Label)?
  2. Why even when self.f1 tab is selected in the UI, I can still see the content of self.f2 tab?
  3. Is self.place required in order to show all sub-elements when not using pack?
  4. If I dynamically create Tkinter elements after the MyNotebook is initialized, will those be bound to respective tabs?

Not sure what I'm doing wrong?

Thanks!

CodePudding user response:

Not sure what I'm doing wrong?

Your create_widgets method needs to add widgets to self, not self.master.

How can I get relative coordinate areas of where the frame is located when place my elements (in this case a Label)?

I don't understand what you mean by this. When you use place, coordinates will be interpreted relative to the frame. However, I strongly advise against using place. Both pack and grid will trigger the frame to resize to fit its children which almost always results in a more responsive UI

Why even when self.f1 tab is selected in the UI, I can still see the content of self.f2 tab?

Because you added internal widgets to self.master instead of self.

Is self.place required in order to show all sub-elements when not using pack?

No. It is required to use a geometry manager but it doesn't have to be place. Usually, place is the least desirable geometry manager to use. pack and grid are almost always better choices except for some very specific situations.

If I dynamically create Tkinter elements after the MyNotebook is initialized, will those be bound to respective tabs?

They will be in whatever tab you put them in.


Finally, I would suggest that you remove self.place in create_widgets. Instead, call pack, place, or grid in the same block of code that creates an instance of that class.

It's a bad practice for a widget to add itself to another widget's layout. The code that creates the widget should be the code that adds the widget to the layout.

  • Related