Updating label inside of function in tkinter - python-3.x

So i have problem with updating labels(i want to change colors of text) inside of function in tkinter. It works correctly when I use variable to store label however when i use list to do that, It doesnt. This how define labels.
for i in range(9):
for a in range(9):
if (NumbersSudoku[a][i] != 0):
label_frame = Frame(root, width=35, height=35, bg="white")
label_frame.grid_propagate(False)
label_frame.grid(row=i, column=a)
label[i][a] = Label(label_frame, text=NumbersSudoku[a][i], fg="blue", borderwidth=2, width=4, height=2,
relief="groove")
label[i][a].grid(row=i, column=a)
else:
label_frame = Frame(root, width=35, height=35, bg="white")
label_frame.grid_propagate(False)
label_frame.grid(row=i, column=a)
label[i][a] = Label(label_frame, borderwidth=2, textvariable=v[i][a], relief="groove", width=4, height=2,
fg='brown')
label[i][a].grid(row=i, column=a)
print(type(label[i][a]))
gnabel = Label(root, borderwidth=2, text='', relief="groove", width=4, height=2,
fg='brown')
gnabel.grid(row=14, column=14)
This is how i define list.
label = []
for x in range(9):
label.append([])
for y in range(9):
label[x].append([])
Inside a function printing types of variable and list returns diffrent results even tho when checked outside of function it returns the same <class 'tkinter.Label'>.
def base_info():
global gnabel
global label
print(type(gnabel)) # <class 'tkinter.Label'>
print(type(label)) # <class 'NoneType'>
............
# label[y][x] = Label(root, text=NumbersSudoku[y][x], fg="green").grid(row=y,column=x) # doesnt work
# gnabel = Label(root, text=NumbersSudoku[y][x], fg="orange").grid(row=14, column=14) # work

I can't really give you an answer in relation to your example, but I can give you an example using the function .config()
label = Label(root, fg = "green")
label.pack()
def update_label():
label.config(fg = "red")
update_label()
An explanation of what I've done is I've created the label with a foreground of green, and called a function which is going to change that to red. You can do this with text, with size, etc, as long as you use the config() function.
If you don't really understand what I'm saying, there are many youtube videos you can find which will give a better explanation of the config() function.

Related

How to get the values of all the OptionMenu widgets within a frame inside a canvas in Tkinter

I'm writing a minimalist image tagging app that will list out all the image files in a specific location alongside a dropdown menu to select the options for tagging the image. Once the images are tagged, I need to save the changes to a JSON file and I've got a button for that. How can we read all the options selected so that it can be written into a file?
Following is the code so far:
from tkinter import N, RIGHT, Button, OptionMenu, Scrollbar, StringVar, Tk, Canvas, Frame, Label
class App:
def __init__(self):
self.root = Tk()
self.tags = ['Apples', 'Oranges', 'Berries']
self.GetRows()
self.SaveButton()
self.root.mainloop()
def GetRows(self):
self.canvas = Canvas(self.root)
self.scroll_y = Scrollbar(self.root, orient="vertical", command=self.canvas.yview)
self.frame = Frame(self.canvas)
lst = [f"A01{str(i)}.JPG" for i in range(100)]
for idx, r in enumerate(lst):
filename = Label(self.frame, text=r)
filename.grid(row=idx+2, column=0, sticky=N)
label = StringVar()
drop = OptionMenu(self.frame, label, *self.tags)
drop.grid(row=idx+2, column=1)
# put the frame in the canvas
self.canvas.create_window(0, 0, anchor='nw', window=self.frame)
# make sure everything is displayed before configuring the scrollregion
self.canvas.update_idletasks()
self.canvas.configure(scrollregion=self.canvas.bbox('all'),
yscrollcommand=self.scroll_y.set)
self.canvas.pack(fill='both', expand=True, side='left')
self.scroll_y.pack(fill='y', side='right')
def SaveState(self):
pass
def SaveButton(self):
self.save_button = Button(self.root, text="Save Changes", padx=50, pady=10, command=self.SaveState)
self.save_button.pack(side=RIGHT)
if __name__ == '__main__':
App()
The SaveState method is what will be used to write the selections so far into a file.
Thanks in advance!
In order to make OptionMenu results available try modifying your code so that
all StringVars are accessible outside of GetRows.
def GetRows(self):
...
# Define label as a list
self.label = []
for idx, r in enumerate(lst):
filename = Label(self.frame, text=r)
filename.grid(row=idx+2, column=0, sticky=N)
label = StringVar()
drop = OptionMenu(self.frame, label, *self.tags)
drop.grid(row=idx+2, column=1)
# Save StringVar reference
self.label.append(label)
...
def SaveState(self):
self.data = dict()
# Retrieve results into dictionary
for i, a in enumerate(self.label):
self.data[f"A_{i}"] = a.get()
print(self.data)
Then use json.dump(self.data, a_file) to save it

