Что хранится в этой переменной Tcl? - PullRequest
0 голосов
/ 25 декабря 2008

В соответствии с советом, данным в в этом вопросе Я написал небольшой графический интерфейс, чтобы взять параметры для программы C на командной строке и передать их указанной C-программе, которая уже настроена для их обработки. Он отображается так, как я хотел.

Однако я хотел бы убедиться, что значения, хранящиеся в переменных, верны. Получение значений для печати доставляет мне много горя (сейчас я не могу протестировать in vivo из-за некоторых аппаратных проблем). Чего мне не хватает?

  1. При добавлении имени переменной к значению «$» я получаю «$ variableName», а не значение переменной.
  2. Добавление этих переменных в массив и вызов array get arr должны вывести индекс и значение массива; Я получаю имена переменных.
  3. Я пытался pathName cget опция , но, очевидно, -value не вариант, и если опустить опцию, я не получу список допустимых параметров.

Вот весь код с различными вещами, которые не работали (из варианта № 1, который является самым простым способом; другие были только мной, пробующим обходные пути). Все они производят ошибки вдоль строк: "не могу прочитать" :: ": нет такой переменной" или "не могу прочитать "колориметрический": такой переменной нет ".

#!/opt/ActiveTcl-8.5/bin/wish8.5

wm title . "Gretag"

ttk::frame .f -borderwidth 5 -relief sunken -padding "5 10"

# next line part of the "puts" tests at the bottom
global colorimetric
ttk::label .f.dataLabel -text "Data Type"
ttk::label .f.colorimetricLabel -text "Colorimetric"
ttk::checkbutton .f.colorimetric -onvalue "-c" -offvalue "" -command getFilename1
ttk::label .f.spectralLabel -text "Spectral"
ttk::checkbutton .f.spectral -onvalue "-s" -offvalue "" -command getFilename2 

ttk::label .f.gretagNumLabel -text "Gretag #"
ttk::label .f.gretagLabel0 -text "1"
ttk::radiobutton .f.gretagRadio0 -variable gretagNum -value "/dev/ttyS0" 
ttk::label .f.gretagLabel1 -text "2"
ttk::radiobutton .f.gretagRadio1 -variable gretagNum -value "/dev/ttyS1"
ttk::label .f.gretagLabel2 -text "3"
ttk::radiobutton .f.gretagRadio2 -variable gretagNum -value "/dev/ttyS2" 
ttk::label .f.gretagLabel3 -text "4"
ttk::radiobutton .f.gretagRadio3 -variable gretagNum -value "/dev/ttyS3" 
ttk::label .f.gretagLabel4 -text "5"
ttk::radiobutton .f.gretagRadio4 -variable gretagNum -value "/dev/ttyS4" 

ttk::label .f.sampleSize -text "Sample Size"
ttk::label .f.samplex -text "X"
ttk::label .f.sampley -text "Y"
ttk::entry .f.x -textvariable x -width 5 
ttk::entry .f.y -textvariable y -width 5 

ttk::label .f.filterLabel -text "Filter Type"
ttk::label .f.filterLabel0 -text "D50"
ttk::radiobutton .f.filterRadio0 -variable filter -value "-d50" 
ttk::label .f.filterLabel1 -text "D65"
ttk::radiobutton .f.filterRadio1 -variable filter -value "-d65" 
ttk::label .f.filterLabel2 -text "Unfiltered"
ttk::radiobutton .f.filterRadio2 -variable filter -value "-U" 
ttk::label .f.filterLabel3 -text "Polarized"
ttk::radiobutton .f.filterRadio3 -variable filter -value "-p" 

