0

根据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 中的各种语法,以便在变量传递给之前查看它们的值下一个节目。这些都不起作用,我不明白为什么或如何解决它。我希望另一双眼睛能发现问题所在。

4

6 回答 6

5

变量基础

正如其他人指出的那样,可以通过以下规则简化对 $ 或不是 $表示法的混淆。

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, which is的值进行操作foo,从而返回 is 的foohi(链接到“设置”API)

初始化

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

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

global colorimetric

这不起作用,因为您已经在全球范围内运作。全局“除非在 proc 主体的上下文中执行,否则无效。” (链接到“全局”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::checkbuttonttk::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.接下来我利用这两个checkbutton的相似性将创建抽象成一个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)

我也开始结合你的grid命令组合到小部件代码本身中。这里mygrid保留了 GUI 中每行需要网格化的列表,然后在每个部分的末尾进行评估以动态传播 GUI。(链接到“网格”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.如果您一直在关注我还添加了一个-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. (链接到 '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(sorry) 并重写它以使用新的CONF变量。

答案

当然,大部分都是不必要的。我想给你一些有趣的 Tcl/Tk 代码来思考,同时解决你的问题。不过,在这一点上,我们应该有词汇来回答你的问题。

您的变量没有打印出来的原因是因为初始化和范围。您应该始终在稍后需要引用的小部件上使用-variable或。-textvariable我通常在构建它们包含的小部件之前初始化我的引用变量。所以如果你已经完成了,在你文件的顶部,

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

那么你就可以在finish函数中做到,

puts $::colorimetric
于 2009-05-05T19:00:26.267 回答
2

您尚未将任何变量分配给比色检查按钮。将 -variable colorimetric 添加到检查按钮,然后在完成时您可以使用: puts $::colorimetric

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

我看到 colorimetric 可以具有的值是“”和“-c”,所以我假设您将在 exec 行中使用该值。请注意 [exec something yada $::colorimetric something] 可能无法正常工作。如果参数为空,您可能需要在 exec 行中使用 {*}$::colorimetric 以使参数消失。

于 2009-01-21T07:18:45.737 回答
0

这是一个小 tcl 片段 - 从 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
% 
于 2008-12-24T22:57:34.610 回答
0

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

设置内容“你好”

设置废话内容

如果你这样做:

puts $blah 

.. 你得到 blah 变量的内容,即内容。

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

puts [set $blah]
于 2009-01-20T21:33:02.677 回答
0

只要记住这条规则,你就会永远做对:

$foo is shorthand for [set foo]

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

尤其是,

$$foo  ;# not what you think

被替换为

[set [set foo]]   ;# does what you think
于 2009-01-21T07:53:13.953 回答
0

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

放 $::gretagNum

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

于 2008-12-26T10:26:25.780 回答