【问题标题】:What's stored in that Tcl variable?Tcl 变量中存储了什么?
【发布时间】:2015-01-16 20:25:30
【问题描述】:

根据this question 中给出的建议,我编写了一个小 gui 来获取命令行 C 程序的选项并将它们传递给已设置为处理它们的所述 C 程序。它显示的正是我想要的。

但是,我想验证存储在变量中的值是否正确。 打印出来的值让我很伤心(由于一些硬件问题,我现在无法在体内测试)。我错过了什么?

  1. 在变量名前加上“$”会得到“$variableName”而不是变量的
  2. 将这些变量添加到数组并调用array get arr 应该会打印索引和数组值;我得到了变量名。
  3. 我尝试了 pathName cget option,但显然 -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
}

编辑: 我已经发布了所有代码,而不仅仅是部分代码,在最后一行的下一行是我尝试过的选项 #1 中的各种语法,以便在变量传递给下一个程序之前查看它们的值.这些都不起作用,我不明白为什么或如何解决它。我希望另一双眼睛能发现问题所在。

【问题讨论】:

  • 顺便说一句,学习使用打包程序。它是迄今为止最有用的 Tk 布局管理器。
  • 您能否在问题中显示在上下文中不起作用的确切语法?
  • @NXC:来自 TkDocs 教程 (tkdocs.com/tutorial/grid.html):“网格具有打包的所有功能,通常会产生更好的布局,......并且更容易学习和使用。因此,我们认为大多数时候网格是大多数开发人员的正确选择。“我更容易。
  • @Arkadiy:我将放置整个脚本,而不是只是部分,包括那些不起作用的东西。
  • 我从未听过有人说 pack 比 grid 更有用。我已经使用 pack 十多年了,并且可以在睡梦中使用它,但即使我也不会认为它更有用。在某些情况下更有用,在其他情况下则更少。为工作使用正确的工具,在这种情况下,OP 试图实现网格的外观绝对是适合工作的工具。

标签: tcl


【解决方案1】:

变量基础

正如其他人所指出的,令人困惑的 to $ 或 not to $ 符号可以通过以下规则进行简化。

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 表达式(当没有第三个参数运行时)尝试返回变量 @987654331 中保存的值@。这失败了。符号set $bar 采用相同的步骤,但这次set 可以对bar 的值进行操作,即foo,从而返回foo 的值,即hi(link to "set" API)

初始化

您在此脚本中遇到的一个问题是初始化。在 Tcl 中,变量在被赋值之前是不存在的。这就是为什么尝试上面的set $foo 不起作用的原因,因为没有变量hi

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

global colorimetric

这不起作用,因为您已经在全球范围内运作。全局“除非在 proc 主体的上下文中执行,否则无效。” (link to "global" API) 实际上,您必须使用 set 命令来初始化变量。这就是为什么您在proc finish 中打印colorimetric 的尝试都没有成功的原因。

范围

您在此脚本中遇到的另一个问题是范围,尤其是在混合全局范围和过程/本地范围时。你是对的,你有没有正确初始化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. 我所做的第一个更改是在ttk::checkbuttonttk::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 命令来生成对这个新函数的调用。 (link to "list" API)

我还开始将您的grid 命令组合到小部件代码本身中。这里mygrid 保留了 GUI 中每行需要网格化的列表,然后在每个部分的末尾进行评估以动态传播 GUI。 (link to "grid" API)

之前的代码得到它的最终版本,变成了,

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. 如果您一直在注意,我还在您的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,因为我认为您只是获取现有文件名以将其传递给gretag(link to 'tk_getOpenFile' API) 如果您使用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

【讨论】:

  • 选择使用数组是一个不错的选择。这是一种非常有效的技术,可以将表单的所有值(对话框、属性表、任何您想调用的名称)保存在一起。但是,从风格的角度来看,我认为我更喜欢 OP。我理解你的理由,但我认为原版读起来更好。可以更轻松地查看小部件的创建位置以及它们的布局方式。
  • 您可能对小部件和布局的 OP 可读性有一个很好的看法。我一开始并不想修改网格部分,但是当我切换到动态小部件命名时,我开始觉得很危险,我想我最好全部自动化。
【解决方案2】:

您尚未为色度检查按钮分配任何变量。 将 -variable colorimetric 添加到复选按钮,然后完成 您可以使用: 放 $::colorimetric

另外,首先设置 ::colorimetric 以选择您的默认值。那 比试图弄乱小部件的状态更容易。

我看到色度可以具有的值是“”和“-c”所以 我假设您将在 exec 行中使用该值。 当心 [exec something yada $::colorimetric something] 会 那时可能行不通。您可能需要 {*}$::colorimetric 在 exec 行以使参数在为空时消失。

【讨论】:

    【解决方案3】:

    这里有一个小 tcl sn-p - 从 tclsh 运行或希望

    [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 中引用:

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

    • {} {Squiggly 括号):将整个字符串视为没有替换的文字

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

    您也可以在对话框中弹出诊断:

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

    【讨论】:

    • 它必须在脚本中工作,而不仅仅是在 Tcl 命令行中。我认为这就是我找不到正确语法的原因。
    • kajaco,您的评论似乎根本没有解决真正的问题。
    【解决方案4】:

    在您的“# Just here”评论中,尝试添加

    放入 $::gretagNum

    :: 表示全局变量,小部件的 -variable 选项 始终是全球性的。

    【讨论】:

    • 或添加全局 gretagNum 应该会有所帮助
    【解决方案5】:

    我不确定你想做什么,如果你结束打印变量名而不是变量内容,请使用 set 命令as function

    设置内容“你好”

    设置废话内容

    如果你这样做:

    puts $blah 
    

    .. 你得到 blah 变量的内容,也就是 content。

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

    puts [set $blah]
    

    【讨论】:

      【解决方案6】:

      只要记住这条规则,你就会永远正确:

      $foo is shorthand for [set foo]
      

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

      特别是,

      $$foo  ;# not what you think
      

      被替换为

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

      【讨论】:

      • 虽然您在技术上是正确的,但这并不能回答实际问题。
      猜你喜欢
      • 1970-01-01
      • 2011-01-11
      • 2017-11-21
      • 2021-04-11
      • 1970-01-01
      • 2015-06-23
      • 1970-01-01
      • 2010-12-07
      相关资源
      最近更新 更多