2016-06-30 88 views
3

我有以下TCL無限while循環。我想通過按鍵退出或自行超時。我無法讓按鍵退出。代碼中缺少的是什麼?如何通過鍵盤事件擺脫TCL中的無限循環?

fconfigure stdin -blocking 0 
set time 20000 
set start_time [clock clicks -milliseconds] 
while { 0 < 1 } { 
    fileevent stdin readable { 
     exit 
    } 
    set end_time [clock clicks -milliseconds] 
    set dt [expr $end_time-$start_time] 
    if {$dt >= $time} { 
     exit 
    } 
} 
+0

你永遠不進入事件循環,這是需要引起fileevents。 –

+0

如何進入事件循環。 – chuk01

+1

這是GUI還是CLI應用程序? – slebetman

回答

1

具體的解決方案,按順序顯示多個屏幕,使用空格鍵切換到下一個屏幕,並在5秒鐘後自動切換屏幕。

首先,一些虛擬的定義來模擬屏幕。我建議使用這樣的命令程序來設置每個單獨的屏幕,但在實際的腳本中,它們當然會比這些更復雜。現在

grid [label .a] -sticky news 
proc screen1 {} {.a configure -text 1} 
proc screen2 {} {.a configure -text 2} 
proc screen3 {} {.a configure -text 3} 
proc screen4 {} {.a configure -text 4} 
proc screen5 {} {.a configure -text 5} 

,畫面切換機構(一個基本的,簡單的一個)。如果有,則nextScreen命令取消正在運行的定時器。然後,一個全局計數器n遞增(如果第一次不存在,則incr將創建它並將其值設爲1)。然後我們檢查是否有一個叫screen$n的命令:如果沒有,命令返回,結束序列。如果存在,它被調用,然後定時器設置爲在五秒鐘後調用nextScreen

proc nextScreen {} { 
    global n afterId 
    catch {after cancel $afterId} 
    incr n 
    if {[llength [info commands screen$n]] < 1} { 
     return 
    } 
    screen$n 
    set afterId [after 5000 nextScreen] 
} 

設置空格鍵激活nextScreen

bind . <space> nextScreen 

呼叫nextScreen啓動序列(或不這樣做,在這種情況下,空間需要按下啓動它)。

nextScreen 

注:不需要無限循環(事件循環這裏的無限循環)。我們也不需要採取任何動作來啓動事件循環:它會在窗口外殼中自動啓動。

年紀大了,原來的問題,一般的解決方案:

GUI應用

你可以通過調用

vwait forever 

啓動事件循環(forever只是一個可愛的變量名),但在GUI應用程序中,您不需要這樣做,因爲它將自動啓動。

如果您想通過單鍵輸入來控制腳本,通過使用fileevent stdin很難獲得該功能。 OTOH,它真的很容易bind鍵擊事件:

bind . <Key> { 
    exit 
} 

如果你想與剛剛鍵「K」打破,綁定到<Key-k>代替。

具有循環結束20秒後自動是太容易了:

after 20000 exit 

將其組合在一起:

bind . <Key> { 
    exit 
} 
after 20000 exit 
while true { 
    puts -nonewline . 
    update 
} 

puts是否有表明環路是活的,在update有以確保事件仍然處理。

CLI應用

對於CLI應用程序,我真的相信你應該使用的處置如果可能的。你的平臺在這裏很重要。

您需要設置stdin不使用阻塞或緩衝:

chan configure stdin -blocking 0 -buffering none 

然後你需要說服操作系統不使用緩衝以及(又名「原始輸入」)。在Unix/Linux上,我被告知有人使用stty。自從Slackware在60多張軟盤上出現以來,沒有使用Linux,我真的不知道。

對於Windows解決方案,您可以使用twapi來設置原始輸入模式。請注意,您需要刷新stdin,否則它將從一開始就可讀。

(這是測試,並在Win7和Win10可與twapi 3.0.32。我不能肯定這是最好的解決方案。需要注意的是twapi有它自己的命令,以取代fileevent。)

package require twapi 

twapi::modify_console_input_mode stdin -lineinput false -echoinput false 
twapi::flush_console_input stdin 

在下面的通用腳本之前運行此代碼。

否則,則代碼(按Enter鍵停止循環立即,然後依次Enter任何其他關鍵的需求,除非你已經成功地建立了原始輸入):

after 0 { 
    chan configure stdin -blocking 0 -buffering none 
    fileevent stdin readable { 
     exit 
    } 
    after 20000 exit 
    while true { 
     puts -nonewline . 
     update 
    } 
} 
vwait forever 

文檔:afterbindcatchchanexitfileeventglobalgridifincrinfolabel (widget)llengthpackageprocputsreturnsettwapi (package)updatevwaitwhile

+2

除非是CLI應用程序,否則應該使用fileevent stdin完成:D – slebetman

+0

更新時CLI腳本嘗試不足。 –

+0

謝謝@peter。你的CLI代碼幾乎是我想要的。問題是它不會自動開始自動運行。它需要按一個鍵才能開始。我正在嘗試編寫的這段代碼是GUI程序的一部分。它在屏幕上顯示一些東西。該人按下「空格鍵」進入下一個畫面。例如,如果沒有響應,則會在5秒後進入屏幕。它一直在屏幕上繼續顯示,直到程序結束。 – chuk01

1

這只是獲得預期結果的另一種方式,但只能使用Control鍵的組合。

使用Expect的trap命令,我們可以處理諸如SIGINT(Ctrl + C)的信號。

set loop_var 1 
trap {set ::loop_var 0;puts "Resetting the loop_var to zero"} SIGINT 
while {$loop_var} { 
     puts "loop_var : $loop_var" 
     after 1000 
} 
puts "Out of infinite!!!" 

一旦按下了Ctrl+C,循環就會中斷。

參考:Exploring Expect

+0

謝謝@peter。你的CLI代碼幾乎是我想要的。問題是它不會自動開始自動運行。它需要按一個鍵才能開始。 – chuk01

+0

@ chuk01:對不起。我不明白。你的意思是沒有自動啓動? – Dinesh