Create and call Variables In a for loop - python

i am trying to make it so that a user inputs a number, and that number of buttons are created Using TKinter, I have tried doing it by using the following, Where the Buttons are successfully created, however i am struggling with calling them in order to place them / display them on the grid (Added randint to simulate user input (User Input not limited to 9 and may be as high as 40))
from tkinter import *
from random import randint
inputValue = randint(3,9)
print(inputValue)
root = Tk()
while inputValue > 0: # for every number in inputted value
inputValue = int(inputValue) - 1 # take one
globals()['Sailor%s' % inputValue] = Button(root, text="Lap :" + str(inputValue), command=lambda: retrieve_input()) # Create the button function in the format 'Sailors{Inputnumber}'
('Sailors%s' % inputValue).grid(row=inputValue, column=1, columnspan=2) # Place the button (Doesn't work)
root.mainloop() # Does work (required)
Howerver the following does not work (It is meant to place the button),
('Sailors%s' % inputValue).grid(row=inputValue, column=1, columnspan=2) # Place the button (Doesn't work)
Can you think of a method i can use in order to create and place Amount of buttons?
Thanks in advance

You should never create dynamic variable names like you are attempting to do. It adds a lot of complexity, reduces clarity, and provides no real benefit.
Instead, use a dictionary or list to keep track of the buttons. In your case, however, since you're never using the buttons anywhere but in the loop you can just use a local variable.
Example using a local variable, in case you never need to access the button in code after you create it:
for count in range(inputValue):
button = Button(...)
button.grid(...)
Here's how you do it if you need to access the buttons later in your code:
buttons = []
for count in range(inputValue):
button = Button(...)
button.grid(...)
buttons.append(button)
With the above you can iterate over all of the buttons in buttons:
for button in buttons:
button.configure(state='disabled')
If you need to configure a single button, use its index:
button[0].configure(...)

You can calling grid on a string at the moment, which throws your error.
Your need to replace ('Sailors%s' % inputValue) with globals()['Sailor%s' % inputValue], with orders your buttons on individual rows, labeled 0-8.
So, your current code is:
from tkinter import *
from random import randint
inputValue = randint(3,9)
print(inputValue)
root = Tk()
while inputValue > 0: # for every number in inputted value
inputValue = int(inputValue) - 1 # take one
globals()['Sailor%s' % inputValue] = Button(root, text="Lap :" + str(inputValue), command=lambda: retrieve_input()) # Create the button function in the format 'Sailors{Inputnumber}'
globals()['Sailor%s' % inputValue].grid(row=inputValue, column=1, columnspan=2)
root.mainloop() # Does work (required)
When retrieve_input is defined, the code will work fine.
Just to point out, instead of inputValue = int(inputValue) - 1 you can use inputValue -= 1.

Related

Python Tkinter Listbox Back Forward Button

