Tcl 变量中存储了什么?

发布于 2024-07-10 23:59:06 字数 5990 浏览 5 评论 0原文

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

Pursuant to the advice given in this question I have written a little gui to take the options for a command line C program and pass them on to said C program which is already set up to process them. It displays just like I wanted.

However, I would like to verify that the values stored in the variables are correct. Getting the values to print out is giving me a lot of grief (I can't test in vivo right now due to some hardware issues). What am I missing?

  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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

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

设置内容“ hello"

set blah content

如果您这样做:

puts $blah 

..您将获得 blah 变量的内容,即内容。

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

puts [set $blah]

I'm not sure what you want to do, put if you end put printing the variable name and not the variable content, use the set command as function:

set content "hello"

set blah content

if you do:

puts $blah 

.. you get the content of the blah variable, which is content.

To get the content of the variable content via blah, use the following:

puts [set $blah]
走走停停 2024-07-17 23:59:07

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

$foo is shorthand for [set foo]

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

特别地,

$foo  ;# not what you think

被替换为

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

Just remember this rule and you will always get it right:

$foo is shorthand for [set foo]

Try rewriting your code with [set foo] and it will work.

In particular,

$foo  ;# not what you think

is replaced by

[set [set foo]]   ;# does what you think
双马尾 2024-07-17 23:59:06

变量基础知识

正如其他人所指出的,令人困惑的 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表达式(当不带第三个参数运行时)尝试返回变量 hi 中保存的值。 这失败了。 符号 set $bar 采取相同的步骤,但这次 set 可以对 bar 的值进行操作,即 foo,因此返回 foo 的值,即 hi(链接到“set”API)

初始化

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

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

global colorimetric

但这不起作用,因为您已经在全局范围内操作。 全局“除非在过程主体的上下文中执行,否则没有任何效果”。 (链接到“global”API) 您实际上必须使用 < code>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 上使用 -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)

我也开始结合你的< code>grid 命令插入小部件代码本身。 这里 mygrid 保存了 GUI 中每行需要网格化的内容的列表,然后在每个部分的末尾进行评估以动态传播 GUI。 (链接到“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 。 (链接到“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

Variable Basics

As others have pointed out, the confusing to $ or not to $ notation can be simplified by the following rule.

var is a reference to the variable itself, not its value

$var yields the value held in the variable

It can become a little more confusing when you start to think of everything in Tcl as a string (it's really not, but close enough), so you can store the name of one variable in another and restore its value by reference.

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

Here the notation set $foo is evaluated in step - first $foo yields its value hi and then the set expression (when run with no third argument) attempts to return the value held in the variable hi. This fails. The notation set $bar takes the same steps but this time set can operate on the value of bar, which is foo, and thus returns the value of foo which is hi. (link to "set" API)

Initialization

One problem you have in this script is initialization. In Tcl variables don't exist until they're assigned a value. That's clearly why trying to set $foo above didn't work, because there was no variable hi.

At the top of your script you attempt to declare a variable with,

global colorimetric

which doesn't work, because you are already operating in global scope. Global "has no effect unless executed in the context of a proc body." (link to "global" API) You actually have to use a set command to initialize the variable. This is why none of your attempts to print colorimetric in proc finish worked.

Scope

The other problem you have in this script is scope, particularly with mixing global and procedural/local scope. You're correct that, had you initialized colorimetric correctly then the code,

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

would have worked. Another way to achieve this is with,

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

My Solution

I'd like to present my solution. I admit that I've moved a lot of code around, and I will go into a short explanation of what changes I implemented to make it more concise.

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

1. The first changes I made were to use the -text options on the ttk::checkbutton and ttk::radiobutton. Granted, using an extra label for these allows you to place the text before the button, but doing so is non-standard and requires more code.

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

becomes

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

2. Next I used the similarities between these two checkbuttons to abstract the creation into a foreach. (I do this all the time in my Tcl code for work.) This generates much easier code to read and allows you to add/remove/swap names and tags for the widgets. It results in slightly more but much more versitile code.

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

becomes

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

3. The next change was to merge your getFilename1 and getFilename2 into a single getFilename procedure. We can pass arguments into this function to determine who is calling it. I use the list command to generate the call for this new function. (link to "list" API)

I also started to combine your grid commands into the widget code itself. Here mygrid keeps a list of what needs to be gridded per line in the GUI and then is evaluated at the end of each section to propagate the GUI on the fly. (link to "grid" API)

The previous code gets its final revision and becomes,

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 ]
    ]
}

Then the grid command can be evaluated and the mygrid variable cleared after every use!


4. If you've been paying attention I also added a -variable option to your ttk::checkbutton and at this point started storing the button state in a global variable CONF. This is a big change.

Tk loves to pollute your global namespace and it's good practice to fight back. I usually put all of my configuration state in a global array, and set that right up top of the code so that anyone can come in and change the default state of my code, without digging into it or doing search/replace calls or anything like that. Just good practice, so wherever you had a variable I moved it into CONF and moved CONF up top.

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

5. Next I propagated these changes throughout your code. Almost all of the widget creation was susceptible to these revisions. With respect to the widget creation this sometimes made independent code sections larger. But it also allowed me to remove your entire grid section, merging this code up into the widget code as I've discussed, greatly decreasing the size and clutter of your code at the added expense of complexity.


6. The final changes I made were to your function code. You have a couple of minor bugs in your getFilename1 and getFilename2 code. The first bug was that you want to call tk_getOpenFile because I gather you are only grabbing an existing file name to pass it to gretag. (link to 'tk_getOpenFile' API) If you use tk_getOpenFile the dialog will make sure the file exists.

The second bug in getFilename1 is that the dialog has a Cancel button, and if the user clicks this cancel button the dialog returns an empty string. In this case you have to un-check the ttk::checkbutton and you have to unset the CONF(colorimetric) variable. Or more correctly you have to set CONF(colorimetric) to the button's -offvalue. You can do both of these at once by sending a click event to the current button.

proc getFilename1 {} {
set filename1 [tk_getSaveFile]
}

becomes

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) ""
    }
}

In your finish function I renamed the function to submit (sorry) and rewrote it to make use of the new CONF variable.

The Answer

Of course most of this was unnecessary. I wanted to give you some interesting Tcl/Tk code to think about while at the same time solving your problem. At this point we should have the vocabulary to answer your question, though.

The reason your variables weren't printing out was because of initialization and scope. You should always use a -variable or -textvariable on widgets that you need to reference later. I generally initialize my referenced variables before building their containing widgets. So at the top of your file if you had done,

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

Then you would have been able to do, in the finish function,

puts $::colorimetric
风渺 2024-07-17 23:59:06

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

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

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

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.

土豪 2024-07-17 23:59:06

这是一个小 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)

  • {} {波浪括号):将整个字符串视为不进行替换的文字

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

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

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

Here's a little tcl snippet - run from tclsh or 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
% 

Quoting in Tcl:

  • "" (double quotes): Evaluate substitutions ($variable)

  • {} {Squiggly brackets): Treat the entire string as a literal with no substitution

  • [] (Square brackets): Execute the string as a command with substitution

As an alternative you could pop up the diagnostic in a dialog box:

% set mb [tk_messageBox -message "Port is $foo" -type ok -icon info]
ok
% 
甜妞爱困 2024-07-17 23:59:06

在“# Just here”注释中,尝试添加

put $::gretagNum

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

At your "# Just here" comment, try adding

puts $::gretagNum

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文