Tkinter: retrieve Radiobutton column and row value - python

Here How to retrieve the row and column information of a button and use this to alter its settings in python I've found a nice code that I would like to alter and use.
Here's the original:
from tkinter import *
root = Tk()
def showGrid():
row = btn.grid_info()['row'] # Row of the button
column = btn.grid_info()['column'] # grid_info will return dictionary with all grid elements (row, column, ipadx, ipday, sticky, rowspan and columnspan)
print("Grid position of 'btn': {} {}".format(row, column))
btn = Button(root, text = 'Click me!', command = showGrid)
btn.grid(row = 0, column = 0)
root.mainloop()
If the buttons are 2, how the def would recognize which button I've clicked?
I've tried to change the button into a Radiobutton (which is fine for me), set a value, set a variable, ecc, tried to used .get() and so on but I am unable to clearly identify the single radiobutton.
Any suggestion?
Best

Using lambda might help.
from tkinter import *
root = Tk()
def showGrid(widget):
row = widget.grid_info()['row'] # Row of the button
column = widget.grid_info()['column'] # grid_info will return dictionary with all grid elements (row, column, ipadx, ipday, sticky, rowspan and columnspan)
print("Grid position of 'btn': {} {}".format(row, column))
btn = Button(root, text = 'Click me!', command = lambda: showGrid(btn))
btn.grid(row = 0, column = 0)
btn1 = Button(root, text = 'Click me!', command = lambda: showGrid(btn1))
btn1.grid(row = 1, column = 0)
root.mainloop()

Related

Tkinter update label with variable from inside function

I'm using Tkinter to create a window with an entry field and a button. When the button is pressed and a certain condition is not met, I need my_label2 to show a specific text, in this case 'Not Valid'. Otherwise, I need the my_label2 to be blank. I have the variable label_text inside a function that is called by the button press, but I get an error saying that label_text is not defined. Can someone help me out with this?
root = tk.Tk()
def my_function():
valid = #this variable is either true or false
if valid :
label_text = ''
else :
label_text = 'Not Valid'
my_label = tk.Label(root, text = "Enter text: ")
my_label.grid(row = 0, column = 0)
my_entry = tk.Entry(root)
my_entry.grid(row = 0, column = 1)
my_button = tk.Button(root, text = "Submit", command = my_function)
my_button.grid(row = 1, column = 1)
my_label2 = tk.Label(root, textvariable = label_text)
my_label2.grid(row = 2, column = 1)
root.mainloop()
Tkinter Variables are different from normal variables. To create one:
label_text = tk.StringVar()
Then, rather than assigning to the variable, you nee to use the set method:
label_text.set('')
or
label_text.set('Not Valid')
See: http://effbot.org/tkinterbook/variable.htm

How to use a dictionary to create tkinter check boxes, then check which are ticked

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!

tkinter: dynamically create and delete entry