Tkinter, How do I extract values from a slider, manipulate them and display them in a new grid on my tk window?

I have managed to create 3 sliders. Each slider controls the X, Y, and Z helical angles. I want to combine the three angles from the sliders into one np.array, manipulate that array and display the result in a new grid in the open window. I would like the result to update in real-time as the slider is moved.
I can manipulate the sliders, but I don't know how to a). combine the 3 results into one array b). manipulate those results c). display those results.
I am trying to update the slider values in two different grid locations and am failing at that. Here is the code I've written so far. Any feedback would be greatly appreciated!
import numpy as np
import tkinter as tk
from tkinter import ttk
# root window
root = tk.Tk()
root.geometry('900x800')
# root.attributes('-fullscreen', True)
root.resizable(True, True)
root.title('Slider Demo')
# slider current value
current_value_x = tk.DoubleVar()
current_value_y = tk.DoubleVar()
current_value_z = tk.DoubleVar()
def get_current_value():
return ['{: .2f}'.format(current_value_x.get()),
'{: .2f}'.format(current_value_y.get()),
'{: .2f}'.format(current_value_z.get())]
def slider_changed(event):
value_label_x.configure(text=get_current_value()[0])
value_label_y.configure(text=get_current_value()[1])
value_label_z.configure(text=get_current_value()[2])
# label for the slider
slider_label_helical_x = ttk.Label(root, text='Helical X:')
slider_label_helical_y = ttk.Label(root, text='Helical Y:')
slider_label_helical_z = ttk.Label(root, text='Helical Z:')
slider_label_helical_x.grid(column=0, row=0, sticky='w')
slider_label_helical_y.grid(column=0, row=1, sticky='w')
slider_label_helical_z.grid(column=0, row=2, sticky='w')
# slider
slider_x = ttk.Scale(root, from_=-180, to=180, orient='horizontal', command=slider_changed, variable=current_value_x)
slider_y = ttk.Scale(root, from_=-180, to=180, orient='horizontal', command=slider_changed, variable=current_value_y)
slider_z = ttk.Scale(root, from_=-180, to=180, orient='horizontal', command=slider_changed, variable=current_value_z)
slider_x.grid(column=1, row=0, sticky='we')
slider_y.grid(column=1, row=1, sticky='we')
slider_z.grid(column=1, row=2, sticky='we')
# Helical value label
value_label_x = ttk.Label(root, text=get_current_value()[0])
value_label_x.grid(column=2, row=0, sticky='n')
value_label_y = ttk.Label(root, text=get_current_value()[1])
value_label_y.grid(column=2, row=1, sticky='n')
value_label_z = ttk.Label(root, text=get_current_value()[2])
value_label_z.grid(column=2, row=2, sticky='n')
# Rotation Matrix Labels
rotationMatrixLabelHeader = ttk.Label(root, text='Rotation Matrix')
rotationMatrixLabel_1_1 = ttk.Label(root, text='(1,1)')
rotationMatrixValue_1_1 = ttk.Label(root, text=get_current_value()[0])
rotationMatrixLabelHeader.grid(column=0, row=3, sticky='w')
rotationMatrixLabel_1_1.grid(column=0, row=4, sticky='w')
rotationMatrixValue_1_1.grid(column=0, row=5, sticky='w')
root.mainloop()
So far, I have managed to place the 3 sliders where I want them, as well as their appropriate labels and outputs. In column 0, row 5, I'm trying to manipulate the value from the first slider and display that. At the moment, I can't even display the value from that slider, let alone manipulate it.
I have written a working solution from your original code. The below code shows the "original" Numpy array and the "changed" Nupmy array (reversed) in real time. The Matrix values are updated when you change one on sliders.
I have added several comments to the code where I have changed as explanation.
Code:
import numpy as np
import tkinter as tk
from tkinter import ttk
# root window
root = tk.Tk()
root.geometry("900x800")
# root.attributes('-fullscreen', True)
root.resizable(True, True)
root.title("Slider Demo")
# slider current value
current_value_x = tk.DoubleVar()
current_value_y = tk.DoubleVar()
current_value_z = tk.DoubleVar()
def get_current_value():
return [
"{: .2f}".format(current_value_x.get()),
"{: .2f}".format(current_value_y.get()),
"{: .2f}".format(current_value_z.get()),
]
def slider_changed(event):
current_values = get_current_value()
value_label_x.configure(text=current_values[0])
value_label_y.configure(text=current_values[1])
value_label_z.configure(text=current_values[2])
# Fill the numpy array with the "new" values when you change a slider.
# Probably here a float(X) casting would be nice.
np_arr = np.array([current_values[0], current_values[1], current_values[2]])
# Call the calculation and show method.
# You can implement the Array manipulations in this function.
calculate_and_show_matrix(np_arr)
def calculate_and_show_matrix(np_array):
"""
Here you can do anything with your Numpy array and show the value on the GUI.
Currently this function only show the original Numpy array and the reversed array.
#param np_array: The Numpy Array
#return: None
"""
# Set the "text" parameter of "rotationMatrixValue_1_1_orig" object to the "original" array value.
rotationMatrixValue_1_1_orig.configure(text="{}".format(np_array))
# This is the array manipulation (Reverse the array)
reversed_arr = np_array[::-1]
# Set the "text" parameter of "rotationMatrixValue_1_1_changed" object to the "changed" array value.
rotationMatrixValue_1_1_changed.configure(text="{}".format(reversed_arr))
# label for the slider
slider_label_helical_x = ttk.Label(root, text="Helical X:")
slider_label_helical_y = ttk.Label(root, text="Helical Y:")
slider_label_helical_z = ttk.Label(root, text="Helical Z:")
slider_label_helical_x.grid(column=0, row=0, sticky="w")
slider_label_helical_y.grid(column=0, row=1, sticky="w")
slider_label_helical_z.grid(column=0, row=2, sticky="w")
# slider
slider_x = ttk.Scale(
root, from_=-180, to=180, orient="horizontal", command=slider_changed, variable=current_value_x
)
slider_y = ttk.Scale(
root, from_=-180, to=180, orient="horizontal", command=slider_changed, variable=current_value_y
)
slider_z = ttk.Scale(
root, from_=-180, to=180, orient="horizontal", command=slider_changed, variable=current_value_z
)
slider_x.grid(column=1, row=0, sticky="we")
slider_y.grid(column=1, row=1, sticky="we")
slider_z.grid(column=1, row=2, sticky="we")
# Helical value label
value_label_x = ttk.Label(root, text=get_current_value()[0])
value_label_x.grid(column=2, row=0, sticky="n")
value_label_y = ttk.Label(root, text=get_current_value()[1])
value_label_y.grid(column=2, row=1, sticky="n")
value_label_z = ttk.Label(root, text=get_current_value()[2])
value_label_z.grid(column=2, row=2, sticky="n")
# Rotation Matrix Labels
rotationMatrixLabelHeaderOriginal = ttk.Label(root, text="Original rotation Matrix")
rotationMatrixValue_1_1_orig = ttk.Label(root)
rotationMatrixLabelHeaderChanged = ttk.Label(root, text="Changed rotation Matrix (Reversed)")
rotationMatrixValue_1_1_changed = ttk.Label(root)
rotationMatrixLabelHeaderOriginal.grid(column=0, row=3, sticky="w")
rotationMatrixValue_1_1_orig.grid(column=0, row=4, sticky="w")
rotationMatrixLabelHeaderChanged.grid(column=0, row=5, sticky="w")
rotationMatrixValue_1_1_changed.grid(column=0, row=6, sticky="w")
root.mainloop()
GUI:

