Positioning widget buttons and Entry boxes better in Python - python

Ok, so I'm using the Tkinter module for Python, and I want to have 5 Entry boxes, each of which will be a variable which combines into a single variable, and is used as the serial key for an automatic installer for Office (among other software, for an install script for a medium-sized office).
None of that is really relevant, except to give you an idea of the content.
I am attempting to put all of the Entry boxes in a small space, preferably one or two columns (I am currently using 4-6 columns, I can check).
Is this possible with Tkinter?
Here's my code so far for this part of the script:
Label(app, text="Office Serial Key").grid(row=3, column=0)
entries = ["e1", "e2", "e3", "e4", "e5"]
colnum = 1
for item in entries:
item = Entry(app, width=10)
item.grid(row=3, column=colnum)
colnum = colnum + 1
I want to look more professional/like a Microsoft install.

You can do a lot to get the layout exactly the way you want it. I think that the way to accomplish something like this is to put your Label+Entrys into another Frame, then when you grid that Frame, use the columnspan keyword to set how wide it should be with respect to the other widgets in "app".
( http://effbot.org/tkinterbook/grid.htm )
here's a silly example:
import Tkinter as tk
root=tk.Tk()
for i in range(7):
tk.Label(root,text='%d'%i,width=12).grid(row=0,column=i)
myframe=tk.Frame(root)
tk.Label(myframe, text="Office Serial Key").grid(row=0, column=0)
entries = ["e1", "e2", "e3", "e4", "e5"]
for column,item in enumerate(entries,start=1):
item = tk.Entry(myframe, width=10)
item.grid(row=0, column=column)
myframe.grid(row=1,column=0,columnspan=3)
root.mainloop()

Related

How can I make an another block of items under a grid in tkinter? - Python

I want to make a 'maze' game with tkinter, and I wanted to display some things under the grid(game area), and this happend:
Because I wanted to put the text in the last line and the first column of the grid.
frame = tk.Frame(window)
frame.grid(row=line+1, column=0)
label = tk.Label(master=frame, text="Dashing:")
label.pack()
Any ideas, how can I fix this?
Make the frame span all of the columns:
frame.grid(row=line+1, column=0, columnspan=17)

how to set a list of entries text variables to another dynamic list

Is there anyway I can plug my list of integers into my list of entry boxes? The list of integers is constantly changing..
This would not be a problem if the list of integers and list of entry boxes had the same number of data points, however I can't determine that initially because I want user input to determine this entry list length in future code. I've tried using Insert to solve this problem, to no avail, given that I couldn't use the index of entry to configure its text option.
from tkinter import *
def entry_list_extender():
entrylist.extend(number)
gx=10
number=0
root=Tk()
frame=Frame(root)
frame.pack()
entry=[]
entrylist=[1,2,3,4]
var = []
entrybox=Entry(frame,bg='blue',textvariable=number)
entrybox.pack()
button=Button(frame,bg='red',command=entry_list_extender)
button.pack()
for i in range(gx):
entry.append(Entry(frame, textvariable=entrylist[i]))
entry[-1].pack()
root.mainloop()
A solution or path I could take to get the results I want would be appreciated.
Edit: my original question was quite ambiguous. This should make it more clear
UPDATE:
I am going to have to make an assumption here to make this work.
I am assuming that gx is the user defined variable you want to use down the road.
If that is the case then you need to change you your code a bit to re-create the entry fields when you press the button and also use the value of gx to decide on how many entry fields you should use.
Let me know if this is closer to what you are trying to do as it is still not very clear what your goal is.
from tkinter import *
root=Tk()
gx=10
number=0
entry=[]
entrylist=[1, 2, 3, 4]
var = []
def entry_list_extender():
global frame, entrylist, entry
entry = []
entrylist = []
for i in range(gx):
entrylist.append(i)
frame.destroy()
create_entry_fields()
entrybox=Entry(root, bg='blue', textvariable = number)
entrybox.pack()
button=Button(root, bg='red', command = entry_list_extender)
button.pack()
def create_entry_fields():
global frame, entrylist, entry
frame = Frame(root)
frame.pack()
print (len(entrylist))
for i in range(len(entrylist)):
entry.append(Entry(frame, textvariable = i))
entry[-1].pack()
create_entry_fields()
root.mainloop()

Second row entry width affect the first row style in tkinter?

I'm trying to create the some sample application.
Which first row is One label then input entry box then submit button.
Then second row has the another entry box.
My problem is when I increase width of the entry box in second row it affect the first row style. I don't know what is the problem.
import Tkinter
tk_obj = Tkinter.Tk()
tk_geo = tk_obj.geometry("1200x800")
Tkinter.Label(tk_obj, text='Enter query ').grid(row=1,column=1)
def callback():
print "hi"
E1 = Tkinter.Entry(tk_obj,bd=3,width=120)
E1.grid(row=1, column=2,ipady=3)
b = Tkinter.Button(tk_obj, text="Check", command=callback)
b.grid(row=1,column=3)
E2 = Tkinter.Entry(tk_obj,bd=3,width=100)
E2.grid(row=2,column=1,ipady=100)
tk_obj.mainloop()
The grid method places widgets in the center of the cell they inhabit. When you have two widgets of different sizes sharing a row or column, this means that there will be blank space around the smaller widget. To make the second Entry widget span the first two columns, use columnspan=2 when you grid() it. To left-align it within those two columns, use sticky='W':
E2.grid(row=2,column=1,ipady=100, columnspan=2, sticky='W')
You can then adjust that Entry widget's width attribute until it looks the way you want it to.

Programatically add and remove tkinter python labels causes IndexError: list index out of range

Sorry for the vague title but I didn't know how to explain myself better. Basically what I try to do in tkinter here is adding and removing labels. The label value gets updated so that I always have an increment of 1 even though I deleted a label in the beginning. If I generate labels and delete them from the bottom up I have no problems but it I delete one from the middle and then try to clean my list I get an error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1536, in __call__
return self.func(*args)
File "/Users/XXXX/Helper/development/dynamicListLabels.py", line 21, in <lambda>
labelList[index].append(ttk.Button(root, text="Remove", command=lambda: removeLabel(labelList[index][0], index)))
IndexError: list index out of range
My python code looks like this:
#!/usr/bin/python
from Tkinter import *
import ttk
def removeLabel(labelToRemove, bla):
labelList[labelToRemove.get()][1].destroy()
labelList[labelToRemove.get()][2].destroy()
del labelList[labelToRemove.get()]
for label in labelList:
index = labelList.index(label)
label[0].set(index)
def addNewLabel():
labelList.append([IntVar()])
index = len(labelList) - 1
labelList[index][0].set(index)
labelList[index].append(ttk.Label(root, textvariable=labelList[index][0]))
labelList[index].append(ttk.Button(root, text="Remove", command=lambda: removeLabel(labelList[index][0], index)))
labelList[index][1].grid(column=0)
labelList[index][2].grid(column=1, row=labelList[index][1].grid_info()['row'])
root = Tk()
labelList = []
ttk.Button(root, text="add label", command=addNewLabel).grid(column=1, row=0)
root.mainloop()
And my GUI looks like this:
Thanks for your help!
Design
The main problem comes when dealing with different indexes. Trying to manipulate them carefully leads to complicated operations resulting in a long and inefficient code. To remedy to this problem, we simply get rid of them and take advantage of the label class variable Tkinter.IntVar() you already are using. This gives us full control of the labels and associated widgets.
An other efficient decision to take that prevents from getting lot of headache is to attach each (label, button) couple widgets to a unique Tkinter.Frame() instance. This offers the advantage of deleting the frame using destroy() method leading automatically to the destruction of the widgets it contains. In the same time, this keeps the look of your GUI and makes your it scalable as it offers you the possibility to add more widgets.
Designing addNewLabel()
There is nothing new here compared to your original code except, as I said in 2. each (label, button) couple will be drawn into a single and unique Tkinter.Frame() instance. Of course, the list frames must be declared global in this method.
Designing removeLabel()
From 1. the only argument we need to pass to removeLabel() is the Tkinter variable (var in the code below) inherent to the label we want to get rid of.
We need then to loop over list of frames (frames in the code below) using winfo_children() to seek for the label which has the text variable we are looking for.
Note that because I draw the label before the button inside individual frames, winfo_children() returns as first widget list element the label
winfo_children():
Returns a list containing the path names of all the children of window. Top-level windows are returned as children of their logical
parents. The list is in stacking order, with the lowest window first,
except for Top-level windows which are not returned in stacking order.
Use the wm stackorder command to query the stacking order of Top-level
windows.
This is why it is correct to write : if frame.winfo_children()[0].var == var and destroy the frame that contains the label which satisfies this condition.
Solution
Here is the program. I commented the lines which I think deserve to be commented:
'''
Created on Jun 25, 2016
#author: billal begueradj
'''
from Tkinter import *
import ttk
def removeLabel(var):
global frames
z = -1
# Loop over the list of rames
for frame in frames:
z = z + 1
# Check the text variable of the label of this frame
if frame.winfo_children()[0].var == var:
# Destroy the related frame
frame.destroy()
# Update the size of the list of frames
frames = frames[:z] + frames[z+1:]
# Do not forget to always rest this flag back to -1
z = -1
# Update the labels' numbers
r = 0
for frame in frames:
frame.winfo_children()[0].var.set(r)
r = r + 1
def addNewLabel():
global frames, i
var = IntVar()
frame = Frame(root)
i = i + 1
frame.grid(row=i, column=0)
var.set(len(frames))
l = ttk.Label(frame, textvariable=var)
l.grid(row=0, column=0)
l.var = var
b = ttk.Button(frame, text="Remove", command=lambda: removeLabel(var))
b.grid(row=0, column=1)
frames.append(frame)
if __name__ == '__main__':
root = Tk()
frames = []
i = 1
ttk.Button(root, text="add label", command=addNewLabel).grid(column=0, row=0)
root.mainloop()
Demo
Let us create 6 labels:
Now let us delete the label number 3. You can see that the numbering of the labels is automatically updated:
Now let us add a new label. You can see the newly added label has a number which is consecutive to the last existing label number in the list:
Note that the length of the list is updated all the time as you wanted.

tkinter present the values from a list to entry widgets

Using tkinter in the gui I'm building an array made of entry boxes. In some columns of the array I'm entering values which i read them and build a list for every column. There are also columns without values which are readonly in which I want to export results from lists. I can't find how to make the program to show the results.
Keep in mind that i have two defs one for building the widgets and one for the calculations.
In order to build one empty-results column I make this:
self.akt_b =[]
for i in range(12):
akt_b =StringVar()
self.akt_b =Entry(self, textvariable =akt_b, width =10,
state='readonly', readonlybackground ="white")
self.akt_b.grid(row =7+i, column =3)
which makes visually what I want.
Then I calculate a list_b of 12 float numbers which I want to show in self.akt_b
I'm trying:
for i in range(12):
self.akt_b[i+1].delete(0, END)
self.akt_b[(i+1)].set("round(list_b[(i+1)], 2)")
which doesn't work
I've tried also .insert not again, I've tried akt_b nothing again
what can I do?
after Kevin's answer I found out the solution, I was missing the .append argument.
Just in case anyone is interested here is the solution:
self.akt_b, self.entries =[], []
for i in range(12):
akt_b =StringVar()
entries =Entry(self, textvariable =akt_b, width =10,
state='readonly', readonlybackground ="white")
entries.grid(row =7+i, column =3)
self.akt_b.append(akt_b)
so now
for i in range(12):
self.akt_b[i].set(round(list_b[i], 2))
works perfect.
Sometimes brain just seems to stop...

Categories