Reputation: 23
I can't seem to get tkinter's built in widget virtual events to work.
It seems that command =
works, however I cannot use it because it doesn't return the tkinter event parameters to the callback function. I need the tkinter event parameter to be returned to the callback so I can get the calling widget's instance.
Am I doing something wrong for '<<ComboboxSelected>>'
, '<<Increment>>'
, '<<Decrement>>'
to do nothing?
I need the callback to be called and the event to be passed so that I can get the widget instance. I also cannot use lambda (as all of the similar examples suggest) to return the index number as I need to access the widget directly to get its current row number as I intend on deleting rows. If I use lambda it makes a mess of things as the index is fixed. So if I click on row 5 and delete row 5, row 6 becomes row 5 and if I want to delete it deletes row 6 instead.
I was also hoping that there was a virtual event for the button to perform the same effect as command. So if you tab to the button hit spacebar or click on it, but I can't find any such virtual event. Is there something like this?
from tkinter import *
root = Tk()
omuVars = []
omuList = ['Bahamas','Canada', 'Cuba','United States']
omu = []
spb = []
cmd = []
def callSelectChanged(event):
caller = event.widget
rowNumber = caller.grid_info()['row']
print(rowNumber)
def callIncrement(event):
caller = event.widget
rowNumber = caller.grid_info()['row']
print(rowNumber)
def callDecrement(event):
caller = event.widget
rowNumber = caller.grid_info()['row']
print(rowNumber)
def callButton(event):
caller = event.widget
rowNumber = caller.grid_info()['row']
print(rowNumber)
for i in range(0,10):
omuVars.append(StringVar())
omu.append(OptionMenu(root,
omuVars[i],
*omuList))
omu[i].bind('<<ComboboxSelected>>', callSelectChanged)
omu[i].config(font='Terminal 18 bold', anchor=W, fg='blue', relief=FLAT, bg='SystemWindow', borderwidth=0, width=15)
omu[i].grid(row=i, column=0)
spb.append(Spinbox(root,
from_=00,
to=23,
wrap=True,
width=2,
font='Terminal 18 bold',
fg='blue',
format="%02.0f",
relief=FLAT))
spb[i].bind('<<Increment>>', callIncrement)
spb[i].bind('<<Decrement>>', callDecrement)
spb[i].grid(row=i, column=1)
cmd.append(Button(root,
text='\u2718',
font='Terminal 16 bold',
fg='blue',
width=2))
cmd[i].bind('<Button-1>',callButton)
cmd[i].bind('<space>', callButton)
cmd[i].grid(row=i, column=2)
root.mainloop()
Upvotes: 0
Views: 331
Reputation: 23
Switching from tkinter to ttk solved the problem:
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
cmbVars = []
cmbList = ['Bahamas','Canada', 'Cuba','United States']
cmb = []
spb = []
cmd = []
# Define the style for combobox widget
style= ttk.Style(root)
#print(style.theme_names()) #('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
style.theme_use('default')
style.configure("TCombobox", font='Terminal 18 bold' , anchor=tk.W, foreground='blue', relief='flat', background='SystemWindow', borderwidth=0, width=15, arrowcolor='blue', arrowsize=18)
# I don't understand why ttk ignores the following style settings unless I specify it here and in this order.
style.map('TCombobox', fieldbackground=[('readonly','SystemWindow')])
style.map('TCombobox', selectbackground=[('readonly', 'SystemWindow')])
style.map('TCombobox', selectforeground=[('readonly', 'blue')])
style.configure("TButton", font='Terminal 16 bold', anchor=tk.W, foreground='blue', relief='flat', background='SystemWindow', borderwidth=0, width=2)
style.configure("TSpinbox", font='Terminal 18 bold', anchor=tk.W, foreground='blue', relief='flat', background='SystemWindow', borderwidth=0, width=2, arrowcolor='blue', arrowsize=18)
print()
def callSelectChanged(event):
caller = event.widget
rowNumber = caller.grid_info()['row']
print(rowNumber)
def callIncrement(event):
caller = event.widget
rowNumber = caller.grid_info()['row']
print(rowNumber)
def callDecrement(event):
caller = event.widget
rowNumber = caller.grid_info()['row']
print(rowNumber)
def callButton(event):
caller = event.widget
rowNumber = caller.grid_info()['row']
print(rowNumber)
for i in range(0,10):
cmbVars.append(tk.StringVar())
cmb.append(ttk.Combobox(root,
textvariable=cmbVars[i],
state="readonly",
style="TCombobox",
font=style.lookup('TCombobox', 'font')))
cmb[i]['values'] = cmbList
cmb[i].bind('<<ComboboxSelected>>', callSelectChanged)
cmb[i].grid(row=i, column=0)
spb.append(ttk.Spinbox(root,
from_=00,
to=23,
wrap=True,
format="%02.0f",
style="TSpinbox",
font=style.lookup('TSpinbox', 'font'),
foreground=style.lookup('TSpinbox', 'foreground'),
background=style.lookup('TSpinbox', 'background'),
width=style.lookup('TSpinbox', 'width')))
spb[i].bind('<<Increment>>', callIncrement)
spb[i].bind('<<Decrement>>', callDecrement)
spb[i].grid(row=i, column=1)
cmd.append(ttk.Button(root,
text='\u2718',
style="TButton"))
cmd[i].bind('<Button-1>',callButton)
cmd[i].bind('<space>', callButton)
cmd[i].grid(row=i, column=2)
root.mainloop()
Upvotes: 0
Reputation: 43495
According to the documentation for event, <<Increment>>
and <<Decrement>>
are not predefined virtual events.
Also, OptionMenu
does not support <<ComboboxSelected>>
.
That requires the ttk.Combobox
widget.
W.r.t. the spinbox, tkinter
merely invokes command
when it has changed. This call does not contain an event
, though.
My workaround to submit different parameters to the same callback this is functools.partial
.
The command
callback for OptionMenu
does not receive an event, but the selected string. See modified code below.
Modified code. I've also used the tk namespace instead of import *
.
from functools import partial
import tkinter as tk
root = tk.Tk()
omuVars = []
omuList = ["Bahamas", "Canada", "Cuba", "United States"]
omu = []
spb = []
cmd = []
def callSelectChanged(selection):
print(f"callSelectChanged: {selection}")
def callSpinChanged(index):
print(f"callSpinChanged: {index}")
def callButton(event):
caller = event.widget
rowNumber = caller.grid_info()["row"]
print(f"callButton: {rowNumber}")
for i in range(0, 10):
omuVars.append(tk.StringVar())
omu.append(tk.OptionMenu(root, omuVars[i], *omuList, command=callSelectChanged))
omu[i].config(
fg="blue",
font="Terminal 18 bold",
relief="flat",
borderwidth=0,
width=15,
)
omu[i].grid(row=i, column=0)
spb.append(
tk.Spinbox(
root,
from_=0,
to=23,
wrap=True,
width=2,
font="Terminal 18 bold",
fg="blue",
format="%02.0f",
relief=tk.FLAT,
command=partial(callSpinChanged, index=i),
)
)
spb[i].grid(row=i, column=1)
cmd.append(
tk.Button(root, text="\u2718", font="Terminal 16 bold", fg="blue", width=2)
)
cmd[i].bind("<Button-1>", callButton)
cmd[i].bind("<space>", callButton)
cmd[i].grid(row=i, column=2)
root.mainloop()
Upvotes: 0
Reputation: 385820
The tkinter OptionMenu
widget does not generate a <<ComboboxSelected>>
event, and the tkinter Spinbox
widget does not generate an <<Increment>>
or <<Decrement>>
event. Binding those events on those widgets will have no effect.
For those events and bindings to work, you need to use the Combobox
and Spinbox
widgets from ttk.
Upvotes: 1