I have a listbox and I printed some contents with it. I want to put a back and forth button, as I press the button, it goes back and forth in the indexes and this selection appears in the listbox.
For example, 0 index is selected in the photo. When I press Forth, I want it to come to 1 index.
For this, I did research and analyzed the sample codes, but did not have the idea that I could not find the result.
import tkinter as tk
from tkinter import *
root = tk.Tk()
my_frame = Frame(root)
my_scrollbar = Scrollbar(my_frame, orient=VERTICAL)
list = Listbox(my_frame, width=85, height=20, yscrollcommand=my_scrollbar.set)
for i in range(50):
list.insert(END, str(i)+".index")
my_scrollbar.config(command=list.yview)
my_scrollbar.pack(side=RIGHT, fill=Y)
my_frame.pack()
list.pack()
So if I understand correctly, you'd like 2 buttons that allows you to cycle forward and backwards through the indexes.
So below is your code modified that allows you to visibly cycle through the listbox by using the GUI.
(EXPLANATION BELOW)
import tkinter as tk
from tkinter import *
root = tk.Tk()
my_frame = Frame(root)
my_frame.pack()
my_scrollbar = Scrollbar(my_frame)
my_scrollbar.pack(side=RIGHT, fill=Y)
listBox_0 = tk.Listbox(my_frame, width=85, height=20, yscrollcommand=my_scrollbar.set, selectmode='single')
listBox_0.pack()
my_scrollbar.config(command=listBox_0.yview)
def backButtonFN(my_frame):
curselection = listBox_0.curselection()
indexLB = curselection[0]
indexLB = indexLB - 1
if indexLB > 0 or indexLB == 0:
listBox_0.selection_clear(0, last='end')
listBox_0.selection_set(indexLB)
else:
None
pass
def forwardButtonFN(my_frame):
curselection = listBox_0.curselection() #current selection method
indexLB = curselection[0] #get value of index
indexLB = indexLB + 1 #add 1 to current value
listBox_0.selection_clear(0, last='end') #delete old selection
listBox_0.selection_set(indexLB) #select new index
pass
backButton = tk.Button(my_frame)
backButton.configure(text='BACK')
backButton.pack(anchor='s', side='left')
backButton.bind('<1>', backButtonFN, add='' )
forwardButton = tk.Button(my_frame)
forwardButton.configure(text='FOWARD')
forwardButton.pack(anchor='s', side='right')
forwardButton.bind('<1>', forwardButtonFN, add='' )
for i in range(50):
listBox_0.insert(END, str(i)+".index")
listBox_0.selection_set(0) #activate first index
Initially you called your listbox list, which is a big no-go as it can cause issues in terms of syntax later on down the line, so I changed the name of the Listbox to listBox_0, so this is your new Listbox as you originally intended.
once I did that, I created 2 functions for each of the forward and backwards buttons, I also had to rearrange the code to allow it to <bind> correctly.
I started off by making the first index active, that essentially will give me a value for curselection(), I then took that value, turned it into an integer I could play with, then each time you press the button, it will turn that extracted index, add one to it, then remove the previous selection and then add the new updated selection with indexLB
Then the same process for backButtonFN except this time, I am deducting 1 instead of adding, and then making sure it doesn't continue to go back once it hits 0.
I've also changed your configuration for listBox_0 to selectmode='single' to allow you to select one item at a row, you can disable this if you'd like to select multiple.
It can definitely use some tidying up as the pro's may immediately see, but I hope this answers your question

Tkinter widget where User chooses parameter values, but how do I store these values?

My objective is to create three variables called 'Number of pipes','Anisotropy ratio', 'Filter depth (in cm)' which values are chosen by the user (through a User interface). I achieved to create the scrollbar of Tkinter and to enter values but these are not stored as variables. Could someone give me a hand solving that? I am new in Python and specially I am struggling with Tkinter. The code is developped in Python 3.7:
root = tk.Tk()
root.geometry('1000x1000')
#Description show in window
info=tk.Label(root,anchor='e')
info.pack()
#Parameters
parameters = ('Number of pipes','Anisotropy ratio', 'Filter depth (in cm)')
def ask_parameter(entry):
user_pipes = str (entry['Number of pipes'].get())
user_aniso = str (entry['Anisotropy ratio'].get()) #effective screen length = b
user_depth = str (entry['Filter depth (in cm)'].get())
print(user_pipes,user_aniso,user_depth)
#if parameters
# return True
# else:
# tkinter.messagebox.showwarning ('Only numbers', 'Try again')
# return True
#
def form(parameters):
entry = {}
for parameter in parameters:
print(parameter)
row = tk.Frame(root)
lab = tk.Label(row, width=15, text=parameter+": ", anchor='w')
ent = tk.Entry(row)
row.pack(side=tk.TOP,
fill=tk.X,
padx=2,
pady=2)
lab.pack(side=tk.LEFT)
ent.pack(side=tk.RIGHT, expand=tk.YES,fill=tk.X)
entry[parameter] = ent
return entry
if __name__ == '__main__':
ents = form(parameters)
save_button = tk.Button(root)
save_button.configure(text='Save', command=lambda: ask_parameter(ents))
save_button.pack()
root.mainloop()
Any problem is shown with the code but the parameters are not stored as variables with the entered values.
Thank you very much for your time.
Your code is storing the values inputted through the GUI to variables, your problem is probably that your variables aren't global, so you won't be able to use them outside of the function they are created in. you can either get around this with a return, or make them global by putting global varname above the definition of each variable as well as at the start of every function that will use it.

Tkinter - Creating multiple check boxes using a loop

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()
)

Trying to produce function for a custom number of tkinter Entry fields.

