我刚刚开始学习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_file
',变量$n
一直设置为0
。但是我不知道如何在不调用整个子程序的情况下调用打开窗口的操作....
第二个问题是发送数组到下一个进程。当我发送到进程' show_files
'时,我总是得到下一个错误:can't read "opendFiles": variable is array
.
需要全局变量。
########## 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
<<p> 数组问题/strong> 在Tcl中,你不能向进程发送数组。您需要使用array get
将它们转换为列表,将此列表发送到进程,然后使用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
如果想简洁,可以将show_files写成
proc show_files {files} {
puts [join $files n]
}
,现在它很短,你可以把它放在一行,而不是另一个进程。
最后,您是否考虑过如果用户在tk_getOpenFile
中按下cancel,您想要做什么?在这种情况下,filename将被设置为一个空(零长度)字符串。你可以选择
- 忽视这些;或
- 取消
tk_messageBox
调用,当用户输入了他们想要的任意多的文件时,按取消键。
如果你想忽略用户按下取消键的次数,你可以使用
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
}
如果你想用cancel来跳出循环,那么主程序就变成这样:
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_file
的所有调用中都是持久的。