How to add multiple entries in tkinter?

I am extremely new to Tkinter. I have been trying to create something that basically calculates the average of the marks inputted. I am trying to give the user the option to choose the number of subjects, and accordingly create that many entries.
from tkinter import *
root = Tk()
x, y, d = 0, 0, {}
for i in range(1, int(input('Enter no of subjects')) + 1):
sub1 = Entry(root, width=15, borderwidth=5)
sub1.grid(row=x, column=y)
max1 = Entry(root, width=15, borderwidth=5)
max1.grid(row=x, column=y+2)
sub1label = Label(root, text='Marks attained', bg='grey', fg='white')
sub1label.grid(row=x, column=y+1)
max_sub1label = Label(root, text='Max Marks', bg='grey', fg='white')
max_sub1label.grid(row=x, column=y+3)
x += 1
root.mainloop()
Is there a way to store the data inputted each time so as to compute the percentage acquired? Or is there another method I can use?
You can store the values in the list and then index the list later on with the required value and get the items you wants. Here is your corrected code:
from tkinter import *
root = Tk()
def show():
for i in range(tot_sub): # Loop through the number of subjects
attained = attained_marks[i].get() # Get the indexed item from the list and use get()
max_mark = max_marks[i].get()
print(f'Attained marks: {attained}, Max marks: {max_mark}') # Print the values out
attained_marks = [] # Empty list to populate later
max_marks = [] # Empty list to populate later
tot_sub = int(input('Enter no. of subjects: ')) # Number of subjects
for i in range(tot_sub):
sub1 = Entry(root, width=15, borderwidth=5)
sub1.grid(row=i, column=0)
attained_marks.append(sub1) # Append each entry to the list
max1 = Entry(root, width=15, borderwidth=5)
max1.grid(row=i, column=2)
max_marks.append(sub1) # Append each entry to the list
sub1label = Label(root, text='Marks attained', bg='grey', fg='white')
sub1label.grid(row=i, column=1, padx=5)
max_sub1label = Label(root, text='Max Marks', bg='grey', fg='white')
max_sub1label.grid(row=i, column=3, padx=5)
root.bind('<Return>',lambda e: show()) # For demonstration of getting all the data
root.mainloop()
I have also changed the loop a bit as you don't need to initialize x,y,d and so on, as it can be easily achieved from inside the loop itself. I have also expanded the code so you can understand easily. Also I dont recommend using input() as it is for the terminal, use an Entry instead.
Alternatively: You can also use a dict and avoid the use of 2 lists, the dictionary would be something like {'Alternative Marks':[att1,att2,...],'Maximum Mark':[max1,max2,...]}, but this would make looping through and indexing a bit more lengthy.