I've been trying to create a piece of code that would take a integer as a argument and create that number of tkinter entry fields. With a submit button at the end that would retrieve the data from the fields add these data to a list then close the window.
I have been able to get it working however I cant find a way to convert this to a callable function; a requirement to use it with the rest of my program.
This is the code I have produced so far, thanks:
import tkinter as tk
b = input("Enter: ")
b = int(b)
root = tk.Tk()
newdict = dict()
outputs = list()
for i in range(b):
newdict["entry" + str(i)] = tk.Entry(root)
newdict["entry" + str(i)].pack()
button1 = tk.Button(root, text="Submit", command=lambda: Get(newdict))
button1.pack()
def Get(newdict):
for j in range(b):
outputs.append(newdict["entry" + str(j)].get())
root.quit()
root.mainloop()
print(outputs)
The basic idea is to create a window, then use the wait_window method to wait for the window to be destroyed. Once it has been destroyed you can return some value.
The problem is that the values you want to fetch must not be attributes of the window, since it will have been destroyed by the time you are ready to fetch them. You need to set up your code to save the values before the window is destroyed.
A simple way is to provide an "OK" button which gets the values and then destroys the window. Another way would be to put a trace on variables associated with each entry, and save the values immediately as they are edited.
Which method you choose depends on what behavior you want when the user clicks the window control to close the window (eg: the red circle on OSX, the [x] button on windows, etc). Do you want to return what they had input, or do you treat that as a cancel action and return nothing?
Here's a simple example using an OK button. This example assumes that you aren't already running a GUI, and that this is to be run as part of a non-GUI application.
import tkinter as tk
class Dialog(object):
def show(self, num_fields):
self.num_fields = num_fields
self.root = tk.Tk()
self.entries = []
for i in range(num_fields):
entry = tk.Entry(self.root)
entry.pack(fill="x")
self.entries.append(entry)
ok = tk.Button(self.root, text="OK", command=self.ok)
ok.pack(side="bottom", anchor="e", pady=(10,0), padx=10)
# wait for the window to be destroyed, then
# return the values. If the user clicks the OK button
# the values will be set; if they cancel the dialog
# this will return None.
self.values = None
self.root.wait_window()
return self.values
def ok(self):
# save all the values, then destroy the window
self.values = []
for i in range(self.num_fields):
self.values.append(self.entries[i].get())
self.root.destroy()
Assuming you're running a non-gui program, here's an example of how you would use this class:
b = input("Enter: ")
b = int(b)
result = Dialog().show(b)
print("result:", result)

How do I create a button in Python Tkinter to increase integer variable by 1 and display that variable?

I am trying to create a Tkinter program that will store an int variable, and increase that int variable by 1 each time I click a button, and then display the variable so I can see that it starts out as 0, and then each time I click the button it goes up by 1. I am using python 3.4.
import sys
import math
from tkinter import *
root = Tk()
root.geometry("200x200")
root.title("My Button Increaser")
counter = 0
def nClick():
counter + 1
def main_click():
mLabel = Label(root, text = nClick).pack()
mButton1 = Button(text = "Increase", command = main_click, fg = "dark green", bg = "white").pack()
root.mainloop()
Ok so there are a few things wrong with your code so far. My answer basically changes what you have already into the easiest way for it to do what you want.
Firstly you import libraries that you don't need/use (you may need them in your whole code, but for this question include a minimal example only). Next you must deifne the counter variable as a global variable, so that it will be the same in the function (do this inside the function as well). Also you must change counter + 1 to counter += 1 so it increments the variable
Now the code will be able to count, but you have set variables as Buttons, but then made them None type objects, to change this .pack() the variable on the next line. You can get rid of the second function as you only need one, then you change the command of the button and its text to counter. Now to update the text in the button, you use .config(text = counter) which configures the button.
Here is the final solution (changes button value and has no label, but this is easily changed):
from tkinter import *
root = Tk()
root.geometry("200x200")
root.title("My Button Increaser")
global counter
counter = 0
def nClick():
global counter
counter += 1
mButton1.config(text = counter)
mButton1 = Button(text = counter, command = nClick, fg = "darkgreen", bg = "white")
mButton1.pack()
root.mainloop()
hope that helps!
You could use Tkinter variables. They are specially useful when you need to modify a data that other widgets might interact with. Here is a look alike code to the one in the question, but instead of defining counter as a normal variable, it is a variable from Tkinter.
import tkinter
import sys
root = tkinter.Tk()
root.geometry("200x200")
root.title("His Button Increaser")
counter = tkinter.IntVar()
def onClick(event=None):
counter.set(counter.get() + 1)
tkinter.Label(root, textvariable=counter).pack()
tkinter.Button(root, text="Increase", command=onClick, fg="dark green", bg = "white").pack()
root.mainloop()
Instead of passing the value this variable holds to the text attribute of the Label, we assign the variable to textvariable attribute, so when the value of the variable gets updated, Label would update the displayed text accordingly.
When you want to change the value of the variable, you'd need to call the set() method of the variable object (see onClick) instead of assigning the value directly to it.

Categories

Resources