English 中文(简体)
那个 Tcl 变量储存了什么内容?
原标题:
  • 时间:2008-12-24 22:10:20
  •  标签:

根据这个问题中给出的建议,我编写了一个小的GUI,以获取命令行C程序的选项并将它们传递给已设置好的C程序进行处理。它显示得就像我想要的那样。

然而,我想要验证存储在变量中的值是否正确。目前我无法通过实际测试来检验(由于硬件问题)。但是,让这些值打印出来让我很苦恼,我错过了什么?

  1. Prepending the variable name with $ gives me $variableName rather than the value of the variable.
  2. Adding these variables to an array and calling array get arr is supposed to print the index and the array value; I get variable names.
  3. I tried pathName cget option, but apparently -value isn t an option, and leaving off the option doesn t give me a list of valid options.

Here s all the code with the various things that didn t work (from option #1, which is the most straightforward way; the others were just me trying workarounds). They all produce errors along the lines of: "can t read "::": no such variable" or "can t read "colorimetric": no such variable".

#!/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: I ve posted all the code rather than just part, and in the next to last line are the various syntaxes from option #1 that I ve tried in order to view the values of the variables before they re passed to the next program. None of these are working and I don t understand why or how to fix it. I m hoping another set of eyes will catch what s wrong.

问题回答

Variable Basics

正如其他人指出的那样,“该不该使用 $”这种混乱的符号表示法可以通过以下规则简化。

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)

Initialization

你在这个脚本中遇到的一个问题是初始化。在Tcl中,变量在分配值之前不存在。这显然就是为什么在上面尝试set $foo时失败了,因为没有变量hi

你的脚本顶部尝试声明一个变量,使用了

global colorimetric

这并不起作用,因为你已经在全局范围内操作了。全局“不起作用,除非在进程体的上下文中执行。”(链接到“global”API)你实际上必须使用一个set命令来初始化变量。这就是为什么你在proc finish中尝试打印colorimetric都没有成功的原因。

Scope

你在这个脚本中遇到的另一个问题是作用域,尤其是全局和过程/局部作用域混合。你是对的,如果你正确地初始化了色度测量,那么代码,

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

My Solution

我想提出我的解决方案。我承认我移动了很多代码,我将简要说明我实施的更简明的更改。

#!/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)
}

Discussion of Changes

我所做的第一个更改是在ttk::checkbutton和ttk::radiobutton上使用-text选项。诚然,使用额外的标签可以使您将文本放置在按钮之前,但这样做是不标准的,需要更多的代码。

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. 下一个更改是将您的 getFilename1getFilename2 合并为一个单一的 getFilename 过程。我们可以传递参数到该函数中来确定谁在调用它。我使用 list 命令生成这个新函数的调用。(“list” API 的链接)

我还开始将您的grid< / code>命令合并到小部件代码本身中。这里,mygrid保留了在GUI中每行需要进行网格化的内容列表,然后在每个部分结束时进行评估,以便即时传播GUI。(“网格”API链接)

The previous code gets its final revision and 变成,

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变量!


如果你有注意到,我还为你的ttk::checkbutton添加了一个-variable选项,并且此时开始将按钮状态保存在全局变量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. 我做的最后更改是你的函数代码。你的 getFilename1getFilename2 代码中有几个小错误。第一个错误是,因为我推测你只是获取一个已存在的文件名来传递给,所以你想要调用tk_getOpenFile(链接到 tk_getOpenFile API) 如果你使用tk_getOpenFile,对话框会确保文件存在。

getFilename1中的第二个错误是对话框有一个取消按钮,如果用户单击该取消按钮,则对话框将返回一个空字符串。在这种情况下,您必须取消选择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变量。

The Answer

当然,这些大多数是不必要的。我想给你一些有趣的Tcl/Tk代码,让你思考的同时解决你的问题。到这一点,我们应该有足够的词汇来回答你的问题了。

你的变量没有被打印的原因是因为它们的初始化和作用域。你应该始终在之后需要使用的小部件上使用 -variable -textvariable 。我通常在构建包含变量的小部件之前初始化这些变量。所以,如果你在文件顶部做了以下操作,

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

那么你就能在finish函数中实现了。

puts $::colorimetric

You have not assigned any variable to the colorimetric checkbutton. Add -variable colorimetric to the checkbutton, and then in finish you can use: puts $::colorimetric

Also, set ::colorimetric first to select your default value. That is easier than trying to mess with the state of the widget.

I see that the values colorimetric can have are "" and "-c" so I assume you will use that value in the exec line. Beware that [exec something yada $::colorimetric something] will probably not work then. You ll probably need {*}$::colorimetric in the exec line to make the argument disappear if it is empty.

这是一个小的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中引用:

  • 双引号:"":评估替换($变量)

  • { }(花括号):将整个字符串视为文字字面意义,无法替换。

  • [ ](方括号):将字符串作为带有替代的命令执行

作为一种替代方案,您可以在对话框中弹出诊断信息:

% set mb [tk_messageBox -message "Port is $foo" -type ok -icon info]
ok
% 

在您的“#就在这里”留言中,尝试添加

放置 $::gretagNum

:: signifies a global variable, and the -variable option to widgets are always global.

我不确定你想做什么,但是如果你最终打印的是变量名称而不是变量内容,那么请使用set命令作为函数:

设置内容“你好”

设置博客内容

如果你做:

puts $blah 

你获得了blah变量的内容,即内容。

通过blah获取变量content的内容,请使用以下内容:

puts [set $blah]

只要记住这个规则,你就永远都能做对。

$foo is shorthand for [set foo]

尝试使用 [set foo] 重写您的代码,它会工作。

特别是

$$foo  ;# not what you think

被取代了

[set [set foo]]   ;# does what you think




相关问题