Forcing TKinter button output to bottom of window?

I am very new to using TKinter. I am making a TKinter window that displays the descriptive statistics of the wine quality data sets. The problem I am having is with the positioning. Even using pack(side=BOTTOM), the button for the histogram shows up next to the column option buttons I have, like so:
Ideally, what I would like the window to look like, is similar to this:
I tried making the button in the same place I made the label "Descriptive statistics", and then configuring it later, but while that keeps the button where I would like it to be, the histogram ends up in the same place.
Edit: I was originally using grid() to manually place everything, but, for aesthetic reasons, I didn't like how the spaces in between the buttons would adjust as more objects were added to the window. I was also getting a "can't use pack() and grid()" warning even though I had changed all pack()s to grid()s, specifically only because of my plot function, and I couldn't figure it out. So in the end I just made the switch from grid() to pack() to avoid continually getting that error.
My code:
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
#the main window
root = tk.Tk()
root.title('Descriptive statistics for vinho verde datasets')
#generate some labels
lbl1 = tk.Label(root, text = "Wine choice:")
lbl1.pack(side=TOP)
lbl2 = tk.Label(root, text = "Descriptive statistics:")
lbl2.pack(side=BOTTOM)
def wine_choice(opt):
#functions determining for which columns to output descriptive statistics
def describe(colm):
if opt == 'white':
res = white[colm].describe()
else:
res = red[colm].describe()
txt = "\nDescriptive statistics for {0} wine, {1}:\n\n{2}"
lbl2.config(text = txt.format(opt,colm,res))
def b_plot():
#figure that will contain the plot
fig = Figure(figsize = (5, 5), dpi = 75)
p1 = fig.add_subplot()
if opt == 'white':
p1.hist(white[colm])
else:
p1.hist(red[colm])
#creating the canvas containing figure and placing on the window
canvas = FigureCanvasTkAgg(fig, root)
canvas.draw()
canvas.get_tk_widget().pack(side=BOTTOM)
btn_p = tk.Button(root, command = b_plot, width=10, height=3, text = "Histogram").pack(side=BOTTOM)
lbl3 = tk.Label(root, text = "Pick an attribute to investigate:")
lbl3.pack(side=TOP)
#spawn attribute buttons after user chooses a wine
#generate buttons
btn3 = tk.Button(root, text='fixed acidity', width=10, height=3)
btn3.pack(side=LEFT)
btn3.bind('<Button-1>', lambda e: describe('fixed acidity'))
btn4 = tk.Button(root, text='volatile\nacidity', width=10, height=3)
btn4.pack(side=LEFT)
btn4.bind('<Button-1>', lambda e: describe('volatile acidity'))
btn5 = tk.Button(root, text='citric\nacid', width=10, height=3)
btn5.pack(side=LEFT)
btn5.bind('<Button-1>', lambda e: describe('citric acid'))
btn6 = tk.Button(root, text='residual\nsugar', width=10, height=3)
btn6.pack(side=LEFT)
btn6.bind('<Button-1>', lambda e: describe('residual sugar'))
btn7 = tk.Button(root, text='chlorides', width=10, height=3)
btn7.pack(side=LEFT)
btn7.bind('<Button-1>', lambda e: describe('chlorides'))
btn8 = tk.Button(root, text='free\nsulfur\ndioxide', width=10, height=3)
btn8.pack(side=LEFT)
btn8.bind('<Button-1>', lambda e: describe('free sulfur dioxide'))
btn9 = tk.Button(root, text='total\nsulfur\ndioxide', width=10, height=3)
btn9.pack(side=LEFT)
btn9.bind('<Button-1>', lambda e: describe('total sulfur dioxide'))
btn10 = tk.Button(root, text='density', width=10, height=3)
btn10.pack(side=LEFT)
btn10.bind('<Button-1>', lambda e: describe('density'))
btn11 = tk.Button(root, text='pH', width=10, height=3)
btn11.pack(side=LEFT)
btn11.bind('<Button-1>', lambda e: describe('pH'))
btn12 = tk.Button(root, text='sulphates', width=10, height=3)
btn12.pack(side=LEFT)
btn12.bind('<Button-1>', lambda e: describe('sulphates'))
btn13 = tk.Button(root, text='alcohol', width=10, height=3)
btn13.pack(side=LEFT)
btn13.bind('<Button-1>', lambda e: describe('alcohol'))
btn14 = tk.Button(root, text='quality', width=10, height=3)
btn14.pack(side=LEFT)
btn14.bind('<Button-1>', lambda e: describe('quality'))
#buttons for wine choices
btn1 = tk.Button(root, text = "white", width=10, height=2)
btn1.pack(side=TOP)
#remember which button user picks
btn1.bind('<Button-1>', lambda e: wine_choice('white'))
btn2 = tk.Button(root, text = "red", width=10, height=2)
btn2.pack(side=TOP)
btn2.bind('<Button-1>', lambda e: wine_choice('red'))
#must be called for window to be drawn and events to be processed
root.mainloop()
The solution is to break your UI into logical groups, and use frames to organize the logical groups. You can make what you have work, but it's much easier to use frames to organize your widgets.
I see perhaps four logical groups:
a set of two buttons stacked vertically
a dozen or so buttons aligned vertically
a block of statistics with a "histogram" button
a histogram
So, start by creating four frames, one for each of those sections. Stacking vertically is best done with pack.
Once you've done that, put the various widgets inside one of those frames. Each frame is independent from a layout perspective, so you can use grid or pack in each. Though, since each group seems to be a vertical or horizontal grouping, pack will probably work best in all cases since it excels at left-to-right and top-to-bottom layouts with the fewest lines of code.

