1

我刚开始学习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 ' show_files'时,我总是会收到下一个错误:can't read "opendFiles": variable is array

我似乎无法找到两个答案..

4

3 回答 3

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

而且,既然它很短,你可以把它排成一行,而不是再有一个 proc。

最后,你有没有考虑过如果用户按下取消键你想做什么tk_getOpenFile?在这种情况下,文件名将设置为空(零长度)字符串。你也可以

  • 忽略这些;或者
  • 摆脱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
}

如果您想使用取消来跳出循环,那么主程序将变为:

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

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

于 2013-05-24T11:12:45.610 回答
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

于 2013-05-24T08:50:59.817 回答
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

(您也可以将数组的序列化传递给 witharray get openedFiles以进行序列化和array set ary $serialization反序列化,但这会带来一些开销。)

您可能应该将该openedFiles变量添加到该global行,以便它在所有open_file.

于 2013-05-24T14:21:17.067 回答