Tkinter create buttons from list each with its own function - python-3.x

I want to create buttons from a list and assign each button a function based on the list item. I tried below and the button doesn't respond to a click. I see solutions where a lambda function is used to pass a parameter to a function but I would like separate functions. Using Python 3.5 in Anaconda
import tkinter as tk
def North():
print('slected North')
def South():
print('slected South')
def East():
print('slected East')
def West():
print('slected West')
lst = ['North','South','East','West']
win = tk.Tk()
win.title = 'Compass'
for col,Direction in enumerate(lst):
butName = tk.Button(win, text = Direction, command = Direction)
butName.grid(row = 1, column = col)
win.mainloop()

Your list contains strings; it needs to contain functions
lst = [North,South,East,West]

Faster and better:
import tkinter as tk
def onbutton_click(label):
print('selected ', label)
lst = ['North','South','East','West']
win = tk.Tk()
win.title = 'Compass'
for col,Direction in enumerate(lst):
butName = tk.Button(win, text=Direction, command=lambda e=Direction: onbutton_click(e))
butName.grid(row=0, column=col)
win.mainloop()
or your way:
import tkinter as tk
def North():
print('slected North')
def South():
print('slected South')
def East():
print('slected East')
def West():
print('slected West')
lst = [North, South,East, West]
win = tk.Tk()
win.title = 'Compass'
for col,Direction in enumerate(lst):
butName = tk.Button(win, text=Direction.__name__, command=Direction)
butName.grid(row=0, column=col)
win.mainloop()
I also set the row as 0 cause there is no need to be 1.

Related

In tkinter entry widget, failed to type in Indian language using sanskrit/hindi keyboard

from tkinter import *
from tkinter import ttk
lst = ['रामायणसारः', 'देवयाज्ञिकपद्धतिः', 'तर्कसङ्ग्रहः',]
def check_input(event):
value = event.widget.get()
if value == '':
data = lst
else:
data = []
for item in lst:
if value.lower() in item.lower():
data.append(item)
update(data)
def update(data):
# Clear the Combobox
menu.delete(0, END)
# Add values to the combobox
for value in data:
menu.insert(END, value)
def fillout(event):
combo_box.delete(0, END)
combo_box.insert(0, menu.get(menu.curselection()))
combo_box = Entry(root, width=10, font=("Times", 12))
combo_box.place(relx=0.20, rely=0.4, relwidth=0.45, relheight=0.1)
combo_box.bind('<KeyRelease>', check_input)
menu = Listbox(root)
menu.place(relx=0.20, rely=0.5, relwidth=0.45, relheight=0.1)
menu.bind("<<ListboxSelect>>", fillout)
update(lst)
while typing Indian language(Sanskrit/Hindi) text in entry box using sanskrit/hindi keyboard, it shows question mark, please help to rectify the issue.
ref
I have also tried encoding('utf-8') with entry widget, but it doesn't worked.

Drag Drop List in Tkinter

from tkinter import *
import tkinter as tk
root = tk.Tk()
def add_many_songs():
# Loop thru to fill list box
for song in range(11):
playlist_box.insert(END, song)
playlist_box =tk.Listbox(root,bg="black", fg="green", width=60, selectbackground="green", selectforeground='black',font = 20)
playlist_box.grid(row=0, column=0)
add_many_songs()
class DragDropListbox(tk.Listbox):
""" A Tkinter listbox with drag'n'drop reordering of entries. """
def __init__(self, master, **kw):
kw['selectmode'] = tk.SINGLE
tk.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.setCurrent)
self.bind('<B1-Motion>', self.shiftSelection)
self.curIndex = None
def setCurrent(self, event):
self.curIndex = self.nearest(event.y)
def shiftSelection(self, event):
i = self.nearest(event.y)
if i < self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i+1, x)
self.curIndex = i
elif i > self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i-1, x)
self.curIndex = i
##I found this code that does drag and drop features within tkinter list. I got it to work with the example code. However, I am not able to get it to work within the attached code. I am still learning Python.
You should use the class DragDropListbox instead of tk.Listbox when creating playlist_box:
import tkinter as tk
def add_many_songs():
# Loop thru to fill list box
for song in range(11):
playlist_box.insert(tk.END, song)
class DragDropListbox(tk.Listbox):
""" A Tkinter listbox with drag'n'drop reordering of entries. """
def __init__(self, master, **kw):
kw['selectmode'] = tk.SINGLE
tk.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.setCurrent)
self.bind('<B1-Motion>', self.shiftSelection)
self.curIndex = None
def setCurrent(self, event):
self.curIndex = self.nearest(event.y)
def shiftSelection(self, event):
i = self.nearest(event.y)
if i < self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i+1, x)
self.curIndex = i
elif i > self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i-1, x)
self.curIndex = i
root = tk.Tk()
playlist_box = DragDropListbox(root,bg="black", fg="green", width=60, selectbackground="green", selectforeground='black',font = 20)
playlist_box.grid(row=0, column=0)
add_many_songs()
root.mainloop()
Note that it is not recommended to import tkinter like below:
from tkinter import *
import tkinter as tk
Just use import tkinter as tk.