ttk::label .f.baudLabel -text "Baud Rate"
ttk::label .f.baudLabel0 -text "4800"
ttk::radiobutton .f.baudRadio0 -variable baud -value "B4800" 
ttk::label .f.baudLabel1 -text "9600"
ttk::radiobutton .f.baudRadio1 -variable baud -value "B9600" 
ttk::label .f.baudLabel2 -text "19200"
ttk::radiobutton .f.baudRadio2 -variable baud -value "B19200" 
ttk::label .f.baudLabel3 -text "38400"
ttk::radiobutton .f.baudRadio3 -variable baud -value "B38400" 
ttk::label .f.baudLabel4 -text "57600"
ttk::radiobutton .f.baudRadio4 -variable baud -value "B57600" 

ttk::button .f.submitBtn -text "Submit" -command finish

grid columnconfigure . 0 -weight 1
grid rowconfigure . 0 -weight 1
grid .f -column 0 -row 0 -columnspan 11 -rowspan 5

grid .f.dataLabel -column 0 -row 0 -sticky we
grid .f.colorimetricLabel -column 1 -row 0 -sticky e
grid .f.colorimetric -column 2 -row 0 -sticky w
grid .f.spectralLabel -column 3 -row 0 -sticky e
grid .f.spectral -column 4 -row 0 -sticky w

grid .f.gretagNumLabel -column 0 -row 1 -sticky we
grid .f.gretagLabel0 -column 1 -row 1 -sticky e
grid .f.gretagRadio0 -column 2 -row 1 -sticky w
grid .f.gretagLabel1 -column 3 -row 1 -sticky e
grid .f.gretagRadio1 -column 4 -row 1 -sticky w
grid .f.gretagLabel2 -column 5 -row 1 -sticky e
grid .f.gretagRadio2 -column 6 -row 1 -sticky w
grid .f.gretagLabel3 -column 7 -row 1 -sticky e
grid .f.gretagRadio3 -column 8 -row 1 -sticky w
grid .f.gretagLabel4 -column 9 -row 1 -sticky e
grid .f.gretagRadio4 -column 10 -row 1 -sticky w

grid .f.sampleSize -column 0 -row 2 -sticky we
grid .f.samplex -column 1 -row 2 -sticky e
grid .f.x -column 2 -row 2 -sticky w
grid .f.sampley -column 3 -row 2 -sticky e
grid .f.y -column 4 -row 2 -sticky w

grid .f.filterLabel -column 0 -row 3 -sticky we
grid .f.filterLabel0 -column 1 -row 3 -sticky e
grid .f.filterRadio0 -column 2 -row 3 -sticky w
grid .f.filterLabel1 -column 3 -row 3 -sticky e
grid .f.filterRadio1 -column 4 -row 3 -sticky w
grid .f.filterLabel2 -column 5 -row 3 -sticky e
grid .f.filterRadio2 -column 6 -row 3 -sticky w
grid .f.filterLabel3 -column 7 -row 3 -sticky e
grid .f.filterRadio3 -column 8 -row 3 -sticky w

grid .f.baudLabel -column 0 -row 4 -sticky we
grid .f.baudLabel0 -column 1 -row 4 -sticky e
grid .f.baudRadio0 -column 2 -row 4 -sticky w
grid .f.baudLabel1 -column 3 -row 4 -sticky e
grid .f.baudRadio1 -column 4 -row 4 -sticky w
grid .f.baudLabel2 -column 5 -row 4 -sticky e
grid .f.baudRadio2 -column 6 -row 4 -sticky w
grid .f.baudLabel3 -column 7 -row 4 -sticky e
grid .f.baudRadio3 -column 8 -row 4 -sticky w
grid .f.baudLabel4 -column 9 -row 4 -sticky e
grid .f.baudRadio4 -column 10 -row 4 -sticky w

grid .f.submitBtn -column 1 -row 5 -columnspan 7 -sticky we

foreach w [winfo children .f] {grid configure $w -padx 5 -pady 5}
focus .f.colorimetric
.f.colorimetric state selected
.f.filterRadio1 state selected
.f.baudRadio1 state selected
bind . <Return> {finish}

