Home > other >  How to generalize passing callbacks into tkinter notebook?
How to generalize passing callbacks into tkinter notebook?

Time:02-01

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.

  1. Add a generic *args argument to each of the seven default F* functions that are defined so they will work if any arguments get passed to them (like frame).
  2. Change the commands variable from a list-of-tuples to a list-of-lists to make its subsequences mutable.
  3. Make the Buttons command= keyword argument value a lambda 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()

  •  Tags:  
  • Related