Tcl增加过程中的值

Tcl增加过程中的值,tcl,Tcl,我刚开始学习Tcl。我想写一个简单的程序。 该过程启动时,将打开一个浏览窗口以浏览文件。 在那里,您可以选择要打开的文件 然后弹出窗口,询问您是否要选择其他文件。 您选择的每个文件都必须放入一个数组中 我必须输入以下代码: ########## Defining the sub procedures ############ proc open_file {} { set n 0 set title "Select a file" set types { {{

我刚开始学习Tcl。我想写一个简单的程序。 该过程启动时,将打开一个浏览窗口以浏览文件。 在那里,您可以选择要打开的文件

然后弹出窗口,询问您是否要选择其他文件。 您选择的每个文件都必须放入一个数组中

我必须输入以下代码:

########## Defining the sub procedures ############
proc open_file {} {
    set n 0
    set title "Select a file"
    set types {
      {{GDS files} {.gds} }
      {{All Files} * }
    }

    set filename [tk_getOpenFile -filetypes $types -title $title]

    set opendFiles($n) $filename
    set n [expr $n + 1]

    set answer [tk_messageBox -message "Load another GDS file?" -type yesno -icon question]
    if {$answer == yes } {
       open_file
    } else {
       show_files ($opendFiles)
    }
}

proc show_files {} {
    foreach key [array names opendFiles] {
        puts $opendFiles($key)
    }
}

########## Main Program ###########
open_file
我有以下问题。因为我总是回忆起过程“
open\u文件”
”,所以变量
$n
保持设置为
0
。但是我不知道如何在不调用整个子程序的情况下调用窗口的打开

第二个问题是将数组发送到下一个进程。当我发送到进程“
show_files
”时,总是会出现下一个错误:
无法读取“opendFiles”:变量是数组


我似乎找不到两个答案

您需要全局变量。这对我很有用:

########## Defining the sub procedures ############
set n 0
array set openedFiles {}

proc open_file {} {
    set title "Select a file"
    set types {
        {{GDS files} {.gds} }
        {{All Files} * }
    }

    set filename [tk_getOpenFile -filetypes $types -title $title]

    set ::openedFiles($::n) $filename
    incr ::n

    set answer [tk_messageBox -message "Load another GDS file?" -type yesno -icon question]
    if {$answer == yes } {
        open_file
    } else {
        show_files
    }
}

proc show_files {} {
    foreach key [array names ::openedFiles] {
        puts $::openedFiles($key)
    }
}

########## Main Program ###########
open_file
阵列问题


在Tcl中,不能将数组发送到进程。您需要使用
array get
将它们转换为一个列表,然后将此列表发送到proc,然后使用
array set
将其再次转换为一个数组。全局变量有时非常有用,但我相信最好尽可能避免使用它们。在这种情况下,我宁愿在主程序中处理循环和数组,而不是
proc

另外,在其他编程语言中使用数组时,最好在Tcl中使用列表,例如:

proc open_file {} {
    set title "Select a file"
    set types {
        {{GDS files} {.gds} }
        {{All Files} * }
    }

    set filename [tk_getOpenFile -filetypes $types -title $title]
    return $filename
}

proc show_files {files} {
    foreach file $files {
        puts $file
    }
}

set openedFiles [list]
set answer yes

while {$answer == yes}
    lappend openedFiles [open_file]
    set answer [tk_messageBox -message "Load another GDS file?" -type yesno -icon question]
}
show_files $openedFiles
set openedFiles [list]
set filename dummy
while {[string length $filename] > 0} {
    set filename [open_file]
    if {[string length $filename] > 0} {
        lappend openedFiles $filename
    }
}
show_files $openedFiles
如果你想简洁,可以编写show_文件

proc show_files {files} {
    puts [join $files \n]
}
而且,现在它很短,你可以把它放在一行,而不是有另一个程序

最后,您是否考虑过如果用户在
tk_getOpenFile
中按cancel,您想做什么?在这种情况下,filename将设置为空(零长度)字符串。你也可以

  • 忽略这些;或
  • 摆脱
    tk_messageBox
    调用,让用户在输入所需文件数量后按cancel
如果您只想忽略用户按下“取消”时的时间,您可以这样做

set filename [open_file]
if {[string length $filename] > 0} {
    # The user entered a new filesname - add it to the list
    lappend openedFiles $filesname
} else {
    # The user pressed cancel - just ignore the filename
}
如果要使用“取消”来中断循环,则主程序将变成如下所示:

proc open_file {} {
    set title "Select a file"
    set types {
        {{GDS files} {.gds} }
        {{All Files} * }
    }

    set filename [tk_getOpenFile -filetypes $types -title $title]
    return $filename
}

proc show_files {files} {
    foreach file $files {
        puts $file
    }
}

set openedFiles [list]
set answer yes

while {$answer == yes}
    lappend openedFiles [open_file]
    set answer [tk_messageBox -message "Load another GDS file?" -type yesno -icon question]
}
show_files $openedFiles
set openedFiles [list]
set filename dummy
while {[string length $filename] > 0} {
    set filename [open_file]
    if {[string length $filename] > 0} {
        lappend openedFiles $filename
    }
}
show_files $openedFiles

在这种情况下,您可能希望在主程序的开始处设置一个消息框,告诉用户发生了什么。

要使变量的状态在对过程的调用之间保持不变,您需要使该变量在过程之外活动。最简单的方法是使用全局变量:

# Initialize it...
set n 0
proc open_file {} {
    # Import it...
    global n
    ...
    # Use it...
    set openedFiles($n) $filename
    incr n
    ...
}
数组不是值,因此不能直接传递给另一个过程。您可以通过传入名称并使用
upvar 1
将本地别名链接到调用堆栈帧中的变量来处理此问题:

proc show_files {varName} {
    upvar 1 $varName ary
    foreach key [array names ary] {
        puts $ary($key)
    }
}
使用数组的名称调用,因此没有
$

show_files openedFiles
(您还可以通过
array get openedFiles
将数组的序列化传递到中进行序列化,并通过
array set ary$serialization
进行反序列化,但这会带来一些开销。)


您可能应该将该
openedFiles
变量添加到
global
行中,以便它在进程外部的
open\u文件

set/init
n
的所有调用中都是持久的。在过程中,使用
incr::n
增加值
::n
是一个全局变量。要将数组发送到另一个进程,请使用
array get
&
array set
或传递变量名并使用
upvar
无人提及的
incr n
而不是
set n[expr$n+1]
。在Tcl中,最好将第一个参数放在expr:
set n[expr{$n+1}]
我希望您没有复制并粘贴代码!:-)如果你想学习它,你需要写作并理解它-PI把代码输入我的程序。我现在犯了错误,还需要很多东西去学习,我知道;)我现在有另一个“问题”。当我重新启动脚本时,数组不是完全可用的。它会记住以前的值。我试图清除数组:array unset openedFiles{},但似乎不起作用。有什么建议或提示吗?谢谢你找到了解决方案!在数组名称后忘记了“*”。在
show_files
中,可以调用
unset::openedFiles;数组集::openedFiles{}
set::n0。当然是在循环之后。