tkinter generate buttons with names and pass names as an argument when clicked
Question:
I have a code that generates 12 buttons with names from a list. I’d like to pass those names to a function but currently only last text from button is remembered and it’s being used for all buttons.
Is there a way (except creating all buttons manually), to have it worked, meaning that proper button name will be passed further?
import tkinter as tk
from tkinter import *
notes_list = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#']
c = 0
r = 0
def notes(note):
major_scale_steps = [2,2,1,2,2,2,1]
minor_scale_notes = [2,1,2,2,1,2,2]
#find where provided note is located and rearrange notes_list accordingly
ind = notes_list.index(note)
n_tran = notes_list[ind:]+notes_list[:ind]
#gather notes using major_scale_steps
major_scale = [n_tran[0],n_tran[2],n_tran[4],n_tran[5],n_tran[7],n_tran[9], n_tran[11], n_tran[0]]
minor_scale = [n_tran[0],n_tran[2],n_tran[3],n_tran[5],n_tran[7],n_tran[8], n_tran[10], n_tran[0]]
return major_scale
def clicked():
te = notes(i.config('text')[-1])
#place label with results
test = tk.Label(window, text=te)
test.place(x=100, y=150)
window = tk.Tk()
window.title('Guitar notes')
window.geometry('320x250')
#create buttons
for i in notes_list:
i = Button(window, text=i, command=clicked)
i.config(height=2, width=10)
i.grid(column=c, row=r)
c+=1
if c == 4:
c=0
r+=1
window.mainloop()
Answers:
It is because you used i
inside clicked()
. When clicked()
is executed, i
will be the last button created in the main for loop.
You should instead pass the required note
to clicked()
using functools.partial()
and use this note
to update the test
label.
Also better create the test
label outside clicked()
and update its text inside the function.
import tkinter as tk
from functools import partial
notes_list = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#']
def notes(note):
#major_scale_steps = [2,2,1,2,2,2,1]
#minor_scale_notes = [2,1,2,2,1,2,2]
#find where provided note is located and rearrange notes_list accordingly
ind = notes_list.index(note)
n_tran = notes_list[ind:]+notes_list[:ind]
#gather notes using major_scale_steps
major_scale = [n_tran[0],n_tran[2],n_tran[4],n_tran[5],n_tran[7],n_tran[9], n_tran[11], n_tran[0]]
# shorter version of above:
#major_scale = [n_tran[x] for x in (0, 2, 4, 5, 7, 9, 11, 0)]
#minor_scale = [n_tran[0],n_tran[2],n_tran[3],n_tran[5],n_tran[7],n_tran[8], n_tran[10], n_tran[0]]
return major_scale
def clicked(note):
# update test label
test.config(text=notes(note))
window = tk.Tk()
window.title('Guitar notes')
window.geometry('320x250')
# create test label here
test = tk.Label(window)
test.place(x=100, y=150)
#create buttons
for i, note in enumerate(notes_list):
btn = tk.Button(window, text=note, width=10, height=2, command=partial(clicked, note))
btn.grid(column=i%4, row=i//4)
window.mainloop()
I have a code that generates 12 buttons with names from a list. I’d like to pass those names to a function but currently only last text from button is remembered and it’s being used for all buttons.
Is there a way (except creating all buttons manually), to have it worked, meaning that proper button name will be passed further?
import tkinter as tk
from tkinter import *
notes_list = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#']
c = 0
r = 0
def notes(note):
major_scale_steps = [2,2,1,2,2,2,1]
minor_scale_notes = [2,1,2,2,1,2,2]
#find where provided note is located and rearrange notes_list accordingly
ind = notes_list.index(note)
n_tran = notes_list[ind:]+notes_list[:ind]
#gather notes using major_scale_steps
major_scale = [n_tran[0],n_tran[2],n_tran[4],n_tran[5],n_tran[7],n_tran[9], n_tran[11], n_tran[0]]
minor_scale = [n_tran[0],n_tran[2],n_tran[3],n_tran[5],n_tran[7],n_tran[8], n_tran[10], n_tran[0]]
return major_scale
def clicked():
te = notes(i.config('text')[-1])
#place label with results
test = tk.Label(window, text=te)
test.place(x=100, y=150)
window = tk.Tk()
window.title('Guitar notes')
window.geometry('320x250')
#create buttons
for i in notes_list:
i = Button(window, text=i, command=clicked)
i.config(height=2, width=10)
i.grid(column=c, row=r)
c+=1
if c == 4:
c=0
r+=1
window.mainloop()
It is because you used i
inside clicked()
. When clicked()
is executed, i
will be the last button created in the main for loop.
You should instead pass the required note
to clicked()
using functools.partial()
and use this note
to update the test
label.
Also better create the test
label outside clicked()
and update its text inside the function.
import tkinter as tk
from functools import partial
notes_list = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#']
def notes(note):
#major_scale_steps = [2,2,1,2,2,2,1]
#minor_scale_notes = [2,1,2,2,1,2,2]
#find where provided note is located and rearrange notes_list accordingly
ind = notes_list.index(note)
n_tran = notes_list[ind:]+notes_list[:ind]
#gather notes using major_scale_steps
major_scale = [n_tran[0],n_tran[2],n_tran[4],n_tran[5],n_tran[7],n_tran[9], n_tran[11], n_tran[0]]
# shorter version of above:
#major_scale = [n_tran[x] for x in (0, 2, 4, 5, 7, 9, 11, 0)]
#minor_scale = [n_tran[0],n_tran[2],n_tran[3],n_tran[5],n_tran[7],n_tran[8], n_tran[10], n_tran[0]]
return major_scale
def clicked(note):
# update test label
test.config(text=notes(note))
window = tk.Tk()
window.title('Guitar notes')
window.geometry('320x250')
# create test label here
test = tk.Label(window)
test.place(x=100, y=150)
#create buttons
for i, note in enumerate(notes_list):
btn = tk.Button(window, text=note, width=10, height=2, command=partial(clicked, note))
btn.grid(column=i%4, row=i//4)
window.mainloop()