Python/Tkinter: fine points about super init and self

Question:

Following is very simple code to create a frame and label as a class:

class FrameWithLabel(tk.Frame):
    def __init__(self, parent):
        super().__init__(self, parent)
        self.label = tk.Label(self)

I have two questions:

  1. In an earlier post, it was explained to me why the super init was required (in this case, referring to the tk.Frame), and that without the super init, the Frame widget would not be correctly initialized. If that’s the case, then why can the Label simply be passed in as self.label without a similar super init type initialization of a tk Label? There is obviously something I am still not grasping about super init.

  2. This is a very minor point, but I don’t like the way that referring to the Frame and Label are asymmetrical. That is, if I wanted to change an attribute of the frame from the main app, I would use FrameWithLabel.configure(bg="red"). But if I wanted to change the label, I would have to use FrameWithLabel.label.configure(bg="red"). I would rather that the Frame and Label occupied the same level in the code so that I would be referring to FrameWithLabel.frame and FrameWithLabel.label. In that case, the class would have to be initialized with something other than the Frame widget. If that’s possible, what would that element be? I have tried "class SomeClassName(tk):" and it doesn’t work. Also, if there is an element that could be used in this way, would a super init line be required?

My apologies if question 2 seems trivial, since the above code works just fine, but I’m really trying to drill down to how all of this stuff works under the hood, and make my classes as intuitive and user-friendly as possible. Any advice appreciated.

Asked By: fmex

||

Answers:

why can the Label simply be passed in as self.label without a similar super init type initialization of a tk Label?

It is because you aren’t subclassing the Label class. The label you create in __init__ is just a normal label. You only need to call super when you’re creating a subclass of a widget.

if I wanted to change the label, I would have to use FrameWithLabel.label.configure(bg="red"). I would rather that the Frame and Label occupied the same level in the code so that I would be referring to FrameWithLabel.frame and FrameWithLabel.label.

This question is unclear. You do refer to the label with FrameWithLabel.label. It’s not clear what you want instead.

If you want to be able to do something like frame_with_label.configure(label="the label") then you’ll have to create a custom configure method. It might look something like this:

class FrameWithLabel(tk.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        self.label = tk.Label(self)
        self.label.pack(padx=10, pady=10)

    def configure(self, **kwargs):
        if "label" in kwargs:
            value = kwargs.pop("label")
            self.label.configure(text=value)

        # call the built-in configure method for the other arguments
        super().configure(**kwargs)

With the above, you can set the label like this:

labelframe = FrameWithLabel(root)
labelframe.configure(label="Hello, world")
Answered By: Bryan Oakley
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.