proc getFilename1 {} {
set filename1 [tk_getSaveFile]
}

proc getFilename2 {} {
set filename2 [tk_getSaveFile]
}

proc finish {} {
.f.x insert 0 "-x"
.f.y insert 0 "-y"
# Pick one
# puts $colorimetric
# puts colorimetric
# puts "$colorimetric"
# puts $::colorimetric
# puts .f.colorimetric
# puts $.f.colorimetric
# puts $::.f.colorimetric
# puts "$::colorimetric"
exec ./gretag .f.colorimetric filename1 .f.spectral filename2 .f.gretagNum .f.x .f.y .f.filter .f.baud
}

Edit: Я разместил весь код, а не только его часть, и в следующей за последней строкой представлены различные синтаксисы из опции № 1, которые я пытался просмотреть значения переменных, прежде чем они будут переданы следующей программе. , Ничто из этого не работает, и я не понимаю, почему или как это исправить. Я надеюсь, что другой набор глаз поймет, что не так.

Ответы [ 6 ]

5 голосов
/ 05 мая 2009

Переменная Основы

Как уже указывали другие, запутанная запись в $ или не в $ может быть упрощена следующим правилом.

var - это ссылка на саму переменную, а не на ее значение

$ var возвращает значение, содержащееся в переменной

Это может стать немного более запутанным, когда вы начинаете думать обо всем в Tcl как о строке (на самом деле это не так, но достаточно близко), так что вы можете сохранить имя одной переменной в другой и восстановить ее значение по ссылке.

% set foo "hi"
hi
% set bar "foo"
foo
% set $foo
can't read "hi": no such variable
% set $bar
hi

Здесь обозначение set $foo вычисляется на шаге - сначала $foo возвращает его значение hi, а затем выражение set (при запуске без третьего аргумента) пытается вернуть значение, содержащееся в переменной hi. Это не удается. Обозначение set $bar принимает те же шаги, но на этот раз set может работать со значением bar, которое составляет foo, и, таким образом, возвращает значение foo, которое составляет hi. (ссылка на API "set")

инициализация

Одной из проблем, с которыми вы столкнулись в этом скрипте, является инициализация. В Tcl переменные не существуют, пока им не присвоено значение. Именно поэтому попытка set $foo выше не сработала, потому что не было переменной hi.

В верхней части вашего скрипта вы пытаетесь объявить переменную с помощью

global colorimetric

, который не работает, потому что вы уже работаете в глобальном масштабе. Глобальный «не имеет эффекта, если не выполняется в контексте тела процесса». (ссылка на «глобальный» API) Для инициализации переменной вам действительно нужно использовать команду set. Вот почему ни одна из ваших попыток печати colorimetric в proc finish не сработала.

Область

Другая проблема, с которой вы сталкиваетесь в этом скрипте, - это область действия, особенно при смешивании глобальной и процедурной / локальной области. Вы правы, если бы вы правильно инициализировали colorimetric, а затем код

puts $::colorimetric ;# print the value of the global variable colorimetric

сработало бы. Еще один способ добиться этого с помощью

global colorimetric ;# reference a global variable into the local scope
puts $colorimetric  ;# print the value of colorimetric in the local scope

Мое решение

Я бы хотел представить свое решение. Я признаю, что я переместил много кода, и я кратко объясню, какие изменения я реализовал, чтобы сделать его более кратким.

#!/usr/bin/env wish

# --- default configuration --- #
array set CONF {
    colorimetric "-c"
    spectral     ""
    cfilename    "/path/to/defaultCI.txt"
    sfilename    ""
    x            0
    y            0
    gretagnum    "/dev/ttyS0"
    filter       "-d65"
    baud         "B9600"
}

# --- build the interface --- #
wm title . "Gretag"
ttk::frame .f -borderwidth 5 -relief sunken -padding "5 10"
grid columnconfigure . 0 -weight 1
grid rowconfigure    . 0 -weight 1
grid .f


