I would like to sort numbers when I clicked sort numbers Radiobutton. I already achieve this by calling a function when the Radiobutton is clicked. however, i couldn't sort numbers without calling a function.
this is my code
R1=Radiobutton(root,text="Sort Student Numbers",value=1)
R1.pack(anchor=W)
R2=Radiobutton(root,text="Sort Student Names",value=2)
R2.pack(anchor=W)
with open("student.json", "r"") as f:
data = json.load(f)
for d in data["student"]:
if value == 1:
data["student"].sort(key = lambda d: d["Numbers"])
elif value == 2:
data["student"].sort(key = lambda d: d["Names"])
label_1 = Label(frame , text="Name: %s" %(d["Names"]))
label_1.pack()
label_2 = Label(frame , text="Student Numbers: %d" %(d["Numbers"]))
label_2.pack()
if I say for example R1=Radiobutton(root,text="Sort Student Numbers",value=1, command = sorted_numbers(1)) everything works fine but the reason I don't want to use function calling is I would have to create 3 functions to achieve what I want. thanks
One way to solve this problem is to tie these radio buttons to a shared tkinter variable instance. When a radio button is selected, the value of the variable will be set to the value of the radio button, and then you can use that value in your code.
I haven't had time to test this code, but I have copied your code and modified it in a way that should work. This code assumes that you are importing everything from tkinter, using the line from tkinter import *; otherwise, you will need to do something like from tkinter import IntVar. There are several types of tkinter variable subclasses (IntVar, BooleanVar, etc.), and each has the methods get and set, which behave exactly as you'd expect (as demonstrated below).
# This is the variable that will store the value of the currently selected radio button
sort_value = IntVar()
# For each radio button, assign sort_value to the keyword parameter "variable"
R1=Radiobutton(root,text="Sort Student Numbers",variable=sort_value,value=1)
R1.pack(anchor=W)
R2=Radiobutton(root,text="Sort Student Names",variable=sort_value,value=2)
R2.pack(anchor=W)
with open("student.json", "r") as f:
data = json.load(f)
for d in data["student"]:
# sort_value is an IntVar, so sort_value.get returns a Python int
if sort_value.get() == 1:
data["student"].sort(key = lambda d: d["Numbers"])
elif sort_value.get() == 2:
data["student"].sort(key = lambda d: d["Names"])
label_1 = Label(frame , text="Name: %s" %(d["Names"]))
label_1.pack()
label_2 = Label(frame , text="Student Numbers: %d" %(d["Numbers"]))
label_2.pack()
Edit: Like Nae pointed out in the comments, you can also initialize the variable to a default value like this:
sort_value = IntVar(value=1)
Otherwise, its default value will be 0. I believe that by setting it to 1, this will also cause the radio button whose value is 1 to be selected by default.
I hope this helps.
Related
I am trying to limit the number of characters that can be input in a list of Entry widgets. I tried using the following:
def character_limit(entry_text):
if len(entry_text.get()) > 0:
entry_text.set(entry_text.get()[:10])
player_names = []
for i in range(num_players):
player_names.append(tk.StringVar())
player_names[i].trace("w", lambda *args: character_limit(player_names[i]))
player_name_entry = tk.Entry(top, textvariable=player_names[i])
player_name_entry.grid(row=i, column=0)
But this only limits the last Entry widget. How can I fix this?
The looping problem is a very commonly seen problem and to fix it, you have to store the current value of the iteration within the lambda itself:
def character_limit(*args, entry_text):
# if len(entry_text.get()) > 0: Can remove this line as it seems to not really be doing anything
entry_text.set(entry_text.get()[:10])
for i in range(num_players):
...
player_names[i].trace("w", lambda *args, i=i: character_limit(entry_text=player_names[i]))
The reason you use *args is because, trace passes 3 arguments to the function itself, that we don't need mostly.
But a more better method to do this will be to use validation for the entry widgets as this will prevent you needing to create a StringVar and trace its activity unnecessarily:
def character_limit(inp):
if len(inp) > 10:
return False
return True
player_names = []
vcmd = (root.register(character_limit), '%P')
for i in range(num_players):
player_name_entry = Entry(root, validate='key', validatecommand=vcmd)
player_name_entry.grid(row=i, column=0)
player_names.append(player_name_entry)
Read:
tkinter creating buttons in for loop passing command arguments
Interactively validating Entry widget content in tkinter
The problem is not related to the widget. Variable i is not local to the lambda functions, so the last value of i is used for every function.
To create local variables change your lambda into:
player_names[i].trace("w", lambda *args, n=i: character_limit(player_names[n]))
For a good description see https://docs.python.org/3/faq/programming.html#why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result
I need to implement an entry box that accepts only a range of DoubleVar values. I have referenced this question that was asked How to only allow certain parameters within an entry field on Tkinter, but I want the user to be notified (using a change in font colour or anything) while they are entering the values. I've read the documentation but this is something that I haven't come across.
I'm new to Tkinter, so please excuse me if this sounds very stupid
You can bind KeyRelease event of the Entry to a callback and check whether the input value is valid and within the required range, then update the foreground color of the Entry accordingly:
import tkinter as tk
root = tk.Tk()
def check_value(entry, min_value, max_value):
try:
value = float(entry.get().strip())
valid = min_value <= value <= max_value
except ValueError:
valid = False
entry.config(fg='black' if valid else 'red')
return valid # in case you want the checking result somewhere else
entry = tk.Entry(root)
entry.pack()
entry.bind('<KeyRelease>', lambda e: check_value(e.widget, 10, 20))
root.mainloop()
Use the validatecommand option for the Entry.
Some code fragments to show how it works:
root = tk.Tk()
vcmd = root.register(is_number)
e = ttk.Entry(pressf, justify='right', validate='key', validatecommand=(vcmd, '%P'))
def is_number(data):
"""Validate the contents of an entry widget as a float."""
if data == '':
return True
try:
rv = float(data)
if rv < 0:
return False
except ValueError:
return False
return True
This basically calls the validation function on every keypress. Only if the validation succeeds is the character added to the entry.
You can find a complete working example here.
Edit
Above is the "canonical" example of a validator. It allows or disallows characters into the Entry.
But you can also use it in other ways.
For example, you can always return True, but e.g. change the text color of the Entry to red if the value is not within the limits you want.
Trying to create a simple shopping list app, that will have user check the checkbox then enter the number of items. The button press should output a name with the number of item. for example, Apples 10, 1 banan etc. I would like to grow the fruits dictionary and later add other items to GUI, like meats/fish, general etc.
I thought the logical way would be to create a dictionary {key:value} where key would be the name of the checkbox, and a value will be the number inside entry text box.
Could you please show me how to do this? Thanks!
tried following some examples but could not understand how to extract a text="Apples" from checkbox and add this as a key into dictionary. Also played with lambda as output but nothing really materialized because all examples alreaddy had a dictionary created.
here is my example.
from tkinter import *
item_list = []
items_dict = {}
# mget gets output from checkboxes and prints results
def mget():
print(apples.get(), pears.get(), bananas.get(),apricots.get())
print(apples_qnty.get(),pears_qnty.get(), bananas_qnty.get(),
apricots_qnty.get())
#item_list.append(apples.get)
window = Tk()
apples = IntVar()
chk_apples = Checkbutton(window,text="Apples",
variable=apples).grid(row=1,sticky=W)
apples_qnty = StringVar()
apples_qnty = Entry(width=3)
apples_qnty.grid(column=1, row = 1)
item_list.append("Apples")
pears = IntVar()
chk_pears = Checkbutton(window,text="Pears",
variable=pears).grid(row=2,sticky=W)
pears_qnty = StringVar()
pears_qnty = Entry(width=3)
pears_qnty.grid(column=1, row = 2)
item_list.append("Pears")
bananas = IntVar()
Checkbutton(window,text="Bananas",
variable=bananas).grid(row=3,sticky=W)
bananas_qnty = StringVar()
bananas_qnty = Entry(width=3)
bananas_qnty.grid(column=1, row = 3)
item_list.append("Bananas")
apricots = IntVar()
Checkbutton(window,text="Apricots",
variable=apricots).grid(row=4,sticky=W)
apricots_qnty = StringVar()
apricots_qnty = Entry(width=3)
apricots_qnty.grid(column=1, row = 4)
item_list.append("Apricots")
print(item_list) # just to see list
Button(window,text="print check box states", command = mget).grid(row=5,sticky=W)
Button(window,text="exit", command =window.destroy).grid(row=5,sticky=E)
window.mainloop()
I would like the program to do the following:
user checks on checkbuttons and enters numbers of items.
pressing the button would output the items selected.
For example, Apples 4, Bananas 1 etc...
If the checkbutton is unchecked then don't output anything.
The variable parameter in each of your Checkbutton functions is currently IntVar(). This means that when it is checked it returns a value of 1 and when it is unchecked it returns the value of 0.
You can change the variable to a StringVar() and then assign it with a offvalue (unchecked) and onvalue (checked). This will give you scope to decide on what these values can return.
Using apples as an example:
#Your current code using IntVar()
apples = IntVar()
chk_apples = Checkbutton(window,text="Apples",
variable=apples).grid(row=1,sticky=W)
#Modification using StringVar()
apples = StringVar()
chk_apples = Checkbutton(window,text="Apples",
variable=apples, offvalue="", onvalue="Apples").grid(row=1,sticky=W)
By setting the onvalue as "Apples" the checkbutton will return the string variable "Apples".
I can then apply that logic for each of the fruits and then finally amend the mget function to the below:
def mget():
fruit_list = [apples.get(), pears.get(), bananas.get(), apricots.get()]
fruit_quantity =[apples_qnty.get(), pears_qnty.get() , bananas_qnty.get(),
apricots_qnty.get()]
fruit_dictionary = dict(zip(fruit_list,fruit_quantity)) #combines the two lists by their indices to create a dictionary
print(fruit_dictionary)
for keys, values in fruit_dictionary.items():
if keys == "" or values =="":
pass
else:
print(keys, values)
As a side note it would be good practice and improve readability if you assign each get.function to a variable i.e.
fruit_apple = apple.get()
I have a dynamically created Tkinter checkbutton widget, which takes in the contents of a list of usernames. I then displayed those names with a checkbox alongside.
What I need to do is obviously collect which usernames have been checked, so I can pass that off to another function to action.
How should I write the variable part of this so it creates a new list of chosen usernames?
What I have thus far:
def delprof_results(users_delprof):
for i in range(len(users_delprof)):
c = Checkbutton(resultsFrame, text=users_delprof[i], variable=users_delprof[i])
c.pack(anchor=W)
def delprof_del():
users_chosen = []
print str(users_delprof[i]).get() # Works up until this point. How to get individual variable with ID.
del_but = Button(resultsFrame, text="Delete", width=7, height=1, command=delprof_del)
del_but.pack(side=LEFT)
Thanks in advance,
Chris.
If you want to reach individual objects, simply keep a reference to the individual objects instead of creating objects while overwriting the same variable with each iteration of a loop like:
for i in range(30):
a = i
How to reach a's state where it was 13? Well, you can't as it's overwritten.
Instead, use collection types. In the example below I used dict:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
def upon_select(widget):
print("{}'s value is {}.".format(widget['text'], widget.var.get()))
if __name__ == '__main__':
root = tk.Tk()
names = {"Chester", "James", "Mike"}
username_cbs = dict()
for name in names:
username_cbs[name] = tk.Checkbutton(root, text=name,
onvalue=True, offvalue=False)
username_cbs[name].var = tk.BooleanVar()
username_cbs[name]['variable'] = username_cbs[name].var
username_cbs[name]['command'] = lambda w=username_cbs[name]: \
upon_select(w)
username_cbs[name].pack()
tk.mainloop()
You could make a list of values from the checkbuttons:
values = []
for i in range(len(users_delprof)):
v = IntVar()
c = Checkbutton(master, text="Don't show this again", variable=v)
c.var = v
values.append(v)
Now you can check the value by looking in the list values, and getting the value of a checkbutton with v.get().
I'm working on a GUI for a project in school. All the buttons that I have in my GUI are bound with functions that I have created. These functions call for already predefined functions. For some of the predefined functions, I need one or two arguments and I have solved that with entries. I type in the arguments in the right entries that are connected to the specific button and when I press the button, the function will run with the corresponding arguments.
The thing I want to do is to in some way when I press a button, the function should be saved to a list instead of being executed right away. And when I push the "run" button(a new button that I will create) everything in my list will be executed. I have been thinking about using a list box but I don't know exactly how they work or if its even possible to run a list box that contains a number of functions. Does someone have any ideas or solutions for me? Can I use the list box for this or is there something else that is better to use?
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.entry1 = IntVar()
self.entry2 = IntVar()
def do_something():
value1 = self.entry1.get()
value2 = self.entry2.get()
self.listbox.insert(END, "predefined_function(value1, value2)")
def run_listbox_contents():
pass
self.button = Button(frame, text="Move", command=lambda: do_something())
self.button.pack(side=TOP)
self.entry1.set("value1")
self.entry = Entry(frame, textvariable=self.entry1)
self.entry.pack(side=TOP)
self.entry2.set("value2")
self.entry = Entry(frame, textvariable=self.entry2)
self.entry.pack(side=TOP)
self.listbox = Listbox(master)
self.listbox.pack(side=TOP)
root = Tk()
app = App(root)
root.title("Mindstorms GUI")
root.geometry("800x1200")
root.mainloop()
root.destroy()
Just use a standard list.
something like this
def hest(txt):
print "hest: " +txt
def horse(txt):
print "horse: " + txt
funcList = []
funcList.append(hest)
funcList.append(horse)
for x in funcList:
x("Wow")
This outputs
hest: Wow
horse: Wow
Was this what you wanted?
If I were you, I wouldn't want to save functions to a list. I would suggest another solution for you.
I suppose you have heard of the principle of MVC (Model-View-Controller). In your case, the list box is a part of view, and the process that saves functions and then calls them at once is a part of controller. Separate them.
You might want to save and display any string in the list box to let the users know that the corresponding functions have been enlisted and ready to run. For example, save a string "Function1 aug1 aug2 aug3" or "Funtion2 aug1 aug2" or whatever you like as a handle of the corresponding function.
And for the controller part, write a function (let's say conductor()). It reads the handle strings from the list, parses them and calls the corresponding functions. Where you want to run the enlisted functions, there you just call conductor().
Update:
Due to your comment I understand that you are pretty new to program. Let me show you how to write a simplest parser with your given variable names.
def run_listbox():
to_do_list = #get the list of strings
for handle_string in to_do_list:
#Let's say you got
#handle_string = "Predfined_function1 value1 value2"
#by here
handle = handle_string.split(" ")
#Split the string by space, so you got
#handle = ["Predfined_function1", "value1", "value2"]
#by here
if handle[0] == "Predfined_function1":
Predfined_function1(handle[1], handle[2]) #Call Predfined_function1(value1, value2)
elif handle[0] == "Predfined_function2":
Predfined_function2(handle[1], handle[2])
#elif ...
#...
#elif ...
#...
#elif ...
#...
This is not a perfect parser, but I hope it could let you know what does a parser look like.