цикл foreach - имя окна существует и ожидается ошибка логического значения - PullRequest
0 голосов
/ 03 сентября 2018

Утро всем,

Я использую код ниже

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
}

и я получаю ошибку:

имя окна "" уже существует в родительском

и когда я нажимаю на флажок, я получаю ошибку приложения:

ожидаемое логическое значение, но получено "" ожидаемое логическое значение, но получил "" во время выполнения "if {$ rs_cb} { установить ds_out ($ ds_out_i) 1 set ds_out ([expr $ ds_out_i + 1]) 1 } еще { установить ds_out ($ ds_out_i) 0 set ds_out ([expr $ ds_out_i + ... " вызывается изнутри ".rs.u_cb invoke" (линия 1 "верхнего уровня") вызывается изнутри "uplevel # 0 [list $ w invoke]" (процедура "tk :: ButtonUp" строка 24) вызывается изнутри "tk :: ButtonUp .rs.u_cb" (команда привязана к событию)

Может кто-нибудь сказать мне, почему это происходит, пожалуйста?

Ответы [ 2 ]

0 голосов
/ 03 сентября 2018

Один хороший принцип программирования - бритва Оккама, т. Е. «Сущности нельзя умножать без необходимости». Вы можете создать пути виджетов и метки флажков из простого списка имен:

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
}

Тогда мы можем начать создавать виджеты. К сожалению, я не могу понять, как вы хотите, чтобы команда работала, но, вероятно, здесь было бы также легко интегрировать обратный вызов. Поскольку мы создаем столбцы виджетов параллельно, нам не нужно отслеживать grid строк и столбцов:

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

Таким образом, настройка массива виджетов становится намного проще, а код становится более понятным.

Кстати, знаете ли вы, что вы можете установить метку в виджете 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
}
0 голосов
/ 03 сентября 2018

window name "" already exists in parent потому, что список, питающий ds_out_i, длиннее (вдвое длиннее!), Чем списки, питающие все остальные переменные; foreach продолжается до тех пор, пока не пройдет все элементы для каждого элемента, присваивая переменные, списки которых были исчерпаны, пустой строкой. Я предполагаю, что вы хотите либо использовать итерацию списка с двумя переменными для списка, в данный момент кормящего ds_out_i, либо, что более вероятно, итерировать по списку пар. (Обратите внимание, что это немного нарушает ваш код в вашем обратном вызове; я скоро к этому вернусь.)

    …
    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}} \
    …

Другая ваша ошибка в том, что вы в настоящее время используете сложный встроенный скрипт для опции -command (до 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
    }
}
...