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.
CodePudding user response:
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()
CodePudding user response:
You will need to make several changes to support what you want.
- Add a generic
*args
argument to each of the seven defaultF*
functions that are defined so they will work if any arguments get passed to them (likeframe
). - Change the
commands
variable from a list-of-tuples to a list-of-lists to make its subsequences mutable. - Make the
Button
scommand=
keyword argument value alambda
and 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()