I'm trying to create multiple entry boxes with a for loop, so i don't have to make all the different boxes manually in case i want to increase the amount of entries, but this way I can't get my entry value via .get(). If i print L1 the output is a list of three empty strings, so no value was added after I typed them into the entry boxes. How can I make a list containing all the entry values as floats?
from tkinter import *
window = Tk()
window.geometry("450x450+200+200")
def do():
print(L1)
L1 = []
for i in range(3):
labelx = Label(window, text = str(i)).grid(row = i, column = 0)
v = StringVar()
num = Entry(window, textvariable = v).grid(row = i, column = 1)
num1 = v.get()
L1.append(num1)
button1 = Button(window, text = 'OK', command = do).grid(column = 1)
Your original code is storing the value in a list. Instead, store a reference to the widget. With this, there's no reason to create the StringVar objects.
Note that to do this you must create the widget and call grid as two separate statements. It not only allows this technique to work, it's generally considered a best practice to separate widget creation from widget layout.
L1 = []
for i in range(3):
labelx = Label(window, text = str(i))
num = Entry(window, textvariable = v)
labelx.grid(row = i, column = 0)
num.grid(row = i, column = 1)
L1.append(num)
...
for widget in L1:
print("the value is", widget.get())
Use the list, L1, to store the id of the Tkinter StringVar(). Use the get method in the function called by the button. Otherwise, how is the program to know when the data is ready to be retrieved. A StringVar returns a string that will have to be converted to a float. Also, it's a bad habit to use i, l, or o as single digit variable names as they can look like numbers.
window = Tk()
window.geometry("450x450+200+200")
def do():
for var_id in L1:
print(var_id.get())
var_id.set("")
L1 = []
for ctr in range(3):
## grid() returns None
Label(window, text = str(ctr)).grid(row = ctr, column = 0)
var_id = StringVar()
ent=Entry(window, textvariable = var_id)
ent.grid(row = ctr, column = 1)
##num1 = v.get()-->nothing entered when program starts
if 0==ctr:
ent.focus_set()
L1.append(var_id)
button1 = Button(window, text = 'OK', command = do).grid(column = 1)
window.mainloop()
Related
I am trying to create a menu program in tkinter, where check boxes are created from items in a dictionary, then the total price of selected items is calculated when a button is clicked.
menu_items = {"Spam - £3" : 3, "Eggs - £7" : 7, "Chips - £1" : 1, "Beer - £2" : 2}
def widgets(self):
# create menu list
row = 1
for item in menu_items:
self.item = BooleanVar()
Checkbutton(self,
text = item,
variable = self.item
).grid(row = row, column = 0, sticky = W)
row += 1
calc_but = Button(self,
text = "Click to calculate",
command = self.calculate
).grid(row = row + 1, column = 0, sticky = W)
self.results_txt = Text(self, width = 20, height = 4, wrap = WORD)
self.results_txt.grid(row = row + 2, column = 0, columnspan = 2)
This creates check boxes, button and text display just fine, but my problem comes with my calculate method.
def calculate(self):
bill = 0
for item in menu_items:
if self.item.get():
bill += menu_items.get(item)
msg = "Total cost - £" + str(bill)
self.results_txt.delete(0.0, END)
self.results_txt.insert(0.0, msg)
It will add up everything (ticked or not), but only when the final check box is ticked. It displays 0 if the final item is not ticked.
I am not sure what my problem is, or if I am approaching this in the wrong way.
What's happening here
The way you create your buttons - by looping through each key in you dictionary - your program only references the last one you created, which it saves as self.item. When you call calculate(), it only checks and adds up this button's value.
One way around this is to save all the references to these Buttons in a table:
menu_items = {"eggs":7, "chips":1, "beer":2}
selected = {}
def calculate():
bill = 0
for item in menu_items:
if selected[item].get():
bill += menu_items[item]
results.delete(1.0, END)
results.insert(END, "Total cost - £" + str(bill))
for item in menu_items:
is_selected = BooleanVar()
button = Checkbutton(master, text=item, variable=is_selected)
button.grid(sticky="w")
selected[item] = is_selected
purchase_btn = Button(master, text="Calculate", command=calculate)
purchase_btn.grid()
results = Text(master, wrap=WORD)
results.grid(columnspan=2)
(I've omitted your class structure here. It is easy enough to re-incorporate it)
Now we're keeping a is_selected dictionary, so that we can track whether a button has been selected or not.
Its easy to reference this table, because the keys are the items themselves! Neat, not?
A couple more tips on tkinter:
If you're gridding in the next row every time, just call grid() with no col and row arguments - tkinter adds the row for you, and keeps the column.
It's best always to create a widget, store it as a variable, then grid this variable. Some people try to do this all in one, and then get confused when the try to reference their widget by this variable. They will find nothing, because the grid method returns nothing!
I hope this helped. Good luck!
I have written a code where Label and Entry widgets' variables are put in a list (in order to avoid the number of lines which will take for creation of each Label and Entry widgets). Then a for loop to create Label and Entry widgets are used, A submit button is used which have to list the entries of each Entry widget. But an empty value is displayed instead of displaying the entered value. Can anyone help me to know the reason and correct the code.
Below is the code which I have written:
from Tkinter import *
app = Tk()
list1 = ['l1','l2','l3']
list2 = ['e1','e2','e3']
entries = []
r = 0
c = 0
for m,n in zip(list1, list2):
x = Label(app, text=m)
x.grid(row = r, column =c)
n = StringVar()
e = Entry(app, textvariable = n)
e.grid(row =r , column = c+1)
entries.append(e.get())
r = r + 1
def func():
entries.append("Dfg")
print entries
s = Button(app, text = "Submit", command = func)
s.grid(row = r, columnspan=2)
app.minsize(400, 400)
app.mainloop()
Please note: There could be indentation problem while posting the code, sorry for the inconvenience.
The for loop is for initialize in this case, When the Entry is been created,The input of Entry is empty , so when you entries.append(e.get()) got the empty
.I change something from your code , and the entries will be entered value when you click the button BTW I use python3 instead of python2
from tkinter import *
app = Tk()
list1 = ['l1','l2','l3']
list2 = ['e1','e2','e3']
entries = []
e = []
r = 0
c = 0
for index, m in enumerate(zip(list1, list2)):
x = Label(app, text=m)
x.grid(row = r, column =c)
n = StringVar()
e.append( Entry(app, textvariable = n))
e[index].grid(row =r , column = c+1)
r = r + 1
def func():
entries = []
for a in e:
entries.append(a.get())
print(entries)
s = Button(app, text = "Submit", command = func)
s.grid(row = r, columnspan=2)
app.minsize(400, 400)
app.mainloop()
I am trying to create a GUI that will be able to monitor the integrity of files using their MD5 hashes (the actual monitoring of updates log can be in the command prompt).
I got the initial command line program to work perfectly, but am having an issue when converting it to a GUI based version using tkinter.
I use the GUI to create a list of files that I want to monitor in the 'addFiles' function, but when I try to pass that list to the 'checkForIntegrity' function (or print the list with my test print(listOfFiles) code in that function), all I get is [tkinter.StringVar object at 0x01FEAD50], but do not get the actual list.
I have searched far and wide for an answer and have tried using various implementations of 'listOfFiles.get()' in different locations but have had no success.
I have no idea why I only get the actual list object but no listed items, my code is below.
Thank you in advance everyone.
edit: Just to be clear, my 'GUI()' function creates a window that asks how many files the user would like to monitor and passes that to the 'addFiles()' function which allows input for the amount of files they specify. I need to be able to pass the files they specify in that GUI to the program via a list. Thanks again.
import hashlib
import time
from tkinter import *
def main():
GUI()
def GUI():
window = Tk()
window.title("Integrity Checker")
frame1 = Frame(window)
frame1.pack()
label1 = Label(frame1, text = "***Proof Of Concept Program That Monitors the Integriry of Files***")
label1.grid(row = 1, column = 1)
frame2 = Frame(window)
frame2.pack()
getNumberOfFiles = Label(frame2, text = "Insert Number of Files You Would Like to Check: ")
getNumberOfFiles.grid(row = 2, column = 1)
numberOfFiles = IntVar()
NumberOfFilesOption = Entry(frame2, textvariable = numberOfFiles)
NumberOfFilesOption.grid(row = 2, column = 2)
button = Button(frame2, text = "OK", command = lambda : addFiles(numberOfFiles))
button.grid(row = 2, column = 3)
window.mainloop()
def addFiles(numberOfFiles):
listOfFiles = []
window = Tk()
window.title("Add Files")
frame1 = Frame(window)
frame1.pack()
label1 = Label(frame1, text = "***Select The Files You Want To Monitor***")
label1.grid(row = 1, column = 1)
for i in range (numberOfFiles.get()):
AddFile = Label(frame1, text = "Add File:")
AddFile.grid(row = (i + 3), column = 1)
FileName = StringVar()
FileNameOption = Entry(frame1, textvariable = FileName)
FileNameOption.grid(row = (i + 3), column = 2)
button = Button(frame1, text = "OK", command = lambda : listOfFiles.append(FileName))
button.grid(row = (i + 3), column = 3)
button2 = Button(frame1, text = "Done", command = lambda : checkforINTEGRITY(numberOfFiles, listOfFiles))
button2.grid(row = (i + 4), column = 2)
window.mainloop()
def checkforINTEGRITY(numberOfFiles, listOfFiles):
#Number = numberOfFiles.get()
#listOfFiles = []
#count = 0
#numberOfFiles = eval(input("How many files would you like to monitor?: "))
#while count < Number:
# filename = input("Enter the name of the file you would like to check: ")
# count += 1
# listOfFiles.append(filename)
print(listOfFiles)
i = 0
originalList = []
for file in listOfFiles:
original_md5 = hashlib.md5(open(listOfFiles[i],'rb').read()).hexdigest()
originalList.append(original_md5)
i += 1
print(originalList)
while True:
i = 0
while i < Number:
md5_returned = hashlib.md5(open(listOfFiles[i],'rb').read()).hexdigest()
print(md5_returned)
if originalList[i] == md5_returned:
print("The file", listOfFiles[i], "has not changed")
else:
print("The file", listOfFiles[i], "has been modified!")
i += 1
time.sleep(5)
main()
It looks like you want to call the get method on FileNameOption:
button = Button(frame1, text = "OK", command = lambda : listOfFiles.append(FileNameOption.get()))
With this change I was able to get a list of strings in listOfFiles.
I want the whole thing to expand, as the user expands it, without widgets, like the listbox, reshaping. I want it to be exactly like expanding a still image which maintains its aspect ratio. How do I do this?
from tkinter import *
import random
from random import *
import threading
selected = 0
contained = {}
contained[0] = []
name = {}
maxEventItems = 100
i = 0
while i < 10 ** 3:
contained[0].append(i)
name[i] = 'Event {}'.format(i)
i += 1
# Beginning of thread part, in the future.
def eventViewerListboxItems():
i = 0
while i < len(contained[selected]):
eventNumber = contained[selected][i]
eventViewerListbox.insert(END,'{}'.format(name[eventNumber]))
i += 1
master = Tk()
masterCanvas = Canvas(master)
masterCanvas.grid(row = 0,column = 0,sticky = N + S + E + W)
masterFrame = Frame(masterCanvas)
masterFrame.grid(row = 0,column = 0)
main = Frame(masterFrame)
main.grid(row = 0,column = 0)
topButtons = Frame(main)
topButtons.grid(row = 0, column = 0)
saveButton = Button(topButtons,text = 'Save')
saveButton.grid(row = 0,column = 0,sticky = W)
loadButton = Button(topButtons,text = 'Load')
loadButton.grid(row = 0,column = 1,sticky = W)
createEventButton = Button(topButtons,text = 'Create event')
createEventButton.grid(row = 0,column = 2,sticky = W)
eventViewer = Frame(main)
eventViewer.grid(row = 1, column = 0)
eventViewerListboxScrollbar = Scrollbar(eventViewer)
eventViewerListboxScrollbar.grid(row = 1,column = 1, sticky = W + N + S)
eventViewerListbox = Listbox(eventViewer)
eventViewerListbox.grid(row = 1,column = 0,sticky = W)
eventViewerListbox.config(yscrollcommand = eventViewerListboxScrollbar.set)
eventViewerListboxScrollbar.config(command = eventViewerListbox.yview)
bottomButtons = Frame(main)
bottomButtons.grid(row = 2, column = 0,sticky = E)
simulateButton = Button(bottomButtons,text = 'Simulate')
simulateButton.grid(row = 0,column = 0,sticky = E)
callEventViewerListboxItems = threading.Thread(target = eventViewerListboxItems)
callEventViewerListboxItems.start()
partial_contained = {}
partial_contained[selected] = []
i = 0
while i < maxEventItems and i < len(contained[selected]):
partial_contained[selected].append(contained[selected][i])
i += 1
print('I started putting the items from contained[{}] into the listbox in the event viewer.'.format(contained[selected][0]))
print()
print('Below, I will show the first {} items that are in contained:'.format(i,contained[selected]))
print(partial_contained[selected])
print()
master.mainloop()
The problem is that you are using grid, but you haven't given a weight to any rows or columns. The weight determines which rows and columns are given extra space such as when a user resizes the window. By default the weight of every row and column is zero, meaning they get no extra weight.
As a rule of thumb, you need to call grid_rowconfigure and grid_columnconfigure for at least one row and one column, and give that row or column a positive weight. You will need to do this for every widget that is using grid to manage its children.
You also aren't using the sticky attribute consistently, so even if you get the columns to grow and shrink, your widgets won't expand to fill the area they've been given.
You need to be methodical about creating a complex layout. My advice is to start over, and just get masterCanvas to grow and shrink like you expect. Give it a bold, distinctive cover so you can see it (ie: if it has the same color as the background, it's impossible to know if it's growing or shrinking to fill the area)
Once you get masterCanvas where it is growing and shrinking properly, move on to masterFrame, and then main, and then eventViewer, and finally the listbox. At each step be sure to give the widget a distinctive color so you can visualize it.
You might also want to consider removing so many layers of nested frames. The more you have, the more difficult it becomes to get them to all work together. However, multiple areas are easy if you take a methodical approach, and don't try to get half a dozen different frames working together all at once. Get one working, then focus on its immediate children. When they work, focus on their children, and so on.
I started making a function that when a selection in a listbox is double clicked, the selection info(a dictionary) gets returned.
def OnDouble(self, event):
widget = event.widget
selection = widget.curselection()
value = widget.get(selection[0])
What I want is to be able to take that selection that gets returned and edit it's contents. By doing this, any changes in content should show up in the listbox and the list from which it comes from.
Example of value that gets returned with double click:
{'Num Tel/Cel': 'test1', 'Email': 'test1', 'Fecha de Entrega': '', 'Orden Creada:': ' Tuesday, June 23, 2015', 'Nombre': 'test1', 'Num Orden': '1'}
from Tkinter import *
oneThing = {"Name:": "Guido", "Tel.:":"666-6969", "Email:":"foobar#lol.com"}
another = {"Name:": "Philler", "Tel.:":"111-1111", "Email:":"philler#lol.com"}
z = [oneThing, another]
root = Tk()
l = Listbox(root)
l.pack(fill = "both")
l.pack_propagate(True)
[l.insert(END, item) for item in z]
def createPerson(index):
#This is whatever function that creates stuff
def edit():
for i in range(len(labels)):
z[index][labels[i]] = entries[i].get()
print z
top.destroy()
top = Toplevel()
labels = ["Name:", "Tel.:", "Email:"]
i = 0
for text in labels:
Label(top, text = text).grid(column = 0, row = i)
i += 1
e1 = Entry(top)
e1.grid(column = 1, row = 0)
e2 = Entry(top)
e2.grid(column = 1, row = 1)
e3 = Entry(top)
e3.grid(column = 1, row = 2)
Button(top, text = "Submit", command = edit).grid(column = 1, row = 3)
entries = [e1, e2, e3]
#Return reference to toplevel so that root can wait for it to run its course
return top
def edit():
global l, z, root
# Get dictionary from listbox
sel = l.curselection()
if len(sel) > 0:
indexToEdit = z.index(eval(l.get(sel[0])))
l.delete(sel)
root.wait_window(createPerson(indexToEdit))
print z[indexToEdit]
l.insert(sel, z[indexToEdit])
Button(root, text = "Edit", command = edit).pack()
root.mainloop()
Edit: Example now shows a way to edit elements on the fly based on user input; uses Toplevel() widget to accept input.
You can use the functions given in this documentation for editing the selections of a listbox.
Example -
widget.selection_set(<item to add>) # adds an item to the selection
or
widget.selection_clear(<item to remove>) # removes the item from the selection
Documentation for selection_set - here
Documentation for selection_clear - here