Tcl foreach循环-窗口名称存在,预期布尔值错误

Tcl foreach循环-窗口名称存在,预期布尔值错误,tcl,tk,Tcl,Tk,早上好 我正在运行下面的代码 foreach rs_lb {kl15_lb din1_lb din2_lb din3_lb din4_lb \ dinnc_lb ain1_lb ain2_lb ain3_lb ain4_lb a_lb \ b_lb u_lb v_lb w_lb sin_lb cos_lb th1_lb th2_lb hvil_lb} \ rs_lb_txt {KL15 DIN1 DIN2 DIN3 D

早上好

我正在运行下面的代码

foreach rs_lb {kl15_lb din1_lb din2_lb din3_lb din4_lb \
               dinnc_lb ain1_lb ain2_lb ain3_lb ain4_lb a_lb \
               b_lb u_lb v_lb w_lb sin_lb cos_lb th1_lb th2_lb hvil_lb} \

        rs_lb_txt {KL15 DIN1 DIN2 DIN3 DIN4 DINNC AIN1 AIN2 AIN3 AIN4 \
        A B U V W SIN COS TH1 TH2 HVIL} \

        rs_lb_rw {0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9} \

        rs_lb_cm {0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2} \

        rs_cb {kl15_cb din1_cb din2_cb din3_cb din4_cb dinnc_cb ain1_cb \
        ain2_cb ain3_cb ain4_cb a_cb \
        b_cb u_cb v_cb w_cb sin_cb cos_cb th1_cb th2_cb hvil_cb} \

        rs_cb_rw {0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9} \

        rs_cb_cm {1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3} \

        ds_out_i {0 0 2 0 4 0 6 0 8 0 10 0 12 0 14 0 16 0 18 0 20 0 \
                  22 0 24 0 26 0 28 0 30 0 32 0 34 0 36 0 38 0} \
{
    label .rs.$rs_lb -text "$rs_lb_txt"     

    checkbutton .rs.$rs_cb -variable $rs_cb -command {
        if {$rs_cb} {
            set ds_out($ds_out_i) 1
            set ds_out([expr $ds_out_i + 1]) 1
        } else {
            set ds_out($ds_out_i) 0
            set ds_out([expr $ds_out_i + 1]) 0
        }
    }
    grid .rs.$rs_lb -row $rs_lb_rw -column $rs_lb_cm    
    grid .rs.$rs_cb -row $rs_cb_rw -column $rs_cb_cm
}
我得到了一个错误:

父窗口中已存在窗口名“”

当我点击复选框时,我会看到应用程序错误:

需要布尔值,但得到“” 需要布尔值,但得到“” 执行时 “如果{$rs_cb}{ 设置D_out($D_out_i)1 设置ds_out([expr$ds_out_i+1])1 }否则{ 设置D_out($D_out_i)0 设置ds_out([expr$ds_out_i+…” 从内部调用 “.rs.u_cb invoke” (“uplevel”车身第1行) 从内部调用 “uplevel#0[列出$w调用]” (程序“tk::ButtonUp”第24行) 从内部调用 “tk::ButtonUp.rs.u_cb” (命令绑定到事件)

有人能告诉我为什么会发生这种情况吗?

父级中已经存在
窗口名“”
是因为馈送
ds\u out\u i
的列表更长(两倍长!)而为所有其他变量提供数据的列表;
foreach
将一直运行,直到它遍历了每个项目的所有元素,并将列表已耗尽的变量分配给空字符串。 我猜您希望对当前馈送的列表使用双变量列表迭代,或者更可能的是,对成对列表进行迭代。(注意,这会进一步破坏回调中的代码;我稍后将讨论这一点。)

您的另一个错误是因为您当前正在为
-command
选项(to
checkbutton
)使用复杂的嵌入式脚本.不要这样做!这真的很难处理。强烈建议的方法是制作一个小助手过程,然后将
-command
选项设置为调用该过程的简单脚本,该脚本由
list
命令生成

不要在回调中直接使用复杂脚本 我们是认真的。如果我能让它成为一个闪烁的旗帜,我会的。这很难做到正确,而且很容易出错。使用帮助程序非常容易做到正确。下面是你可能会遇到的情况

proc rs_checkbutton_callback {varname index} {
    # "parse" the arguments
    global ds_out;             # We're working with a global variable here
    upvar "#0" $varname rs_cb; # Alias the named global variable as rs_cb
    lassign $index a b;        # This splits the two-part ds_out_i into two variables, a and b

    if {$rs_cb} {
        set ds_out($a) 1
        set ds_out($b) 1
    } else {
        set ds_out($a) 0
        set ds_out($b) 0
    }
}

…

checkbutton .rs.$rs_cb -variable $rs_cb \
    -command [list rs_checkbutton_callback $rs_cb $ds_out_i]
我不确定该回调过程是否正在做您真正想要做的事情。相反,最好是这样做:

proc rs_checkbutton_callback {varname index} {
    # "parse" the arguments
    global ds_out;             # We're working with a global variable here
    upvar "#0" $varname rs_cb; # Alias the named global variable as rs_cb
    lassign $index a b;        # This splits the two-part ds_out_i into two variables, a and b

    if {$rs_cb} {
        set ds_out([list $a $b]) 1
        set ds_out([list $a [expr {$b + 1}]) 1
    } else {
        set ds_out([list $a $b]) 0
        set ds_out([list $a [expr {$b + 1}]) 0
    }
}
父项中已存在
窗口名“”,这是因为馈送
ds_out_i
的列表比馈送所有其他变量的列表长(两倍!);
foreach
一直运行,直到它检查了每个项的所有元素,将其列表已耗尽的变量分配到空字符串。 我猜您希望对当前馈送的列表使用双变量列表迭代,或者更可能的是,对成对列表进行迭代。(注意,这会进一步破坏回调中的代码;我稍后将讨论这一点。)

您的另一个错误是因为您当前正在为
-command
选项(to
checkbutton
)使用复杂的嵌入式脚本.不要这样做!这真的很难处理。强烈建议的方法是制作一个小助手过程,然后将
-command
选项设置为调用该过程的简单脚本,该脚本由
list
命令生成

不要在回调中直接使用复杂脚本 我们是认真的。如果我能让它成为一个闪烁的旗帜,我会的。这很难做到正确,而且很容易出错。使用帮助程序非常容易做到正确。下面是你可能会遇到的情况

proc rs_checkbutton_callback {varname index} {
    # "parse" the arguments
    global ds_out;             # We're working with a global variable here
    upvar "#0" $varname rs_cb; # Alias the named global variable as rs_cb
    lassign $index a b;        # This splits the two-part ds_out_i into two variables, a and b

    if {$rs_cb} {
        set ds_out($a) 1
        set ds_out($b) 1
    } else {
        set ds_out($a) 0
        set ds_out($b) 0
    }
}

…

checkbutton .rs.$rs_cb -variable $rs_cb \
    -command [list rs_checkbutton_callback $rs_cb $ds_out_i]
我不确定该回调过程是否正在做您真正想要做的事情。相反,最好是这样做:

proc rs_checkbutton_callback {varname index} {
    # "parse" the arguments
    global ds_out;             # We're working with a global variable here
    upvar "#0" $varname rs_cb; # Alias the named global variable as rs_cb
    lassign $index a b;        # This splits the two-part ds_out_i into two variables, a and b

    if {$rs_cb} {
        set ds_out([list $a $b]) 1
        set ds_out([list $a [expr {$b + 1}]) 1
    } else {
        set ds_out([list $a $b]) 0
        set ds_out([list $a [expr {$b + 1}]) 0
    }
}

一个好的编程原则是Occam的Razor,即“实体不必成倍增加”。您可以从简单的名称列表中创建小部件路径和复选框标签:

foreach name {foo bar baz} {
    lappend lbnames ${name}_lb
    lappend cbnames ${name}_cb
    lappend texts   [string toupper $name]
}
list $lbnames $cbnames $texts
# => {foo_lb bar_lb baz_lb} {foo_cb bar_cb baz_cb} {FOO BAR BAZ}
在这种情况下,最好将名称列表分成两个相等的部分:

set names1 {
    kl15 din1 din2 din3 din4 dinnc ain1 ain2 ain3 ain4
}

set names2 {
    a b u v w sin cos th1 th2 hvil
}
然后,我们可以开始创建小部件。不幸的是,我不知道您希望该命令如何工作,但在这里集成回调调用可能也很容易。因为我们并行创建小部件的列,所以我们不需要跟踪
网格
行和列:

toplevel .rs

foreach name1 $names1 name2 $names2 {
    grid \
        [label .rs.${name1}_lb -text [string toupper $name1]] \
        [checkbutton .rs.${name1}_cb -variable ${name1}_cb -command [list ...]] \
        [label .rs.${name2}_lb -text [string toupper $name2]] \
        [checkbutton .rs.${name2}_cb -variable ${name2}_cb -command [list ...]]
}
这样,设置widget数组变得更加容易,代码也更加清晰

顺便说一句,你知道你可以在
checkbutton
小部件中设置标签吗

foreach name1 $names1 name2 $names2 {
    grid \
        [checkbutton .rs.${name1}_cb -text [string toupper $name1] -variable ${name1}_cb -command [list ...]] \
        [checkbutton .rs.${name2}_cb -text [string toupper $name2] -variable ${name2}_cb -command [list ...]] \
        -sticky w
}

一个好的编程原则是Occam的Razor,即“实体不必成倍增加”。您可以从简单的名称列表中创建小部件路径和复选框标签:

foreach name {foo bar baz} {
    lappend lbnames ${name}_lb
    lappend cbnames ${name}_cb
    lappend texts   [string toupper $name]
}
list $lbnames $cbnames $texts
# => {foo_lb bar_lb baz_lb} {foo_cb bar_cb baz_cb} {FOO BAR BAZ}
在这种情况下,最好将名称列表分成两个相等的部分:

set names1 {
    kl15 din1 din2 din3 din4 dinnc ain1 ain2 ain3 ain4
}

set names2 {
    a b u v w sin cos th1 th2 hvil
}
然后,我们可以开始创建小部件。不幸的是,我不知道您希望该命令如何工作,但在这里集成回调调用可能也很容易。因为我们并行创建小部件的列,所以我们不需要跟踪
网格
行和列:

toplevel .rs

foreach name1 $names1 name2 $names2 {
    grid \
        [label .rs.${name1}_lb -text [string toupper $name1]] \
        [checkbutton .rs.${name1}_cb -variable ${name1}_cb -command [list ...]] \
        [label .rs.${name2}_lb -text [string toupper $name2]] \
        [checkbutton .rs.${name2}_cb -variable ${name2}_cb -command [list ...]]
}
这样,设置widget数组变得更加容易,代码也更加清晰

顺便说一句,你知道你可以在
checkbutton
小部件中设置标签吗

foreach name1 $names1 name2 $names2 {
    grid \
        [checkbutton .rs.${name1}_cb -text [string toupper $name1] -variable ${name1}_cb -command [list ...]] \
        [checkbutton .rs.${name2}_cb -text [string toupper $name2] -variable ${name2}_cb -command [list ...]] \
        -sticky w
}

你的代码很难阅读(在我使用更传统的缩进进行清理之前)。这会给你带来很多麻烦,因为这很容易让你错过你正在进行的其他不良实践。嗨,多纳尔,谢谢你的帮助。你是对的,我在使用不良实践,我只是不知道什么是好实践。你的代码很难阅读(在我用更传统的缩进法清理之前)。这会给你带来很多麻烦,因为这很容易让你错过你在做其他不好的练习。嗨,多纳尔,谢谢你的帮助。你说得对,我在用不好的练习,我只是