ttk::label .f.dataLabel -text "Data Type: "
foreach {dtname dttag dtfile} {
    colorimetric "-c" cfilename
    spectral     "-s" sfilename
} {
    lappend mygrid [
        ttk::checkbutton .f.$dtname -text [string totitle $dtname] \
        -variable CONF($dtname) -onvalue $dttag -offvalue "" \
        -command [list getFilename $dtname $dttag $dtfile ]
    ]
}
grid .f.dataLabel {*}$mygrid -sticky w ; set mygrid { }


ttk::label .f.gretagNumLabel -text "Gretag #: "
for {set tty 0} {$tty < 5} {incr tty} {
    lappend mygrid [
        ttk::radiobutton .f.gretagRadio$tty -text [expr $tty + 1] \
        -variable CONF(gretagnum) -value "/dev/ttyS$tty" 
    ]
}
grid .f.gretagNumLabel {*}$mygrid -sticky w ; set mygrid { }


ttk::label .f.sampleSize -text "Sample Size: "
ttk::label .f.samplex -text "X"
ttk::label .f.sampley -text "Y"
ttk::entry .f.x -textvariable CONF(x) -width 5 
ttk::entry .f.y -textvariable CONF(x) -width 5 
grid .f.sampleSize .f.samplex .f.x .f.sampley .f.y


ttk::label .f.filterLabel -text "Filter Type: "
foreach {ftname ftval} {
    D50        "-d50"
    D65        "-d65"
    Unfiltered "-U"
    Polarized  "-P"
} {
    lappend mygrid [
        ttk::radiobutton .f.filterRadio$ftname -text $ftname \
        -variable CONF(filter) -value $ftval
    ]
}
grid .f.filterLabel {*}$mygrid -sticky w ; set mygrid { }


ttk::label .f.baudLabel -text "Baud Rate: "
foreach {baud} {
    4800 9600 19200 38400 57600
} {
    lappend mygrid [
        ttk::radiobutton .f.baudRadio$baud -text $baud \
        -variable CONF(baud) -value "B$baud"
    ]
}
grid .f.baudLabel {*}$mygrid -sticky w ; set mygrid { }


ttk::button .f.submitBtn -text "Submit" -command submit
grid .f.submitBtn -columnspan 6 -sticky we

foreach w [winfo children .f] {
    grid configure $w -padx 5 -pady 5
}
focus .f.colorimetric
bind . <Return> submit

# --- callbacks --- #
proc getFilename {type tag file} {
    global CONF

    if {$CONF($type) eq $tag} {
        set CONF($file) [tk_getOpenFile]
        if {$CONF($file) eq ""} { .f.$type invoke }
    } else {
        set CONF($file) ""
    }
}

proc submit { } {
    global CONF

    exec ./gretag $CONF(colorimetric) $CONF(cfilename) \
        $CONF(spectral) $CONF(sfilename) $CONF(gretagnum) \
        $CONF(x) $CONF(y) $CONF(filter) $CONF(baud)
}

Обсуждение изменений

1. Первыми изменениями, которые я сделал, было использование параметров -text на ttk::checkbutton и ttk::radiobutton. Конечно, использование дополнительной метки для них позволяет разместить текст перед кнопкой, но это нестандартно и требует больше кода.

ttk::label .f.colorimetricLabel -text "Colorimetric"
ttk::checkbutton .f.colorimetric -onvalue "-c" -offvalue "" -command getFilename1

становится

ttk::checkbutton .f.colorimetric -text "Colorimetric" -onvalue "-c" -offvalue "" -command getFilename1

2. Затем я использовал сходства между этими двумя кнопками, чтобы абстрагировать творение в foreach. (Я делаю это все время в своем коде Tcl для работы.) Это создает гораздо более простой код для чтения и позволяет добавлять / удалять / менять имена и теги для виджетов. В результате получается немного больше, но гораздо более гибкий код.

