Проблемы с назначением в Tkinter Right Click с лямбда-функцией - PullRequest
0 голосов
/ 17 января 2019

У меня довольно сложный графический интерфейс Tkinter, поэтому стоит избегать повторения работы по назначению элементов управления с помощью лямбда-функций. Вот фрагмент кода:

    def createRightMenu(self, treeName, commands: []):
       for cmd in commands:
          self.rightMouse[treeName].add_command(label= cmd['label'], command= lambda: self.execCommand(cmd['reqId']))
       self.tree[treeName].bind("<Button-3>", lambda event: 
       self.rightMouse_click(event.x_root, event.y_root, treeName))

    def execCommand(self, reqId):
       print("execCommand", reqId)

    self.tree = {}
    treeName = 'a'
    self.createTree(root, treeName)
    self.createRightMenu(treeName , [
        {'label': 'copy', 'reqId': 1},
        {'label': 'retrieve', 'reqId': 2},
        ])

Что работает? В моем меню правой кнопки мыши я получаю записи «copy» и «retrieve», и вызывается execCommand.

Что НЕ работает? execCommand ВСЕГДА получает последний элемент списка, то есть 'retrieve', 2 в качестве параметра, даже если я щелкну правой кнопкой мыши на 'copy'. Если я вручную добавлю два .add_command, то все в порядке. Только использование цикла for не работает. Есть идеи почему?

1 Ответ

0 голосов
/ 18 января 2019

Проблема определения объема: когда срабатывает lambda, он проверяет область действия переменной cmd. Во время каждой итерации for cmd in commands: переменная cmd устанавливается в новое значение. В конце итерации переменная cmd в кадре равна конечной итерации for cmd in commands: (т.е. - {'label': 'retrieve', 'reqId': 2}).

Решение состоит в том, чтобы указать «новую» переменную в сигнатуре lambda, которую он затем будет использовать из-за того, как работает область видимости в python (кавычки на «new», потому что это может быть переменная с тем же именем ). В частности, вы можете изменить свой код на что-то вроде:

for cmd in commands:
    self.rightMouse[treeName].add_command(label= cmd['label'],
                                          command= lambda reqid = cmd['reqId']: self.execCommand(reqid))

Ниже приведен пример, демонстрирующий то, о чем я говорю, на примере:

lambdas = []
words = ["Hello","World"]
print("Creating Lambds")
for word in words:
    my_lambda = lambda: word
    print('>>> Result for "{}" lambda: "{}"'.format(word,my_lambda()))
    lambdas.append(my_lambda)

print("Post Loop Lambda Results")
for w,_lambda in zip(words,lambdas):
    print('>>> Result for "{}" lambda: "{}"'.format(w,_lambda()))

word = "Foobar"
print('Setting word variable to "{}"'.format(word))
print("New Results for lambdas:")
for w,_lambda in zip(words,lambdas):
    print('>>> Result for "{}" lambda: "{}"'.format(w,_lambda()))

print("----------------\nSolution:\n")
lambdas = []
words = ["Hello","World"]
print("Creating Lambds")
for word in words:
    my_lambda = lambda myword = word: myword
    print('>>> Result for "{}" lambda: "{}"'.format(word,my_lambda()))
    lambdas.append(my_lambda)

print("Post Loop Lambda Results")
for w,_lambda in zip(words,lambdas):
    print('>>> Result for "{}" lambda: "{}"'.format(w,_lambda()))

word = "Foobar"
print('Setting word variable to "{}"'.format(word))
print("New Results for lambdas:")
for w,_lambda in zip(words,lambdas):
    print('>>> Result for "{}" lambda: "{}"'.format(w,_lambda()))

Вот некоторые ссылки:

...