I am using tkinter to create a small GUI for some Python scripts. For this GUI I need a Combobox named combo with three options:
"none" (the user wants to hand in no data)
"constant" (the user wants to hand in only one value)
"adjustable" (the user wants to hand in more than one value)
Depending on the choice done in combo, a different number of entrys should
appear. The first option should show no entry (and delete all "leftover"
entrys), the second one should show only one entry and the third one should show
two entrys. For me it's no problem to create these entrys but I don't know how
to make them disappear. Once they were created they stay until the GUI is
closed.
I tried something like this:
import tkinter as tk
master = tk.Tk()
var1 = tk.StringVar()
var2 = tk.StringVar()
def submit():
if choice.get() == "none": # all entry boxes schould disappear
entry1.destroy()
entry2.destroy()
if choice.get() == "constant": # only 1 entry box schould be visible
entry1 = tk.Entry(master, textvariable = var1)
entry1.grid(column = 0, row = 1)
entry2.destroy()
if choice.get() == "adjustable": # all entry boxes should be visible
entry1 = tk.Entry(master, textvariable = var1)
entry1.grid(column = 0, row = 1)
entry2 = tk.Entry(master, textvariable = var1)
entry2.grid(column = 0, row = 2)
choice = tk.StringVar(value = "none")
combo = ttk.Combobox(master, textvariable = choice, state = "readonly")
combo["values"] = ("none", "constant", "adjustable")
combo.grid(column = 0, row = 0)
action = tk.Button(master, text = "confirm", command = submit)
action.grid(column=1, row=0)
master.mainloop()
But as I said before, once a entry was created it did not disappear anymore. I
also tried entry.grid_forget() insted of entry.destroy() but this also
didn't work.
I also tryed to declare entry1 and entry1 outside of submit() but then I
don't know how to recreate these entrys once they were destroyed.
Thanks to the help of jasonharper I found a solution while working at another script. The code should look something like this:
import tkinter as tk
from tkinter import ttk
master = tk.Tk()
var1 = tk.StringVar()
var2 = tk.StringVar()
def submit():
if choice.get() == "none": # all entry boxes schould disappear
entry1.grid_remove()
entry2.grid_remove()
if choice.get() == "constant": # only 1 entry box schould be visible
entry1.grid(column = 0, row = 1)
entry2.grid_remove()
if choice.get() == "adjustable": # all entry boxes should be visible
entry1.grid(column = 0, row = 1)
entry2.grid(column = 0, row = 2)
choice = tk.StringVar(value = "none")
combo = ttk.Combobox(master, textvariable = choice, state = "readonly")
combo["values"] = ("none", "constant", "adjustable")
combo.grid(column = 0, row = 0)
entry1 = tk.Entry(master, textvariable = var1)
entry2 = tk.Entry(master, textvariable = var2)
action = tk.Button(master, text = "confirm", command = submit)
action.grid(column=1, row=0)
master.mainloop()
Now all entrys are deleted or created when wanted. To delete the text at the entrys You only have to add entry.delete(0,"end").
I hope this will also help others.

Getting and calculating stuff through tkinter widets

I was wondering how to calculate stuff using tkinter buttons. I'm making a simple program to calculate seconds to hours:minutes:seconds. The user inputs an integer using the entry widget on the seconds box and when they press calculate, they get the result via the converted time line. I'm confused on how to start calculating it. I know you get the integer via .get, but I'm stuck on how to do that and calculate it in a h:m:s format. This is my code so far.
import tkinter
from tkinter import *
class TimeConverterUI():
def __init__(self):
self.root_window = Tk()
self.root_window.geometry('400x150')
self.root_window.title('Seconds Converter')
self.text()
self.calculate_button()
self.quit_button()
self.root_window.wait_window()
def text(self):
row_label = tkinter.Label(
master = self.root_window, text = 'Seconds: ')
row_label.grid( row = 0, column = 0, columnspan=2, padx=10, pady=10,
sticky = tkinter.W)
secondsEntry = Entry(master = self.root_window)
secondsEntry.grid(row = 0, column = 1)
row_label = tkinter.Label(
master = self.root_window, text = 'Converted Time(H:M:S): ').grid(row=1)
def calculate_button(self):
quit = Button(self.root_window, text = "Calculate", command = self.calculate)
quit.grid(row = 3, column = 0, columnspan = 3, pady=20,
sticky = tkinter.W)
def calculate(self):
pass
def quit_button(self):
quit = Button(self.root_window, text = "Quit", command = self.quit)
quit.grid(row = 3, column = 3, columnspan = 3, pady=20,
sticky = tkinter.E)
def quit(self) -> bool:
self.root_window.destroy()
return True
if __name__ == '__main__':
convert=TimeConverterUI()
First break this code below into 2 lines if you ever want to use row_label later because this will return NoneType. You should define it first then use .grid on it (just like your button).
row_label = tkinter.Label(
master = self.root_window, text = 'Converted Time(H:M:S): ').grid(row=1)
Now you can create another label to show the result. Remember to put self. before its name so you can use it in the calculate function. Also change secondsEntry to self.secondsEntry for the same reason.Now you just use int(self.secondsEntry.get()) in that function and do the required calculations. Then set the result to that result label with .configure(text=str(result))

Editing an object in a Listbox and the list where it comes from

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

Categories