I have read numerous posts about people with NonType
errors because they have tried to .pack()
, .grid()
or .place()
on the same line as defining the tk widget
variable.
Example
execute = tk.Button(text="Start", command=lambda:
Elements.ClickApp()).place(x=20, y=250, height=20, width=50)
Responses have been that it's A, not possible with 2 functions, and B difficult to read in one line anyway. I started out (reluctantly) accepting an additional line for .pack()
, .grid()
or .place()
but have now encountered additional lines (which don't even work). Such as:
T = tk.Entry()
T.bind("<Key>", Elements.Tbox)
T.place(x=20, y=230, height=20, width=50)
Not only can these three lines not be placed in one line, which is what I want .. but the code does not even function. Instead the first button pressed is ignored, then the second button (and onwards) triggers Elements.Tbox
.
Instead, newlines must be increased further:
sv = tk.StringVar()
sv.trace("w", lambda name, index, mode, sv=sv: Elements.Tbox(sv))
T = tk.Entry(textvariable=sv)
T.place(x=20, y=230, height=20, width=50)
Which then correctly triggers Elements.Tbox
on the first button press.
Is there a way, (maybe with lambda
) these new lines can be reduced? ... When dealing with large groups of widgets, it reads easier to group widget this way. I find this more readable then separate lines.
UPDATE
I have included sample code with the answer to this question ... although the answer marked correct is probably better advice. This is incase someone out there needs to do this or something similar, you can do the following:
Entry Widget Example:
Combining EntryBox = tk.Entry()
with .place()
, .pack()
or .grid()
(lambda g: [(EntryBox.place(x=0, y=0, height=20, width=190)) for g['EntryBox'] in [tk.Entry()]])(globals())
If I understand you correctly, you want to reduce these three lines to something which has fewer lines:
T = tk.Entry()
T.bind("<Key>", Elements.Tbox)
T.place(x=20, y=230, height=20, width=50)
The short answer is, you can't. At least, not without making the code harder to understand which is an anti-pattern for software development.
The reason is just a fundamental aspect of programming in a dynamic language like python: you can't refer to objects before they exist. Thus, a widget must exist before you can call pack
, place
, grid
, or bind
on it. And given that those functions return None
, you can't chain them like you might be able to in other languages.
You may be tempted to combine the first and third statements like this:
T = tk.Entry().place(x=20, y=230, height=20, width=50)
That won't work either. The reason is that T
will be set to None
, making it impossible to refer to the widget later. This is because in python, x().y()
returns the value of y()
, thus with Entry().grid()
, grid
always returns `None.
In my experience you should never group your widget creation and widget layout commands together. Instead, create your widgets in one group, and lay them out in another. The same is usually true for bindings as well.
Why? Because usually when you're needing to solve a layout problem it involves changing the options for more than one widget, so it's convenient that they are grouped together.
In my experience, GUI code is easier to read and manage when it code that does similar things is grouped together, rather than grouping all statements related to a the same object.
For example:
x = tk.Entry(...)
y = tk.Button(...)
z = tk.Label(...)
x.grid(...)
y.grid(...)
z.grid(...)
x.bind(...)
y.bind(...)
z.bind(...)
This isn't a hard and fast rule, of course. The goal should always be clarity, and there are times when it makes sense to put the widget creation and layout together, though that's usually if you are dealing with just a single widget.
If you are wanting to create a bunch of identical widgets where the arguments are nearly identical, instead of cramming as much as possible on a single line you should create a helper function.
For example, in your case you might want to make a create_entry function:
def create_entry(master, x, y, width, height, callback):
entry = tk.Entry(master)
entry.bind("<Key>", callback)
entry.place(x=x, y=y, height=20, width=20)
return entry
With that, you now have your coveted one-line solution:
T = create_entry(root, 20, 230, 20, 50, Elements.Tbox)
U = create_entry(root, 20, 290, 20, 50, Elements.Tbox)
V = create_entry(root, 20, 290, 20, 50, Elements.Tbox)
...