I'm very new at Python and need some help finishing the code. This is Tkiner related. I have an entry box, a button, and a lower frame for the output.
def loop_over_input(the_str=''):
master_list = []
for char in the_str:
tmp_char = passwordConversion[char]
master_list.append(tmp_char)
print("Master Pass List: ", master_list)
return master_list
This will work in command line with a couple of other lines. I'm not sure how tell it when I put text in the entry field and click the button to return the results in my lower frame. I have moved def loop_over_input to different parts of the code I think I may need to reference the test entry box and the button and the lower box.
I will post the complete code if requested to do so.
Firstly, you need to indent your code correctly. Everything that is in the function loop_over_input needs to be indented once more than the line def loop_over_input(the_str=''):
A few other notes. If you look up the documentation for the tkinter button, it will explain how to link a command to it. The piece of code you have supplied appears to be what you want the button command to be. Printing the list will do so in the shell, not in a frame below your entry field and button.
Here's some example code that should do what you want:
import tkinter as tk
# Creating tk window
window = tk.Tk()
# Master list
master_list = []
master_list_string = tk.StringVar()
# Frames
top_frame = tk.Frame(window)
top_frame.pack(expand = True, fill = 'x', pady = 10, padx = 10)
bottom_frame = tk.Frame(window)
bottom_frame.pack(expand = True, fill = 'both', padx = 10)
# Entry box
myEntry = tk.Entry(top_frame)
myEntry.pack(side = 'left',expand = True, fill = 'x', padx = 10)
# Label to display master list
myLabel = tk.Label(bottom_frame, textvariable = master_list_string)
myLabel.pack(expand = True, fill = 'both')
# Button to submit
def clicked():
master_list.append(myEntry.get())
myEntry.delete(0, 'end')
printed_list = ''
for password in master_list:
printed_list += "\n" + password
master_list_string.set(printed_list)
myButton = tk.Button(top_frame, text = "Submit", command = clicked)
myButton.pack(side = 'left', padx = 10)
# Mainloop
window.mainloop()
The two frames allow you to have the top section with your entry and button, while the bottom frame is for your output. However, you cannot just use a frame as your output, as your frame can't display text. Instead, use a Label widget linked to a StringVar which allows the text in the Label to update when the variable is changed.
The button command then takes the string entered into the entry, saves it to the master list and then sets the StringVar to the updated list, which automatically updates the Label.
I would highly recommend ready the documentation on Effbot, it's quite easy to understand with good examples. Link here
Related
This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 1 year ago.
I'm stuck on the following problem. With a for-loop, I want to make a few checkboxes that automatically update a label stating whether the checkbox is ticked or not. However, it gives the wrong results (it always says that the checkboxes are ticked, whether this is the case or not; noteworthy is the fact that the checkboxes are unticked by default), see here how the GUI looks like (including error). The IntVars corresponding with the checkboxes are working correctly, as can be seen when ticking at least one of the checkboxes and pressing a button whose function is to read the checkboxes. See also the following code:
import tkinter as tk
top = tk.Tk()
n_passes = 3
checkbox_var = [0] * n_passes
checkbox = [0] * n_passes
def tick_passes(i): # update label saying if checkboxes are ticked
if checkbox_var[i].get == 0:
label = tk.Label(top, text = f"pass #{i} not ticked")
else:
label = tk.Label(top, text = f"pass #{i} ticked")
label.grid(row = 1, column = i)
def check_checkbox_var(): # check whether checkbox_var[i] is updated
for i in range(n_passes):
print(f"checkbox_var[i].get() = {checkbox_var[i].get()}")
for i in range(n_passes):
checkbox_var[i] = tk.IntVar() # turn on/off certain passes
print(f"checkbox_var[i].get() = {checkbox_var[i].get()}")
checkbox[i] = tk.Checkbutton(top, text = f"Tick pass {i}", variable =
checkbox_var[i], command = tick_passes(i))
checkbox[i].grid(row = 0, column = i, sticky=tk.W)
var_button = tk.Button(top, text = "Check checkbox_var", command =
check_checkbox_var).grid(row = 2, column = 0) # check whether checkbox_var[i] is updated
top.mainloop()
Could somebody help me with updating the labels? If there is another way to fix this issue, e.g. with buttons to be pressed instead of checkbuttons to be ticked, that would also work for mee.
i is always 2 because you're actually not running any loop after mainloop is started.
The following kind of works but you need to change something about the labels, because right now all labels are just added on top of each other. You should create them once and then just update the text but I'll leave that part for you.
import tkinter as tk
top = tk.Tk()
n_passes = 3
checkbox_var = [0] * n_passes
checkbox = [0] * n_passes
def tick_passes(): # update label saying if checkboxes are ticked
for i in range(n_passes):
if checkbox_var[i].get() == 0:
label = tk.Label(top, text = f"pass #{i} not ticked")
else:
label = tk.Label(top, text = f"pass #{i} ticked")
label.grid(row = 1, column = i)
def check_checkbox_var(): # check whether checkbox_var[i] is updated
for i in range(n_passes):
print(f"checkbox_var[i].get() = {checkbox_var[i].get()}")
for i in range(n_passes):
print(i)
checkbox_var[i] = tk.IntVar() # turn on/off certain passes
print(f"checkbox_var[i].get() = {checkbox_var[i].get()}")
checkbox[i] = tk.Checkbutton(top, text = f"Tick pass {i}", variable =
checkbox_var[i], command = tick_passes)
checkbox[i].grid(row = 0, column = i, sticky=tk.W)
var_button = tk.Button(top, text = "Check checkbox_var", command =
check_checkbox_var).grid(row = 2, column = 0) # check whether checkbox_var[i] is updated
top.mainloop()
I am trying to create a program that allows the user to select any number of check boxes and hit a button to return a random result from those check boxes. Since I am basing my list off the roster of Smash bros ultimate, I am trying to avoid creating 70+ variables just to place check boxes. However, I am unable to figure out how to iterate this. The various values set for rows are just placeholders until I can figure this out. I would also like to have a reset button at the top that allows the user to automatically uncheck every box. This code is what I have so far. Any help would be greatly appreciated.
#!/usr/bin/python3
from tkinter import *
window = Tk()
#window name and header
window.title("Custom Random SSBU")
lbl = Label(window, text="Select the fighters you would like to include:")
lbl.grid(column=1, row=0)
f = [] #check boxes
ft = open("Fighters.txt").readlines() #list of all the character names
fv=[0]*78 #list for tracking what boxes are checked
ff=[] #list to place final character strings
def reset():
for i in fv:
fv[i]=0
rst = Button(window, text="Reset", command=reset)
rst.grid(column=0, row=3)
for y in range (0,77):
f[y] = Checkbutton(window, text = ft[y], variable = fv[y])
f[y].grid(column=0, row=4+y)
def done():
for j in fv:
if fv[j] == 1:
ff.append(fv[j])
result = random.choice(ff)
r=Label(window, text=result)
d = Button(window, text="Done", command=done)
d.grid(column=0, row = 80)
window.mainloop()
Unfortunately I'm afraid you are going to have to create variables for each checkbox.
tkinter has special purpose Variable Classes for holding different types of values, and if you specify an instance of one as the variable= option when you create widgets like Checkbutton, it will automatically set or reset its value whenever the user changes it, so all your program has to do is check its current value by calling its get() method.
Here's an example of the modifications to your code needed to create them in a loop (and use them in the done() callback function):
import random
from tkinter import *
window = Tk()
#window name and header
window.title("Custom Random SSBU")
lbl = Label(window, text="Select the fighters you would like to include:")
lbl.grid(column=1, row=0)
with open("Fighters.txt") as fighters:
ft = fighters.read().splitlines() # List of all the character names.
fv = [BooleanVar(value=False) for _ in ft] # List to track which boxes are checked.
ff = [] # List to place final character strings.
def reset():
for var in fv:
var.set(False)
rst = Button(window, text="Reset", command=reset)
rst.grid(column=0, row=3)
for i, (name, var) in enumerate(zip(ft, fv)):
chk_btn = Checkbutton(window, text=name, variable=var)
chk_btn.grid(column=0, row=i+4, sticky=W)
def done():
global ff
ff = [name for name, var in zip(ft, fv) if var.get()] # List of checked names.
# Randomly select one of them.
choice.configure(text=random.choice(ff) if ff else "None")
d = Button(window, text="Done", command=done)
d.grid(column=0, row=len(ft)+4)
choice = Label(window, text="None")
choice.grid(column=1, row=3)
window.mainloop()
I wasn't sure where you wanted the Label containing the result to go, so I just put it to the right of the Reset button.
variable = fv[y]
This looks up the value of fv[y] - i.e, the integer 0 - at the time the Checkbutton is created, and uses that for the variable argument.
You need to use an instance of one of the value-tracking classes provided by TKinter, instead. In this case we want BooleanVar since we are tracking a boolean state. We can still create these in a list ahead of time:
text = open("Fighters.txt").readlines()
# Let's not hard-code the number of lines - we'll find it out automatically,
# and just make one for each line.
trackers = [BooleanVar() for line in text]
# And we'll iterate over those pair-wise to make the buttons:
buttons = [
Checkbutton(window, text = line, variable = tracker)
for line, tracker in zip(text, trackers)
]
(but we can not do, for example trackers = [BooleanVar()] * len(text), because that gives us the same tracker 78 times, and thus every checkbox will share that tracker; we need to track each separately.)
When you click the checkbox, TKinter will automatically update the internal state of the corresponding BooleanVar(), which we can check using its .get() method. Also, when we set up our options for random.choice, we want to choose the corresponding text for the button, not the tracker. We can do this with the zip trick again.
So we want something more like:
result_label = Label(window) # create it ahead of time
def done():
result_label.text = random.choice(
label
for label, tracker in zip(text, trackers)
if tracker.get()
)
So i want to build an assistant off sorts which will do auto backs ups etc and instead of using .place i would like a proper grid to place widgets.
I cannot find a good example of the grid manager.
self.parent = tk.Frame(window, bg = BLACK)
username_label = ttk.Label(self.parent, text = "Username")
password_label = ttk.Label(self.parent, text = "Password")
self.parent.grid(column = 0, row = 0)
username_label.grid(column = 1, row = 1)
password_label.grid(column = 2, row = 2)
self.parent.grid_columnconfigure(0, weight = 1)
I want...
Button
Button
Label Entry Button
Label Entry Button
Button
I don't understand how i can position them like this as i want a blank space above the labels. so far grid has only let me place things next to each other.
Honestly, any websites or code examples would be greatly appreciated
So, if you want blank space above the label, you can either set pady as an argument to the grid method or simply put them in the corresponding row. Consider the following example:
import tkinter as tk
root=tk.Tk()
for i in range(6):
tk.Button(root,text='Button %d'%i).grid(row=i,column=1)
tk.Label(root,text='Label 0').grid(row=2,column=0,pady=20)
tk.Label(root,text='Label 1').grid(row=3,column=0)
root.mainloop()
Notice the effect of the pady argument. Also, if you only want a blank line above the Label, you can try to put a blank Label in the row above. E.g.:
import tkinter as tk
root=tk.Tk()
for i in range(6):
tk.Button(root,text='Button %d'%i).grid(row=i,column=1)
tk.Label(root,text='Label 0').grid(row=2,column=0,pady=20)
tk.Label(root,text='Label 1').grid(row=3,column=0)
tk.Label(root,text='').grid(row=6)
tk.Label(root,text='This is a Label with a blank row above').grid(row=7,columnspan=2)
root.mainloop()
You can refer to effbot for more information, which is the blog of tkinter's developer.
This is my current code for selecting different options and have them appearing in the box (Minecraft ArmorStand Generator).
from tkinter import *
default = "/summon ArmorStand ~ ~ ~ {CustomNameVisible:1}"
NoAI = ",NoAI:1"
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
root = Tk()
def addNOAI():
inputbox.insert(45, NoAI)
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
Button(text="Add NoAI",command=addNOAI,relief = FLAT, bg = "#eF651A", fg = "white", width= 25, height = 2).place(x=10,y=123)
root.title("WIP")
root.wm_state('zoomed')
root.mainloop()
What I'd like to to do is replace the buttons with tick boxes, to prevent the buttons being pressed multiple times. If they click the button, add the text, if they untick, remove it.. I'm not sure where to start with this so any hint in the right direction would be nice.
I've got a working solution, you can try it below.
from tkinter import *
default = "/summon ArmorStand ~ ~ ~ {CustomNameVisible:1}"
NoAI = ",NoAI:1"
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
root = Tk()
def addNOAI():
state = var.get()
if state == 1: #if the state is checked
inputbox.insert(45, NoAI) #then add the text
else: #if the state is not check
inputbox.delete(0, 7) #delete the text
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
var = IntVar() #sets up variable for check button
c = Checkbutton(text="Add NoAI", command=addNOAI, variable=var) #defining check button variable and command
c.place(x=10,y=123)
root.title("WIP")
root.wm_state('zoomed')
root.mainloop()
The only problem is at the moment, you are deleting everything in the entry box (more accurately from position 0, to position 7). I assume that there will be multiple check buttons, all adding their own strings to the entry box.
As a solution, I would suggest extracting everything from the entry box, finding the string you want, taking it out, and putting everything back in again. Here's an example.
def addNOAI():
state = var.get()
if state == 1: #if the state is checked
inputbox.insert(45, NoAI) #then add the text
else: #if the state is not check
contents = inputbox.get() #gets all of contents
position = contents.find(NoAI) #finds the first position of desired string to remove
newcontents = contents[:position]+contents[position+7:] #gets string before the word, and after the word, and joins them
inputbox.delete(0, 'end') #clears input box for new entry
inputbox.insert(45, newcontents) #re-inserts the string
Here, when the user unchecks the box, the program finds the starting position of the string within the contents of the inputbox. Because you know how long the string will be (in this case 7), you can remove the string from the current contents of the inputbox, and place this inside a new variable. Now you have a new string, without the one that was unchecked, you can clear the inputbox, and put the new one in.
Hope this helps!
I've been stuck on this for a little while. I'm basically trying to make an app that will allow me to load multiple files. At the moment, the app has 6 "browse" buttons, and next to those a corresponding Entry box. I'm trying to make the browse button send the file location string to the Entry box next to it. Unfortunately at the moment it appends the file name string to the bottom Entry box only. How can I get it to output to the box on the corresponding row?
Hopefully the solution will help me to understand how to get the correct values back when I press execute, too!
What i've got so far is below.
Thanks in advance
r=3
for i in range(6):
#CREATE A TEXTBOX
self.filelocation = Entry(self.master)
self.filelocation["width"] = 60
self.filelocation.focus_set()
self.filelocation.grid(row=r,column=1)
#CREATE A BUTTON WITH "ASK TO OPEN A FILE"
self.open_file = Button(self.master, text="Browse...", command=lambda i=i: self.browse_file(i))
self.open_file.grid(row=r, column=0) #put it beside the filelocation textbox
#CREATE A TEXT ENTRY FOR HELICOPTER ROUTE NAME
self.heliday = Entry(self.master)
self.heliday["width"] = 20
self.heliday.grid(row=r,column=2)
r = r+1
#now for a button
self.submit = Button(self.master, text="Execute!", command=self.start_processing, fg="red")
self.submit.grid(row=r+1, column=0)
def start_processing(self):
#more code here
print "processing"
def browse_file(self, i):
#put the result in self.filename
self.filename = tkFileDialog.askopenfilename(title="Open a file...")
#this will set the text of the self.filelocation
self.filelocation.insert(0,self.filename)
You have to keep references to all the Entry boxes. To do this you can change your entry creation to:
self.filelocation = []
for i in range(6):
#CREATE A TEXTBOX
self.filelocation.append(Entry(self.master))
self.filelocation[i]["width"] = 60
self.filelocation[i].focus_set()
self.filelocation[i].grid(row=r,column=1)
This way you keep references to all Entry boxes in a list. You can then make sure the filename gets in the correct Entry by using:
self.filelocation[i].insert(0,self.filename)