Tcl/Tk: нельзя связать с Escape и Return

Я не могу bind использовать клавиши Escape и Return в своем коде Tcl/Tk. Следующий код воспроизводит ошибку. Когда я нажимаю клавишу Esc или Enter, я получаю следующее сообщение об ошибке:

Ошибка: невозможно прочитать "cmd": такой переменной нет

proc OkCancel { cmd } {
  button .${cmd}.ok -text "OK" -command [list myOk $cmd ]
  button .${cmd}.cancel -text "Cancel" -command [list myCancel .$cmd]
  grid .${cmd}.ok .${cmd}.cancel -sticky e

  bind .$cmd <Return> { myOk $cmd ; break }
  bind .$cmd <Escape> { myCancel .${cmd} ; break }
}

proc myOk { cmd } {
  puts "The command name is = $cmd"
}

proc myCancel { arg } {
  destroy $arg
}

proc test { } {
  set cmd "hello"
  toplevel .$cmd
  OkCancel $cmd
}

test

person Anand    schedule 11.07.2011    source источник


Ответы (1)


Это связано с тем, что когда событие срабатывает, связанный с ним скрипт получает evaluated на глобальном уровне (согласно bind руководство). То есть, в случае вашей <Return> привязки скрипт myOk $cmd ; break будет выполняться буквально. Следовательно, если во время запуска вашего скрипта в глобальном пространстве имен не существует переменной с именем «cmd», будет вызвана указанная вами ошибка.

Для устранения проблемы есть несколько способов:

  • Сделайте так, чтобы подстановка $cmd оценивалась во время создания и привязки скрипта. Для этого просто замените {} на "", чтобы разрешить подстановку переменных, то есть, если вы напишете bind .$cmd <Return> " myOk $cmd ; break ", связанный скрипт будет myOK hello ; break
  • Превратите сценарий обратного вызова в вызов процедуры и передайте ему явные параметры, например: bind .$cmd <Return> [list mycallback $cmd], а затем убедитесь, что вы определили mycallback процедуру, принимающую один параметр. Таким образом, вы можете написать общие обработчики событий, которые параметризуются любыми параметрами, которые вам нужны во время привязки.
  • Используйте namespace code или аналогичный инструмент, чтобы ваш скрипт выполнялся в указанном пространстве имен, в котором определена указанная переменная.

Во всех случаях имейте в виду, что, поскольку ваш $cmd может в некоторых случаях расшириться до чего-то странного, рекомендуется защитить весь сценарий от такой ситуации — это то, что [list ...] делает во втором примере (см. это для получения дополнительной информации).

person kostix    schedule 11.07.2011
comment
Спасибо за подробное объяснение и исправление ошибки! - person Anand; 11.07.2011
comment
Команда subst предпочтительнее, чем list. Потому что listможет сделать ненужное экранирование в случае скрипта. - person GrAnd; 12.07.2011
comment
@GrAnd: обычно нет; list обычно лучше, чем subst, для создания сценариев привязки (и других обратных вызовов), потому что его гораздо проще использовать правильно, при условии, что вы создаете одну команду. (Вопрос имеет более сложный случай, когда я бы использовал [list …]\;break или даже заставил процедуры обратного вызова заканчиваться return -code break.) - person Donal Fellows; 13.07.2011
comment
@GrAnd: я думаю, ты ошибаешься. list всегда предпочтительнее subst, если вы создаете одну команду. Нет никакой гарантии, что subst даст правильный список, но list всегда будет. - person Bryan Oakley; 14.07.2011