ttk::checkbutton .f.colorimetric -text "Colorimetric" -onvalue "-c" -offvalue "" -command getFilename1
ttk::checkbutton .f.colorimetric -text "Spectral" -onvalue "-s" -offvalue "" -command getFilename2

становится

foreach {dtname dttag dtcommand} {
    colorimetric "-c" getFilename1
    spectral     "-s" getFilename2
} {
        ttk::checkbutton .f.$dtname -text [string totitle $dtname] -onvalue $dttag -offvalue "" -command $dtcommand
}

3. Следующим изменением было объединение ваших getFilename1 и getFilename2 в одну getFilename процедуру. Мы можем передать аргументы в эту функцию, чтобы определить, кто ее вызывает. Я использую команду list для генерации вызова для этой новой функции. (ссылка на API "list")

Я также начал комбинировать ваши grid команды в самом коде виджета. Здесь mygrid хранит список того, что необходимо привязать к сетке для каждой строки в GUI, а затем оценивается в конце каждого раздела для распространения GUI на лету. (ссылка на API "grid")

Предыдущий код получает свою окончательную версию и становится,

foreach {dtname dttag dtfile} {
    colorimetric "-c" cfilename
    spectral     "-s" sfilename
} {
    lappend mygrid [
        ttk::checkbutton .f.$dtname -text [string totitle $dtname] -variable CONF($dtname) -onvalue $dttag -offvalue "" -command [list getFilename $dtname $dttag $dtfile ]
    ]
}

Затем можно оценить команду grid и очистить переменную mygrid после каждого использования!


4. Если вы обращали внимание, я также добавил опцию -variable к вашему ttk::checkbutton и в этот момент начал сохранять состояние кнопки в глобальной переменной CONF. Это большое изменение.

Tk любит загрязнять ваше глобальное пространство имен, и это хорошая практика, чтобы дать отпор. Я обычно помещаю все свое состояние конфигурации в глобальный массив и устанавливаю это в верхней части кода, чтобы любой мог войти и изменить состояние моего кода по умолчанию, не углубляясь в него и не делая вызовы поиска / замены или что-то подобное тот. Просто хорошая практика, поэтому, где бы у вас ни была переменная, я переместил ее в CONF и переместил CONF вверх.

array set CONF {
    colorimetric "-c"
    spectral     ""
    cfilename    "/path/to/defaultCI.txt"
    sfilename    ""
    x            0
    y            0
    gretagnum    "/dev/ttyS0"
    filter       "-d65"
    baud         "B9600"
}

5. Далее я распространил эти изменения по всему вашему коду. Почти все создание виджета было подвержено этим изменениям. Что касается создания виджетов, это иногда делало независимые разделы кода больше. Но это также позволило мне удалить весь ваш раздел сетки, объединяя этот код с кодом виджета, как я уже обсуждал, значительно уменьшая размер и беспорядок вашего кода за счет дополнительной сложности.


6. Последние изменения, которые я внес в ваш код функции. У вас есть пара незначительных ошибок в коде getFilename1 и getFilename2. Первая ошибка заключалась в том, что вы хотите позвонить tk_getOpenFile, потому что, насколько я понимаю, вы только захватываете существующее имя файла, чтобы передать его gretag. (ссылка на API tk_getOpenFile) Если вы используете tk_getOpenFile, диалоговое окно убедится, что файл существует.

Вторая ошибка в getFilename1 заключается в том, что в диалоговом окне есть кнопка Cancel, и если пользователь нажимает эту кнопку отмены, диалоговое окно возвращает пустую строку. В этом случае вы должны отменить проверку ttk::checkbutton и сбросить переменную CONF(colorimetric). Или, более правильно, вы должны установить CONF(colorimetric) для кнопки -offvalue. Вы можете сделать оба этих действия одновременно, отправив событие нажатия на текущую кнопку.

