'How to generalize passing callbacks into tkinter notebook?
I'm trying to generalize the addition of buttons and tabs in a tkinter.ttk.Notebook. Here's what I have so far:
from tkinter.ttk import Notebook, Frame
from tkinter import Button, Tk
f1 = lambda: print("f1")
f2 = lambda: print("f2")
f3 = lambda: print("f3")
f4 = lambda: print("f4")
f5 = lambda: print("f5")
f6 = lambda: print("f6")
f7 = lambda: print("f7")
frames = ["F1", "F2", "F3"]
labels = [('f1', 'f2', 'f3'), ('f4', 'f5'), ('f6', 'f7')]
commands = [(f1, f2, f3), (f4, f5), (f6, f7)]
tk = Tk()
notebook = Notebook(tk)
for i, f in enumerate(frames):
frame = Frame(notebook)
notebook.add(frame, text=f)
for j, label in enumerate(labels[i]):
button = Button(frame, text=label, command=commands[i][j])
button.pack()
notebook.pack()
tk.mainloop()
Now, suppose I want function f7 not to print the string 'f7', but rather to perform a frame.quit so I can exit out of the notebook.
How can I call frame.quit and keep the generic nature of the code above?
I don't know how I could do that before the for loop.
Solution 1:[1]
There are two ways I thought of doing this. The first is to create an empty object (using the method described here) before the lambdas and then by the time the function is called frame is properly defined.
frame = type('', (), {})()
f1 = lambda: print("f1")
f2 = lambda: print("f2")
f3 = lambda: print("f3")
f4 = lambda: print("f4")
f5 = lambda: print("f5")
f6 = lambda: print("f6")
f7 = lambda: print(frame.quit)
If several of your functions are going to make reference to the frame you could pass a reference to it instead, but then every lambda will need to take it as a parameter.
from tkinter.ttk import Notebook, Frame
from tkinter import Button, Tk
f1 = lambda f: print("f1")
f2 = lambda f: print("f2")
f3 = lambda f: print("f3")
f4 = lambda f: print("f4")
f5 = lambda f: print("f5")
f6 = lambda f: print("f6")
f7 = lambda f: f.quit()
frames = ["F1", "F2", "F3"]
labels = [('f1', 'f2', 'f3'), ('f4', 'f5'), ('f6', 'f7')]
commands = [(f1, f2, f3), (f4, f5), (f6, f7)]
tk = Tk()
notebook = Notebook(tk)
for i, f in enumerate(frames):
frame = Frame(notebook)
notebook.add(frame, text=f)
for j, label in enumerate(labels[i]):
button = Button(frame, text=label, command= lambda: commands[i][j](frame))
button.pack()
notebook.pack()
tk.mainloop()
Solution 2:[2]
You will need to make several changes to support what you want.
- Add a generic
*argsargument to each of the seven defaultF*functions that are defined so they will work if any arguments get passed to them (likeframe). - Change the
commandsvariable from a list-of-tuples to a list-of-lists to make its subsequences mutable. - Make the
Buttonscommand=keyword argument value alambdaand with several arguments, some of the with default values. This make it look-up values at runtime.
from tkinter.ttk import Notebook, Frame
from tkinter import Button, Tk
f1 = lambda *args: print("f1")
f2 = lambda *args: print("f2")
f3 = lambda *args: print("f3")
f4 = lambda *args: print("f4")
f5 = lambda *args: print("f5")
f6 = lambda *args: print("f6")
f7 = lambda *args: print("f7")
frames = ["F1", "F2", "F3"]
labels = [('f1', 'f2', 'f3'), ('f4', 'f5'), ('f6', 'f7')]
commands = [[f1, f2, f3], [f4, f5], [f6, f7]] # CHANGED TO LIST OF SUBLISTS
tk = Tk()
notebook = Notebook(tk)
for i, f in enumerate(frames):
frame = Frame(notebook)
notebook.add(frame, text=f)
for j, label in enumerate(labels[i]):
button = Button(frame, text=label, command=lambda i=i, j=j: commands[i][j](frame))
button.pack()
notebook.pack()
commands[2][1] = lambda frame: frame.quit() # Change the F7 slot.
tk.mainloop()
Solution 3:[3]
I did something slightly different, which worked:
from tkinter.ttk import Notebook, Frame
from tkinter import Button, Tk
tk = Tk()
notebook = Notebook(tk)
f1 = lambda: print("f1")
f2 = lambda: print("f2")
f3 = lambda: print("f3")
f4 = lambda: print("f4")
f5 = lambda: print("f5")
f6 = lambda: print("f6")
f7 = lambda: notebook.quit()
frames = ["F1", "F2", "F3"]
labels = [('f1', 'f2', 'f3'), ('f4', 'f5'), ('f6', 'f7')]
commands = [(f1, f2, f3), (f4, f5), (f6, f7)]
for i, f in enumerate(frames):
frame = Frame(notebook)
notebook.add(frame, text=f)
for j, label in enumerate(labels[i]):
button = Button(frame, text=label, command=commands[i][j])
button.pack()
notebook.pack()
tk.mainloop()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Henry |
| Solution 2 | martineau |
| Solution 3 | user32882 |