Fix _tkinter.TclError: no events specified in binding

Got this code from somewhere online. I looked at the other SO answer but it didn't work for me. What should I fix this error
Question: How to fix "- _tkinter.TclError: no events specified in binding"
import tkinter as tk
fields = ['Email', 'Password', 'School']
def fetch(entries):
for entry in entries:
field = entry[0]
text = entry[1].get()
print('%s: "%s"' % (field, text))
def makeform(root, fields):
entries = []
for field in fields:
row = tk.Frame(root)
lab = tk.Label(row, width=15, text=field, anchor='w')
ent = tk.Entry(row)
row.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
lab.pack(side=tk.LEFT)
ent.pack(side=tk.RIGHT, expand=tk.YES, fill=tk.X)
entries.append((field, ent))
return entries
def getCreds():
root = tk.Tk()
ents = makeform(root, fields)
root.bind('', (lambda events=ents: fetch(e)))
b1 = tk.Button(root, text='Show',
command=(lambda events=ents: fetch(e)))
b1.pack(side=tk.LEFT, padx=5, pady=5)
b2 = tk.Button(root, text='Quit', command=root.quit)
b2.pack(side=tk.LEFT, padx=5, pady=5)
root.mainloop()
getCreds()
_tkinter.TclError: no events specified in binding
This should be relatively obvious. If you look at any of the many many examples of bind() on SO or google you will find that the first argument always has something specific in it and never an empty string.
Take some time to read up on tkinter-events-and-bindings.
There are 2 problems with you root.bind() 1st any time you click anywhere on the screen it will call the function. This is likely not what you want.
The 2nd problem both with your bind and the button command is your lambda. events=ents: fetch(e) you define your list of entries as events but then pass e to the function. So you have to correct that.
Personally I would create the list in the same place you define the root as well as define the root in the global name space. This will allow us to avoid the lambda as well.
import tkinter as tk
fields = ['Email', 'Password', 'School']
def fetch(_=None):
for ndex, entry in enumerate(entries):
print('{}: {}'.format(fields[ndex], entry.get()))
root = tk.Tk()
root.config(background='gray')
entries = []
for ndex, field in enumerate(fields):
tk.Label(root, width=15, text=field, anchor='w').grid(row=ndex, column=0, sticky='ew')
entries.append(tk.Entry(root))
entries[-1].grid(row=ndex, column=1, sticky='ew')
# The problem with root.bind is that it will constantly be calling the function anywhere you click on root.
root.bind('<Button-1>', fetch)
tk.Button(root, text='Show', command=fetch).grid(row=len(fields)+1, column=1, sticky='ew')
tk.Button(root, text='Quit', command=root.quit).grid(row=len(fields)+2, column=1, sticky='ew')
root.mainloop()

Resources