tcl/tk の覚え書き

最終更新  2024.04.01


◆◆ 画像の縮小 Linux Japan 2000.3 p.147 ◆◆


コピー先画像 copy コピー元画像 -shrink -subsample 縮小率


◆◆ 値渡しと参照渡し Linux Japan 2002.4 p.49 ◆◆


引数は通常は値渡し
アドレス渡ししたい場合は upvar コマンドを使う

proc sub { value ptr1 ptr2 } {
    upvar $ptr1 ptr1_ $ptr2 ptr2_

#    ptr1 と ptr1_ は同じメモリ空間を共有する
#    ptr2 と ptr2_ は同じメモリ空間を共有する

    set ptr1_ [ expr $value + 1 ]
    set ptr2_ [ expr $value + 2 ]

    set ret_val "string"

    return $ret_val
}

呼び出し方
sub $value ptr1 ptr2


◆◆ 変数のスコープ ◆◆


メインスクリプトの変数は大域変数
関数の中で使うときは global 宣言が必要

global a b c

のように宣言する


◆◆ if 文 ◆◆


if { $x == 6 } then {

} elseif { $x != 10 } then {

} elseif { $x >= 8 } then {

} else {

}

if { $x == "yabu" } then {

}


◆◆ 配列 ◆◆


set abc($i) 10


◆◆ ファイルの存在チェック ◆◆


if { [ file exists wave.bat ] == 1 } then {

}

これを知らないとき、次のような方法をとっていた。

ytest というコマンドを作る。内容は

---------- ytest の内容 --------
#!/bin/sh
#
#  tcl/tk では test コマンドを使うと戻り値が 0 以外のときに
#  エラーとなって関数の実行がそこで打ち切られてしまうので、
#  苦肉の策として、独自のテストコマンド ytest を作成する。
#

if [ -e $1 ]
then
    echo yes
else
    echo no
fi
--------------------------------

そして、ytest の出力を次のように読み取る。

set ret [ exec ./ytest bergeron.pid ]
if { $ret == "yes" } then {
    set fp [ open bergeron.pid r ]
    gets $fp pid
    close $fp
    exec kill $pid
    exec rm -f bergeron.pid
}


◆◆ for 文 ◆◆


for { set i 0 } { $i < 10 } { incr i } {


}


◆◆ while 文 ◆◆


while { $i < 10 } {


}

while { 1 } {

    if {     } then break;
    if {     } then continue;

}


◆◆ スクリプトの絶対パスを取得する ◆◆


set script_name [ info script ]
set dir_name    [ file dirname $script_name ]


◆◆ exec で実行した外部コマンドの標準出力を得る ◆◆


set ret [ exec command ]


◆◆ 文字処理 ◆◆


set    str "abc"
append str " def" " ghi"      連結

string first "abc" $line      "abc" は $line の何番目の文字列か(0 から)
                              "abc" が含まれていないとき -1

set num [ llength $line ]     $line に含まれている文字列の数(空白区切り)
set moji [ lindex $line 2 ]   $line の 3 番目の要素を取り出す(index は 0 から)
set moji [ lindex $line end ] $line の最後の要素


◆◆ format ◆◆


set line ""

for { set i 0 } { $i < $n } { incr i } {
   append line [ format " %6.4f " $var($i) ]
}

puts $line


◆◆ 複合フォント ◆◆


set fontsize 10

font create roman -family  helvetica     -size $fontsize -weight normal
font create kanji -charset jisx0208.1983 -size $fontsize
font create mix   -compound {roman kanji}


◆◆ pack ◆◆


pack .aaa .bbb .ccc -side left

-side left  左から右へ並べる
-side top   上から下へ並べる

-expand yes|no   ウィンドウサイズの変更に追従するかどうか

-fill both|x|y|none  各部品を一番幅の広い(横に並べるときは高さの高い)もの
                     に幅を(高さを)合わせるかどうか

-padx 20    部品間の x 方向の余白 20 dot
-pady 10    部品間の y 方向の余白 10 dot

-anchor n|w   -fill none の場合、右寄せか、左寄せか、上寄せか、下寄せか
              w 左寄せ   r 右寄せ   n 上寄せ   s 下寄せ
              (東西南北の頭文字)


