我刚开始学习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
我有以下问题。因为我总是记得proc open_file
'变量$n
一直设置为0
。但我不知道如何在不回忆整个子程序的情况下回忆窗口的开启......
第二个问题是将数组发送到下一个proc。当我发送到proc'show_files
'时,我总是收到下一个错误:can't read "opendFiles": variable is array
。
我似乎找不到两个答案..
答案 0 :(得分:1)
你需要全局变量。这对我有用:
########## 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中,您无法将数组发送到procs。您需要将它们转换为列表array get
将此列表发送到proc,然后再使用array set
将其转换回数组。
答案 1 :(得分:1)
全局变量有时非常有用,但我相信在可能的情况下最好避免使用它们。在这种情况下,我宁愿在主程序中处理循环和数组而不是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
中按下取消,您是否考虑过要执行的操作?在这种情况下,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
在这种情况下,您可能想在主程序的开头竖起一个消息框,告诉用户发生了什么。
答案 2 :(得分:0)
对于在对过程的调用之间保持变量的状态,您需要在过程外部使该变量生效。最简单的方法是使用全局变量:
# 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
的所有调用中保持不变。