python - lambda en for loop solo toma el último valor
tkinter contextmenu (1)
Por favor, lea sobre ejemplos mínimos . Sin leer su código, creo que se ha topado con un problema bien conocido abordado en preguntas y respuestas anteriores que necesita 2 líneas para ilustrar. Los nombres en los cuerpos de las funciones se evalúan cuando se ejecuta la función.
funcs = [lambda: i for i in range(3)]
for f in funcs: print(f())
imprime ''2'' 3 veces porque las 3 funciones son idénticas y la ''i'' en cada una no se evalúa hasta la llamada, cuando i == 2. Sin embargo,
funcs = [lambda i=i:i for i in range(3)]
for f in funcs: print(f())
realiza tres funciones diferentes, cada una con un valor capturado diferente, por lo que se imprimen 0, 1 y 2. En su estado de cuenta
__cMenu.add_command(label="{}".format(option),
command=lambda: self.filter_records(column, option))
agregar
option=option
antes
:
para capturar los diferentes valores de
option
.
Es posible que desee reescribir como
lambda opt=option: self.filter_records(column, opt)
para diferenciar la variable de bucle del parámetro de función.
Si la
column
cambiara dentro del ciclo, necesitaría el mismo tratamiento.
Conjunto de problemas:
El menú contextual debe mostrar variables de filtro dinámicamente y ejecutar una función con parámetros definidos dentro de la devolución de llamada. Las descripciones genéricas se muestran correctamente, pero la llamada a la función siempre se ejecuta con la última opción establecida.
Lo que he intentado:
#!/usr/bin/env python
import Tkinter as tk
import ttk
from TkTreectrl import MultiListbox
class SomeClass(ttk.Frame):
def __init__(self, *args, **kwargs):
ttk.Frame.__init__(self, *args, **kwargs)
self.pack(expand=True, fill=tk.BOTH)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.View=MultiListbox(self)
__columns=("Date","Time","Type","File","Line","-","Function","Message")
self.View.configure(columns=__columns, expandcolumns=(0,0,0,0,0,0,0,1))
self.View.bind("", self.cell_context)
self.View.grid(row=0, column=0, sticky=tk.NW+tk.SE)
self.__recordset = []
self.__recordset_filtered = False
#Some dummy values
self.__recordset.append(["Date", "Time", "INFO", "File", "12", "-", "Function", "Message Info"])
self.__recordset.append(["Date", "Time", "DEBUG", "File", "12", "-", "Function", "Message Info"])
self.__recordset.append(["Date", "Time", "WARNING", "File", "12", "-", "Function", "Message Info"])
self.__refresh()
def cleanView(self):
self.View.delete(0, tk.END)
def __refresh(self):
self.cleanView()
for row in self.__recordset:
self.View.insert(tk.END, *row)
def filter_records(self, column, value):
print("Filter Log Recordset by {column} and {value}".format(**locals()))
# Filter functionality works as expected
# [...]
def cell_context(self, event):
__cMenu=tk.Menu(self, tearoff=0)
if self.__recordset_filtered:
__cMenu.add_command(label="Show all", command=lambda: filter_records(0, ""))
else:
column=2
options=["INFO", "WARNING", "DEBUG"]
for i in range(len(options)):
option=options[i]
__cMenu.add_command(label="{}".format(option), command=lambda: self.filter_records(column, option))
# Also tried using for option in options here with same result as now
__cMenu.post(event.x_root, event.y_root)
if __name__=="__main__":
root=tk.Tk()
app=SomeClass(root)
root.mainloop()
La salida actual que obtengo es:
Filtrar conjunto de registros de registro por 2 y DEPURAR
No importa cuál de las tres opciones elijo. Supongo que tiene algo que ver con la recolección de basura que solo queda la última opción, pero no puedo entender cómo evitar esto.
Se recomienda cualquier ayuda.