proc getFilename1 {} {
set filename1 [tk_getSaveFile]
}

становится

proc getFilename {type tag file} {
    global CONF

    if {$CONF($type) eq $tag} {
        set CONF($file) [tk_getOpenFile]
        if {$CONF($file) eq ""} { .f.$type invoke }
    } else {
        set CONF($file) ""
    }
}

В вашей функции finish я переименовал функцию в submit (извините) и переписал ее, чтобы использовать новую переменную CONF.

Ответ

Конечно, большая часть этого была ненужной. Я хотел дать вам некоторый интересный код Tcl / Tk, чтобы подумать и в то же время решить вашу проблему. На данный момент у нас должен быть словарный запас, чтобы ответить на ваш вопрос.

Причина, по которой ваши переменные не распечатывались, была из-за инициализации и области видимости. Вы всегда должны использовать -variable или -textvariable для виджетов, на которые вам нужно сослаться позже. Я обычно инициализирую свои переменные, на которые ссылаются, перед созданием содержащих их виджетов. Так что в верхней части вашего файла, если вы сделали,

set colorimetric "-c"
ttk::checkbutton .f.colorimetric -variable colorimetric [...]

Тогда вы могли бы сделать, в функции finish,

puts $::colorimetric
2 голосов
/ 21 января 2009

Вы не присвоили никакую переменную колориметрической кнопке. Добавьте -вариабельный колориметрический к кнопке проверки и затем в конце ты можешь использовать: ставит $ :: колориметрический

Кроме того, сначала установите :: colorimetric, чтобы выбрать значение по умолчанию. Тот это проще, чем пытаться связываться с состоянием виджета.

Я вижу, что колориметрическими значениями могут быть "" и "-c", поэтому Я предполагаю, что вы будете использовать это значение в строке exec. Остерегайтесь, что [exec что-то yada $ :: колориметрическое что-то] будет вероятно, не работает тогда. Вам, вероятно, понадобится {*} $ :: colorimetric в строка exec, чтобы аргумент исчез, если он пуст.

0 голосов
/ 21 января 2009

Просто запомните это правило, и вы всегда получите его правильно:

$foo is shorthand for [set foo]

Попробуйте переписать свой код с помощью [set foo], и он будет работать.

В частности,

$$foo  ;# not what you think

заменяется на

[set [set foo]]   ;# does what you think
0 голосов
/ 21 января 2009

Я не уверен, что вы хотите сделать, поставьте, если вы закончите печатать имя переменной, а не ее содержимое, используйте команду set как функцию :

установить содержимое "привет"

установить бла-контент

если вы делаете:

puts $blah 

.. вы получаете содержимое переменной blah, которая является содержимым.

Чтобы получить содержимое переменного содержимого через бла, используйте следующее:

puts [set $blah]
0 голосов
/ 26 декабря 2008

В вашем комментарии "# Просто здесь" попробуйте добавить

ставит $ :: gretagNum

:: обозначает глобальную переменную и параметр -variable для виджетов всегда глобальны.

0 голосов
/ 25 декабря 2008

Вот небольшой фрагмент tcl - запустите из tclsh или wish

[nigel@rose ~]$ wish
% set foo /dev/ttys0
/dev/ttys0
% puts $foo
/dev/ttys0
% puts "$foo"
/dev/ttys0
% puts [$foo]
invalid command name "/dev/ttys0"
% puts ($foo)
(/dev/ttys0)
% puts {$foo}
$foo
% 

Цитирование в Tcl:

  • "" (двойные кавычки): оценка замен (переменная $)

  • {} {Squiggly скобки): обрабатывать всю строку как литерал без замены

  • [] (квадратные скобки): выполнить строку как команду с подстановкой

В качестве альтернативы вы можете вызвать диагностику в диалоговом окне:

% set mb [tk_messageBox -message "Port is $foo" -type ok -icon info]
ok
% 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...