Retrieve choosen value of a ttk.combobox

I am creating a user interface where there is a first window that ask the user to choose a parameter within a list of choices. Here is an MWE :
from tkinter import *
import tkinter.ttk as ttk
Port=''
root = Tk()
PortCOM = ttk.Combobox(root, values=[1,2,3,4,5,6])
PortCOM.bind("<<ComboboxSelected>>",Port)
PortCOM.pack ()
root.mainloop()
print(Port)
So i already tried that but also :
Port = PortCOM.get
Port = PortCOM.cget
With that last try, i get that error message :
<bound method Misc.cget of <tkinter.ttk.Combobox object .!combobox>>
For example, if the user choose the value '4' in my list of values, i want it to be stored in the variable 'Port'.
You do not need a bind to track the variable. You can do this with a StringVar.
That said you cannot just call Port = PortCOM.get in the global as the code is initiated and expect to get anything. 1st issue with that the correct syntax is Port = PortCOM.get() with parenthesis. 2nd you are calling get() at init and thus the only possible value would be an empty string if not an error.
The next issue I see is the bind() this is not doing what you think it is doing. The bind is used to call a function not to update a variable directly.
The correct use of Combobox is to use a textvariable with an IntVar() or StringVar() depending on the values and then use get() on that var when you need it in a function.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
textVar = StringVar(root)
textVar.set('')
PortCOM = ttk.Combobox(root, textvariable=textVar, values=[1, 2, 3, 4, 5, 6])
PortCOM.pack()
def print_value():
print(textVar.get())
Button(root, text='Print Value', command=print_value).pack()
root.mainloop()
If you really want to use bind() for some reason like to have the selection immediately do something when selected then try this instead.
Make sure the bind call is after the function used to do something with your combobox.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
textVar = StringVar(root)
textVar.set('')
PortCOM = ttk.Combobox(root, textvariable=textVar, values=[1, 2, 3, 4, 5, 6])
PortCOM.pack()
# Note the _ in the argument section of the function.
# A bind will send an event to the function selected unless you use a lambda.
# so to deal with events we don't care about we have a few options.
# We can use an underscore to just except any argument and do nothing with it.
# We could also do event=None but typically I only use that when a function might use the event variable but not always.
def print_value(_):
print(textVar.get())
print(PortCOM.get())
PortCOM.bind("<<ComboboxSelected>>", print_value)
root.mainloop()
And if you don't want extra button you can just do this
from tkinter import *
import tkinter.ttk as ttk
Port=''
root = Tk()
def set_port(_):
global Port
Port = PortCOM.get()
print(Port)
PortCOM = ttk.Combobox(root, values=[1,2,3,4,5,6])
PortCOM.bind("<<ComboboxSelected>>", set_port)
PortCOM.pack ()
root.mainloop()
Look at this OO example to use all the power of the combo ;)
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class Main(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.values = ('Apple','Banana','Orange','Grapes','Watermelon','Plum','Strawberries','Pear')
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1)
f = ttk.Frame()
ttk.Label(f, text = "Combobox").pack()
self.cbCombo = ttk.Combobox(f,state='readonly',values=self.values)
self.cbCombo.pack()
w = ttk.Frame()
ttk.Button(w, text="Callback", command=self.on_callback).pack()
ttk.Button(w, text="Reset", command=self.on_reset).pack()
ttk.Button(w, text="Set", command=self.on_set).pack()
ttk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
def on_callback(self,):
if self.cbCombo.current() != -1:
msg = "You have selected:\n{0}".format(self.cbCombo.get())
else:
msg = "You did not select anything"
messagebox.showinfo(self.parent.title(), msg)
def on_reset(self):
self.cbCombo.set('')
def on_set(self):
self.cbCombo.current(0)
def on_close(self):
self.parent.on_exit()
class App(tk.Tk):
"""Start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_title()
self.set_style()
frame = Main(self,)
frame.pack(fill=tk.BOTH, expand=1)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self):
s = "{0}".format('Simple App')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel("Simple App", "Do you want to quit?", parent=self):
self.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Automatically switching checkboxes on tkinter

I would like to switch on the checkboxes (0, 2, 4) automatically with the click of a button. I have the following code. For some reason it dont work. Please help me.
from tkinter import *
class Error(Frame):
def Widgets(self):
for i in range(len(self.X)):
self.X[i] = Checkbutton(self, text="%d"%(i,))
self.X[i].grid(row=i, sticky=W)
self.X[i].configure(variable = ("var_%d"%(i,)))
self.button = Button(self, text = "set", command = self.test)
self.button.grid(row=5, sticky=W)
def test(self):
for i in range(len(self.X)):
if i == 0 or i == 2 or i == 4:
set (("var_%d"%(i,))) == 1
def __init__(self,initial):
super(Error,self).__init__(initial)
self.X = [{},{},{},{},{}]
self.grid()
self.Widgets()
Window = Tk()
Tool = Error(Window)
Window.mainloop()
The way to handle checkboxes is to associate each box with a variable which reflects wether the box is checked or not.
For an array of checkboxes it is convenient to store these variables in a list. The way I would do it is to create an empty list and then append variables as I go along.
In the function test() I use enumerate in the for-loop as this is the recommended way to generate an index of the list.
from tkinter import *
class Error(Frame):
def __init__(self, master):
super(Error,self).__init__(master)
self.box_list = [] # List to holld checbox variables
self.grid()
self.Widgets()
def Widgets(self):
for i in range(5):
var = BooleanVar() # Create variable to associate with box
cb = Checkbutton(self, text="%d"%(i,))
cb.grid(row=i, sticky=W)
cb.configure(variable=var)
self.box_list.append(var) # Append checkbox variable to list
self.button = Button(self, text = "set", command = self.test)
self.button.grid(row=5, sticky=W)
def test(self):
for i, var in enumerate(self.box_list):
if i == 0 or i == 2 or i == 4:
var.set(True)
Window = Tk()
Tool = Error(Window)
Window.mainloop()

how can i control each button individually in tkinter

How can i control each button individually in tkinter?
Here is my code:
from tkinter import *
class Minesweeper:
def __init__(self,action):
L=[]
for i in range(15):
for j in range(15):
self.action = Button(win, text = " h ",command = self.clickMe)
self.action.grid(column = i, row = j)
def clickMe(self):
self.action.configure(foreground = "red")
def main():
global win
win = Tk()
win.title('Minesweeper')
game = Minesweeper(win)
win.mainloop()
main()
Better way is a binding some event to button:
from tkinter import *
class Minesweeper:
def __init__(self, action):
L=[]
for i in range(15):
for j in range(15):
self.action = Button(win, text="h")
# bind to button function 'clickMe' that runs while <Button-1> clicked on it
self.action.bind("<Button-1>", self.clickMe)
self.action.grid(column=i, row=j)
def clickMe(self, event):
"""changes the 'fg' color of the events widget"""
event.widget.configure(foreground="red")
def main():
global win
win = Tk()
win.title('Minesweeper')
game = Minesweeper(win)
win.mainloop()
main()

Resources