◆◆ label のオプション ◆◆


-width 10   ラベルの幅
-anchor w   ラベルの幅が文字列より広いとき左寄せ
-anchor e   右寄せ


◆◆ text(テキストウィジェット)の扱い ◆◆


宣言

text  .left.text -height 5 -width 40 -font font14

書き込み

.left.text insert 1.0 "  1.466  1.452   1.46\n"
.left.text insert 2.0 "        5      8   \n"

最後の改行を怠ると、insert 2.0 と指定しても、その時点で 2 行目が
存在しないため、1 行目の後に append される

読み出し(空白のみの行 or 空行が来たら終了する)

for { set i 1 } { $i <= $memo_height } { incr i } {
    set line [ .left.text get $i.0 $i.999 ]
    if { [ regexp "^\ *$" $line ] == 1 } then break;     空行で終わり
    puts "|$line|"
}

一行前へ詰める

global message_next message_line    次に書き込む位置, 行数

if { $message_next > $message_line } then {
    for { set i 1 } { $i <= $message_line-1 } { incr i } {
        set j [ expr $i + 1 ]
        .left.text2 delete $i.0 $i.999
        set line [ .left.text2 get $j.0 $j.999 ]
        .left.text2 insert $i.0 $line
        set message_next $message_line
    }
}
.left.text2 insert $message_next.0 $add_line
set message_next [ expr $message_next + 1 ]


◆◆ exec で実行したプロセスの ID を知る ◆◆


方法が分からないため、launch というコマンドを作り、

    exec launch command arg1 arg2

のようにコマンドを実行する。すると command.pid という
ファイルを作り、その中にプロセス ID を保存する。

実装は以下の通り

/*
   プログラムのランチャー
   起動したプログラムの pid を残す

   2004.9.15

< 使い方 >

    % launch prog-name arg1 arg2 .......

< 結果 >

    起動したプログラムのプロセス id が prog-name.pid に入る
    (パスの部分は切り落とされる)
*/

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int    i,ret;
    pid_t  pid;
    FILE   *fp;
    char   *argv2[1000],path[1000],command[1000],fname[1000];

    if ( argc == 1 ){
        fprintf(stderr,"usage : %s command\n",argv[0]);
        exit(1);
    }

    if ( argc > 1000 ){
        fprintf(stderr,"too many argument in %s\n",argv[0]);
        exit(1);
    }

    analyze_path(argv[1],path,command);
    sprintf(fname,"%s.pid",command);

    pid = getpid();

    if ( ( fp = fopen(fname,"w") ) == NULL ){
        fprintf(stdout,"cannot open %s",fname);
        exit(1);
    }
    fprintf(fp,"%d\n",pid);
    fclose(fp);

    for(i=1; i<argc; i++){
        argv2[i-1] = argv[i];
    }
    argv2[argc-1] = NULL;

    ret = execvp(argv2[0],argv2);

    if ( ret != 0 ){
        fprintf(stderr,"error in execvp. argv2[0] = %s\n",argv2[0]);
        exit(1);
    }

    exit(0);   /* ここへ来ることはないと思う */
}

/*----------- パスとファイル名に分解する -----------

     入力   full_name = "/home/Pioneer/graph/nsgraph"

     出力   path      = "/home/Pioneer/graph"
            command   = "nsgraph"
*/

int analyze_path(char full_name[],char path[], char command[])
{
    int i,len,len2;

    len = strlen(full_name);

    for( i = len-1; i >= 0; i--){
        if ( full_name[i] == '/' ){
            strncpy(path,full_name,i);
            path[i] = '\0';
            len2 = len - i - 1;
            strncpy(command,&full_name[i+1],len2);
            command[len2] = '\0';
            return(0);
        }
    }

    strcpy(path,"");
    strcpy(command,full_name);

    return(0);
}


◆◆ 関数 ◆◆


int([ expr 99 / 10.0 ])
sqrt()
abs()


◆◆ 整数演算の結果 ◆◆


set a [ expr 100 / 80 ]       「整数 ÷ 整数 = 整数」  a <--- 1