0

I am looking to override the Tkinter Frame, Button, Lable and Entry widgets to have them creted and packed in the same line of code. (IMO 2 lines to do this and get a reference for the obj is ugly, inefficient and harder to read even if it provides more flexability).

My previous code was this:

   def cFrame(self, element, bg="white", borderwidth=0, relief="groove", side=None, padx=0, pady=0, height=0, width=0, expand=0, fill=None, image=None, highlightbackground=None, highlightcolor=None, highlightthickness=0, ipadx=0, ipady=0):
        f = self.TkUtil.Frame(element, bg=bg, borderwidth=borderwidth, relief=relief, height=height, width=width, image=image, highlightbackground=highlightbackground, highlightcolor=highlightcolor, highlightthickness=highlightthickness)
        f.pack(side=side, padx=padx, pady=pady, ipadx=ipadx, ipady=ipady, expand=expand, fill=fill)
        return f

My proposed class would look like this:

class cFrame(Frame):
    def __init__(self, master, **kwargs):
        Frame.__init__(*(self, master), **kwargs)
        self.pack(**kwargs)

The issue with this being **kwargs does not care if the keyword is valid and returns 'bad option -"kwarg"'.

I have tried expanding the function a bit more but I keep having issues:

class cButton(Button):
    def __init__(self, master,  **kwargs):
        Button.__init__(*(self, master))
        self.conf(**kwargs)
    def conf(self, **kwargs ):
        __pk_ops = {}
        for k, v in kwargs.items():
            try:
                self.configure({k:v})
            except:
                __pk_ops[k] = v
        self._pk(__pk_ops)
    def _pk(self, __ops):
        self.pack(__ops)

In this code, I have to loop through all kwargs before and indervidually configure them which is a lot of wasted time over the 1000's of widgets needed. Additionally, I have issues where self.configure(text="sometext") errors so 'text' is passed through to the self.pack() method because for some magic reason it doesn't like that option in particular (same for compound in this example too).

My end goal is to have x = Frame(root, bg="black") \n x.pack(side=TOP) be replaced with x = cFrame(root, bg="black", side=TOP) without default any of the options like my current code making any changes difficult and any sort of theam almost impossible.

4
  • 1
    Why not a composing function def pack(widget, **kwargs): widget.pack(**kwargs); return widget; you could then do self.frame = pack(TkUtil.frame(..., bg=...), side=...). This would work fine for any widget, not just a cButton, cFrame, ... Commented Nov 8, 2021 at 14:09
  • 2
    I double it's related, but why are you using ...(*(self, master), ...) instead of just ...(self, master, ...)? Creating a tuple only to immediately unpack it doesn't really make sense. Commented Nov 8, 2021 at 14:55
  • 1
    The problem, though, appears to be that you aren't differentiating between the keyword arguments expected by __init__ and the ones expected by pack. You may need to pass two separate dict arguments, with each sent to the appropriate function as keyword arguments. Commented Nov 8, 2021 at 14:57
  • @Scott are you looking for something like this? Commented Nov 8, 2021 at 14:59

1 Answer 1

1

You can create a custom class to override the pack() as below:

def cWidget:
    def pack(self, **kw):
        super().pack(**kw)  # call original pack()
        return self   # return widget itself

    # can do the same for grid() and place()

Then create custom widget class as below:

import tkinter as tk

class cButton(cWidget, tk.Button): pass
# do the same for other widgets you want

Now you can use the custom button class as below:

...

root = tk.Tk()

def clicked():
    print('Button clicked')
    btn['text'] = 'Clicked'  # can use 'btn' to change its text as well

btn = cButton(root, text='Click me!', command=clicked, bg='gold').pack(padx=10, pady=10)
print(f'{btn=}')  # here you won't get None
print(isinstance(btn, tk.Button))  # here you get True
...

Note that for Python 3.8+, you can simply use the Walrus Operator (:=):

(btn := tk.Button(root, text='Click me!')).pack(padx=5, pady=5)
Sign up to request clarification or add additional context in comments.

1 Comment

also to provide this to all the widgets at once, you could define your own pack function that will return self and assign it to tk.Widget: tk.Widget.pack_configure = your_pack

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.