Home > OS >  Why do the tkinter geometry manager methods return None instead of returning the widget on which the
Why do the tkinter geometry manager methods return None instead of returning the widget on which the

Time:01-10

EDIT: I have submitted this as a feature improvement proposal via the GitHub issue tracker for cpython. See Issue 100891.


N.B.: I recognize that this question may border on "opinion-based" - if there is a better venue for this discussion, please let me know and I'll remove it. I appreciate your candor!

It seems like an extremely common pitfall for newcomers to tkinter to do the following:

my_button = tkinter.Button(text='Hello').pack()

only to run into errors because my_button evaluates to None after being chained to a geometry manager method, i.e. pack, place, or grid.

Typical practice for this reason is to declare widgets separately from adding them to a geometry manager, e.g.:

my_button = tkinter.Button(text='Hello')
my_button.pack()

A quick look into the source code for the geometry manager classes shows that it would be an extremely trivial change to return the widget on which they're called. The same line can be added to each geometry manager's respective _configure method: return self (I have done so below)

pack

class Pack:
    """Geometry manager Pack.
    Base class to use the methods pack_* in every widget."""

    def pack_configure(self, cnf={}, **kw):
        """Pack a widget in the parent widget. Use as options:
        after=widget - pack it after you have packed widget
        anchor=NSEW (or subset) - position widget according to
                                  given direction
        before=widget - pack it before you will pack widget
        expand=bool - expand widget if parent size grows
        fill=NONE or X or Y or BOTH - fill widget if widget grows
        in=master - use master to contain this widget
        in_=master - see 'in' option description
        ipadx=amount - add internal padding in x direction
        ipady=amount - add internal padding in y direction
        padx=amount - add padding in x direction
        pady=amount - add padding in y direction
        side=TOP or BOTTOM or LEFT or RIGHT -  where to add this widget.
        """
        self.tk.call(
              ('pack', 'configure', self._w)
                self._options(cnf, kw))

        return self  # return the widget passed to this method

    pack = configure = config = pack_configure

place

class Place:
    """Geometry manager Place.
    Base class to use the methods place_* in every widget."""

    def place_configure(self, cnf={}, **kw):
        """Place a widget in the parent widget. Use as options:
        in=master - master relative to which the widget is placed
        in_=master - see 'in' option description
        x=amount - locate anchor of this widget at position x of master
        y=amount - locate anchor of this widget at position y of master
        relx=amount - locate anchor of this widget between 0.0 and 1.0
                      relative to width of master (1.0 is right edge)
        rely=amount - locate anchor of this widget between 0.0 and 1.0
                      relative to height of master (1.0 is bottom edge)
        anchor=NSEW (or subset) - position anchor according to given direction
        width=amount - width of this widget in pixel
        height=amount - height of this widget in pixel
        relwidth=amount - width of this widget between 0.0 and 1.0
                          relative to width of master (1.0 is the same width
                          as the master)
        relheight=amount - height of this widget between 0.0 and 1.0
                           relative to height of master (1.0 is the same
                           height as the master)
        bordermode="inside" or "outside" - whether to take border width of
                                           master widget into account
        """
        self.tk.call(
              ('place', 'configure', self._w)
                self._options(cnf, kw))

        return self  # return the widget passed to this method

    place = configure = config = place_configure

grid

class Grid:
    """Geometry manager Grid.
    Base class to use the methods grid_* in every widget."""
    # Thanks to Masazumi Yoshikawa ([email protected])

    def grid_configure(self, cnf={}, **kw):
        """Position a widget in the parent widget in a grid. Use as options:
        column=number - use cell identified with given column (starting with 0)
        columnspan=number - this widget will span several columns
        in=master - use master to contain this widget
        in_=master - see 'in' option description
        ipadx=amount - add internal padding in x direction
        ipady=amount - add internal padding in y direction
        padx=amount - add padding in x direction
        pady=amount - add padding in y direction
        row=number - use cell identified with given row (starting with 0)
        rowspan=number - this widget will span several rows
        sticky=NSEW - if cell is larger on which sides will this
                      widget stick to the cell boundary
        """
        self.tk.call(
              ('grid', 'configure', self._w)
                self._options(cnf, kw))

        return self  # return the widget passed to this method

    grid = configure = config = grid_configure

The crux of my question is: Can anyone explain the design rationale behind this "feature" of tkinter? Ultimately, is a change like this worth a pull request / PEP? Would this cause undue breaking changes to tkinter?

This isn't necessarily a problem so much as it is the result of a deep dive taken after seeing so many questions here regarding this behavior.

CodePudding user response:

Can anyone explain the design rationale behind this "feature" of tkinter?

My guess it's because the underlying methods in the tk library don't return anything. In tcl/tk it's impossible to create and manage the widgets in a single statement so there's just no reason for the pack, place, and grid to return anything. Plus, in the tcl/tk world you can call grid and pack and pass in multiple widgets, so it wouldn't make sense to return a single widget.

This is definitely a place where tkinter could stand to be improved IMHO.

Ultimately, is a change like this worth a pull request / PEP?

I personally think so.

Would this cause undue breaking changes to tkinter?

I think it would only cause undue breaking of code that relies on the functions returning None. I doubt anyone intentionally does that, though there may be some poorly coded programs out there that unintentionally rely on the fact these methods return None.

  • Related