tkinter передает целое число по значению intead ссылки - PullRequest
0 голосов
/ 16 марта 2020

Я предоставил минимальный код ошибки, который мог воспроизвести.

Я использую python 3.

У меня есть функция printValue, которая выводит полученный параметр. У меня также есть список значений.

Используя tkinter, я создал фрейм и генерирую таблицу внутри фрейма на основе значений. Проблема возникает, когда мне нужно добавить кнопку, которая выводит номер строки. Каждая кнопка выводит значение 5, когда они должны вывести соответствующий номер строки.

import tkinter as tk

def printValue(param):
    print(param)

Values=[[1],[2],[3],[4],[5]]
mainGuiWindow = tk.Tk()

frame = tk.Frame(mainGuiWindow)
frame.pack()

def refreshFrame():
    global frame
    global Values
    frame.destroy()
    frame = tk.Frame(mainGuiWindow)
    frame.pack()
    tk.Label(frame,text="Sl. No").grid(row=0,column=0)
    tk.Label(frame,text="Value").grid(row=0,column=1)
    tk.Label(frame,text="Print Entry").grid(row=0,column=2)

    numberOfEntries = 0
    for entry in Values:
        numberOfEntries += 1
        tk.Label(frame,text=numberOfEntries).grid(row=numberOfEntries,column=0)
        tk.Label(frame,text=entry[0]).grid(row=numberOfEntries,column=1)
        tk.Button(frame, text="Print",command=lambda: printValue(numberOfEntries)).grid(row=numberOfEntries,column=2)

refreshFrame()

mainGuiWindow.mainloop() 

Кажется, проблема в том, что параметр функции в кнопке связан с переменной 'numberOfEntries'

Как передать параметр в кнопку таким образом, чтобы функция выводила правильное значение? Я не хочу изменять определение функции. Мне нужно изменить способ, которым он вызывается кнопкой.

1 Ответ

1 голос
/ 16 марта 2020

Это из-за того, как ваш кадр "обновляется". Вы определяете страницу здесь, и когда вы настраиваете все кнопки и все, вы go через все for l oop, прежде чем все будет визуализировано. При этом command будет последним значением в вашем l oop, 5. По сути, он будет делать это:

def f(n):
    return n

def list_of_functions():
    x = []
    for i in range(5):
        x.append(lambda: f(i))
    return x

for func in list_of_functions():
    func()

4
4
4
4
4

Это поведение функций в целом, потому что оно принимает значение numberOfEntries, которое вы в последний раз определили, а не то, которое, по вашему мнению, было определено. Чтобы показать это с помощью эквивалентной «нормальной» функции:

# Note that there's no argument here, because
# you haven't given your lambda an argument
def f():
    # does a global lookup for value g
    return g

def create_funcs():
    funcs = []
    for g in range(5):
        funcs.append(f)
    return funcs

for func in create_funcs():
    func()

4
4
4
4
4

Обходной путь здесь заключается в том, чтобы физически связать это значение с функцией во время определения . Это можно сделать с помощью functools.partial. Чтобы сначала показать простой пример:

from functools import partial 

def f(n):
    return n

def list_of_functions():
    x = []
    for i in range(5):
        x.append(partial(f, i))
    return x

for func in list_of_functions():
    func()

0
1
2
3
4

Или, вы можете добавить входной аргумент к своей лямбде и привязать к нему значение по умолчанию:


def f(n):
    return n

def list_of_functions():
    x = []
    for i in range(5):
        # note that now we have bound an input argument to our lambda
        x.append(lambda i=i: f(i))
    return x

for func in list_of_functions():
    func()
0
1
2
3
4

Теперь каждый вызов изменяется соответствующим образом. Чтобы показать, как реализовать это в вашем коде:

from functools import partial


def refreshFrame():
    global frame
    global Values
    frame.destroy()
    frame = tk.Frame(mainGuiWindow)
    frame.pack()
    tk.Label(frame,text="Sl. No").grid(row=0,column=0)
    tk.Label(frame,text="Value").grid(row=0,column=1)
    tk.Label(frame,text="Print Entry").grid(row=0,column=2)

    # I've changed this to be a bit more idiomatic
    for n, (entry, *_) in enumerate(Values, start=1):
        tk.Label(frame,text=n).grid(row=n, column=0)
        tk.Label(frame,text=entry).grid(row=n, column=1)

        # this *looks* like a lambda, and still returns a function
        # but binds the value of `n` to the function, rather than relying
        # on references to a variable that you've changed
        tk.Button(frame, text="Print",command=partial(printValue, n)).grid(row=n, column=2)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...