I'm using tkinter to create a calculator of sorts for one of my classes, and I've been making like 20 labels in a grid layout, using virtually the same line of code every time except the name and obviously the row number. I was just wondering if there was a standard way to quickly and effeciently make labels like this, or if someone has a quick way they use to break the tediousness?
For example, this is what my "code" basically looked like.
label0 = tk.Label(frame,
text="Label0")
label0.grid(row=0,
column=0,
sticky="E")
label1 = tk.Label(frame,
text="Label1")
label1.grid(row=1,
column=0,
sticky="E")
...
labeln = tk.Label(frame,
text="Labeln")
labeln.grid(row=n,
column=0,
sticky="E")
I tried creating a list or library of all the label names, then saying something like
labelnames = ["label0",
"labebl1",
...,
"labeln"]
for i in len(labelnames):
labelx = tk.Label(frame,
text=labelnames[i])
labelx.grid(row=i,
column=0,
sticky="E")
This works, but the point of this was to learn and I would like to know if there is a standard or "correct" way to do this. I tend to stray away from using classes because I still don't understand them (ironic I know), but I would like to learn if this were a better use for them.
A problem with this method that arises for me is I have an entry box corresponding to each label created using a similar loop, and I'm not sure how to get the input from the entry this way.
You are doing it correctly. We could make it a little neater using enumerate:
labels = []
for i, label in enumerate(labelnames):
labelx = tk.Label(frame, text=label)
labelx.grid(row=i, column=0, sticky="E")
labels.append(labelx)
I also added the resulting widgets to a list. That way I can access them later.
labels[2].config(text='new text') # update the text on the 3rd Label
Related
I was wondering if any of you Tkinter experts out there could help me with a question, do any of you know if there is a alternative to packs fill=X or fill=Y commands for grids? Or some code that would add in this function?
(I have searched around for a bit and couldn't find anything)
Use the sticky attribute to make objects fill the row and/or column that they are in.
Apply a weight to the rows and/or columns to get them to use any extra space in the container using grid_rowconfigure and grid_columnconfigure. The "weight" defines how grid will allocate any extra space once all the children are arranged. By default, rows and columns have zero weight, meaning they don't use any extra space.
For example:
frame = tk.Frame(...)
l1 = tk.Label(frame, ...)
l2 = tk.Label(frame, ...)
l1.grid(row=0, column=0, sticky="nsew")
l2.grid(row=1, column=1, stickky="nsew")
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
I have to make 10 entry boxes and rather than do each one individually, I've done them like this:
for i in range(0,10):
widthEntry = Entry(root, width=int(9.5), relief='solid')
widthEntry.grid(row=i+7, column=2, ipady=2)
so how do I pull the value entered into, for example, entry box 7 so that I can use that value in calculations?
I'm using Python and Tkinter
Use a list to contain the Entry instances.
entries = []
for i in range(0,10):
widthEntry = Entry(root, width=int(9.5), relief='solid')
widthEntry.grid(row=i+7, column=2, ipady=2)
entries.append(widthEntry)
Get the 7th entry box:
entries[6].get()
Please allow me to explain the problem I am facing. I am a undergraduate student who started learning Python a couple of month ago. So, I am new and please bear with me if the experts found them rather naive.
The intention was very simple. I want to place 5 menu buttons in parallel (say in one row, well, this is not the pressing issue though) in a tkinter parent frame. What I did was to introduce 5 sub-frames and place the menu button on them one by one. The idea to use sub-frame was to make space for centering and better layout. Using the most straight-forward fashion, I did it one by one: Define a sub-frame and place a menu button. It all worked well.
Now I am trying to avoid the repeated codes and to lay out the menu buttons and sub-frames using for loops, since the sub-frames and buttons are technically identical except the gird locations and labels are different. I came up with the following code.
Python compiler didn't report any errors. But, only the last button ("Other") was displayed while other buttons are missing. I am having difficulty sorting out what exactly went wrong.
indBtnFrame = [tkinter.Frame(self.parent)] * 5
for i in range(len(indBtnFrame)):
column_index = (i+1)*2
indBtnFrame[i].grid(column=column_index,
row=0,
columnspan=2,
rowspan=1,
sticky="WENS")
for r in range(1): # 1 row in total.
indBtnFrame[i].rowconfigure(r, weight=1)
for c in range(1): # 1 column in total.
indBtnFrame[i].columnconfigure(c, weight=1)
# To fix the grid size.
indBtnFrame[i].grid_propagate(False)
indBtnLabel = ["Street",
"City",
"Postcode",
"Province",
"Other"]
indBtn = []
for i in range(len(indBtnLabel)):
button_label = tkinter.StringVar()
button_label.set(str(indBtnLabel[i]))
temp_widget = tkinter.Menubutton(indBtnFrame[i],
textvariable=button_label,
relief=tkinter.GROOVE)
indBtn.append(temp_widget)
for i in range(len(indBtn)):
indBtn[i].grid(column=0, row=0,
columnspan=1, rowspan=1,
sticky="WENS")
indBtn[i].menu = tkinter.Menu(indBtn[i], tearoff=0)
indBtn[i]["menu"] = indBtn[i].menu
Firstly let's look at this program:
def entry_simutaneously_change():
from Tkinter import *
root=Tk()
text_in_entry.set(0)
Entry(root, width=30, textvariable= text_in_entry).pack()
Entry(root, width=30, textvariable= text_in_entry).pack()
Entry(root, width=30, textvariable= text_in_entry).pack()
root.mainloop()
The contents in these three entries can change simultaneously. If I change the value of any of them, the other two would change at the same time. However, for the following program:
def entry_simutaneously_change():
from Tkinter import *
root=Tk()
text_in_entry_list=[IntVar() for i in range(0,3)]
text_in_entry_list[0].set(0)
text_in_entry_list[1].set(text_in_entry_list[0].get() ** 2)
text_in_entry_list[2].set(text_in_entry_list[0].get() ** 3)
Entry(root, width=30, textvariable= text_in_entry_list[0]).pack()
Entry(root, width=30, textvariable= text_in_entry_list[1]).pack()
Entry(root, width=30, textvariable= text_in_entry_list[2]).pack()
root.mainloop()
When I change the content in the first entry, the contents in the other two do not change. Why?
In the first program, you have one source of data, text_in_entry. You could consider it as one box where you're placing a single value that's read by each Entry.
In the second program, you have three sources of data, text_in_entry[0, 1, and 2]. The lines that set the initial value are called only once. It's like you have three boxes where data is placed; to set the initial values, you do look at the value inside the first, but there is no association between the three.
If you would like to achieve the same type of result as with the first program (when the three entries update simultaneously) then you will need to bind on an event. I note that Tkinker does not have an on-change style event but there are various hacks, however you could bind to the FocusOut event. The general idea is to ask for a function to be called when a change occurs to an entry (binding a callback). Inside that function you update the other two values based on the new value in the entry that was changed.
I have the following sections of code (not complete) where I would like to contain several frames into a single tab in the ttk 'Notebook' .
win1 = Tk()
n = ttk.Notebook(win1)
choice_frame = ttk.Frame(n, style="Black.TLabel")
choice_frame.grid(row=2, column=1, sticky="N,S,E,W", padx=1, pady=1)
choice_frame.grid_columnconfigure(0, weight=3)
choice_frame.grid_rowconfigure(0, weight=1)
frame_text = ttk.Frame(n, style="Black.TLabel")
frame_text.grid(row=0, column=0, sticky = "N")
frame_text.grid_columnconfigure(0, weight=3)
frame_text.grid_rowconfigure(0, weight=1)
frame_table = ttk.Frame(n, style="Black.TLabel")
frame_table.grid(row=2, column=0, padx=1, pady=1, sticky= " N, S, E, W ")
frame_table.grid_columnconfigure(0, weight=3)
frame_table.grid_rowconfigure(0, weight=1)
n.add(frame_table, text="One") # Would like same tab not two separate ones
n.add(choice_frame, text="One")
n.grid(sticky="N")
I would also like to know if there's a way of allowing every dimension to adjust automatically when the window is dragged out and maximised. I have previously tried:
frame_table.grid_propagate(0)
But this doesn't seem to allow the height as well as width dimension to stick. I would like my 'table' to be in the center of the window but adjust with the window size.
Thank you for your help!
grid_propagate isn't a solution to any of your problems.
Each tab can contain only a single frame, but that single frame can contain whatever you want. So, create one container frame for the tab and then put all your other frames in the container frame. This is one of the main reasons the Frame class exists -- to build widget containers that facilitate layout.
As for the resizing, the problem is probably that you're not adding a weight to the rows and columns of the main window. Though it could also be related to the fact you are using grid to place the frames in the notebook; you can't use grid to place items in a notebook.