Lindows はマイクロフトwith Microsoft, と対立しているだけでなく、無料ソフトウエア基金 Free Software Foundation. を完全に運営した。Lindows はその製品のソースコードの頒布にある程度無関心 somewhat casual であったようだ。Bruce Perens は Michael Robertson (Lindows CEO) に対し公開書簡 open letter を書いて、会社を無料ソフトウエア運動の忠実な協力者であるとした。Mono Linux はLindowsの報告と解析を公刊した。上下2部になる。
David Ranch は、IP Masquerade HOWTOのリリースを発表した。
変更点は下記である:
Debian Weekly Newsは最近、Debian GNU/FreeBSD ライブを見たい人のための新ベースtarballをNathan Hawkinsが発表したannouncedと、報じた recently reported 。このポートの状況はここで入手出来る available here.。
Gentooはまた、PPCプラットホームに搭載することが出来る。 iMacLinuxが見直した reviewed 。
Slashdot に、 SuSE 8.0 が出荷され、KDE 3.0, kernel 2.4.18, その他各種の更新/強化を含んでいるとの記事 story があるt。
Command Prompt, Inc. は、 PostgreSQLに関するサポート、プログラミング及びサービスを提供する。サービス契約と同時に、タイム及び物質サポートが得られ、カスタマのデータベースソリューションのため単一地点会計責任が可能になる。
Etnus は、自分のサポートするプラットホームの中ではLinux がリーダーであり続けそこでの機能性を増すと考えている。TotalView の次のリリースは、will add support for GCC 3.X 及びLinux用インテルコンパイラに関するサポートにを加えるであろう。
この技術は、ほとんどの攻撃が食い物にされるソフトウエアの行動を測定可能な方法で変えてしまうとの原理に基づく。CylantSecureは、ソフトウエアの行動監視にセンサを、行動の異常性を発見する統計的解析エンジンとともに用いる。連続的な行動監視を通じて、CylantSecure は管理者に攻撃の早期警報を発するので。適切な対策を取ることが出来る。子のような対策には、プログラムの活動停止、攻撃IPからの交信回避、システム状態解析などが含まれる。詳しい情報は、Cylant websiteへ。
Apache 2.0が、公式に安定した is now, officially, stable.
Galeon 1.2.1 がリリースされた been released
AbiWord 1.0が出た is out
Mailmanの新バージョン (バージョン 2.0.10)が入手出来る is now available.
[ **この版は 助けて呉れた友人Natalie Wakelin に捧げる。彼女は私の輝きの星で親友だ。彼女には「技術文書」の意味が理解出来ないだろうが、それでも捧げる。有り難う** ]
暫くお休みを頂いたが、やっと自由の世界と大学生活に飛び込んだ。生活を合わせるのは始めに想像したより難しい。
それは兎も角……
Linuxの中で如何に奇妙に見えようとも、Linuxで解けない可能性はないのにに最近気付いた。
大学の学業とは別に、私はHants LUGで個人的及びメールで問題解決に協力している。大変面白い。沢山のことを学んだ。
前書きはこの位にして、先へ進もう。
簡単な紹介:Squid 目次へ戻る
9月版Septemberを読まれた方はApache の使用について記事を書いたのを覚えておられるだろう。色々な反響を頂いた。 squidを解説するのが良いと思った。
ご存じない方のために言うと、(海のイカではない)Squid はLinuxインターネット代用プログラムだ。squid と呼ぶのは(良い名前を人が先に使ったからだろう)
Squidは、(プロキシサーバーと言う)マシンを使ってインターネット要求をチャンネルする。
さらにSquidは、特定のウエブペイジを読めるようにも読めないようにもフィルタfilter する能力がある。これは、ACL (Access Control Lists)を通じておこなう。詳しくは後で。
squid のインストールは一本道だ。squid は主要ディストリビューション(RedHat, SuSE, Caldera, Debian, など)すべてと一緒に供給されるので、ディストリビューションCDから簡単にアクセスすることが出来る。
RPM フォーマットをサポートするディストリビューションを持っている人は、次のコマンドを使ってインストールされているか否かをチェックすることが出来る:
rpm -qa | grep -i squid
搭載されていれば、 "squid2-2.2.STABLE5-190" (又は類似)が返る筈だ。返事がなければ、ディストリビューションCDからsquidをインストールする。
ディストリビューションCDにsquidがないか、又はRPMフォーマットをサポートしないLinuxを使っているときは、.tgz(tar.gz)のソースを、http://www.squid-cache.org/からダウンロードする。
Squidをソースからインストールするには、"/tmp" にtar ballして次のコマンドを出す:
1. "root" のユーザーでなければ、rootとしてsu 又はlog in する 2. cd /tmp 3. tar xzvf ./name_of_squid.tar.gz -- [or possibly .tgz] 4. Now run: ./configure 5. これらの後、エラーがなければ、単に: make && make install とタイプしてコンパイルしファイルをインストールする。
一般的に、標準RPMインストレーションからは、これらのディレクトリを使う:
/usr/bin /etc /etc/squid (多分 -- RH 5.0の下にあるのが普通) /var/squid/log/ [/usr/local/etc] <-- 多分 "/etc" に symlinkeされている
ソースからコンパイルするのであれば、沢山のファイルが出来上がる:
/etc /etc/squid (多分 -- RH 5.0の下にあるのが普通) /usr/local/bin /var [/usr/local/etc] <-- 多分 "/etc" に symlinkeされている
言うまでもないが、特に要求されない限り、これでファアイルが終わる。
これでSquidがインストールされた。次のコンフィギュレーションに入る。
プロキシサーバーになるため、なすべきことは未だ沢山ある。
作業を一つのファイル /etc/squid.confに集中する。Squidの設定すべてを維持するファイルだ。このファイルを編集するので、元のファイルのコピイを取っておくのが良い。それには次のコマンドを出す:
cp /etc/squid.conf /etc/squid.conf.orig
次いで、好みのエディタを起動し、squid.confのエディットを始める。
Squidを走らせるのにこのファイルを「ボックス外」で使うのは不可能だ。直ぐ働くプロキシサーバーを入手する前にコンフィギュアしなければならないことが沢山ある。一目で見て、このファイルの長さは1マイルもあるが、開発者のお陰で、ファイルの大部分は利用出来るオプションについてのコメントである。
最初に、Squidに対し作動しポートを聴取するマシンのIPアドレスを告げる。squid.confの中に、次のようなコマンド行がある筈だ。、
#http_port 3128
始めの#記号を削除して、この行のコメントを外す。規定値で、ポート番号3128が選ばれる。しかし、Squidに違うポートを聴取させたいときは、それを変える。そこで、私のプロキシマシンでは、次のように規定した:
http_port 10.1.100.1:8080
これは、squidにポート8080を使って上記IPアドレスを聴取させる。注意しなければならないのは、同じポートを他の作動中アプリケーションが使用しないことだ。多くの人がこの間違いを犯す。
ここで、このコンフィギュレーション・ファイル全体を通って進むので、ここで変更しなければならない主なオプションはcache_memである。このオプションは、キャッシュなどにどれほどのメモリを使うかをsquidに告げる。
私はこの行のコメントを外して−−8MBの規定値を残した。
このオプションの遙か下に、幾つかオプションがあってsquidにキャッシュ"watermark"の高/低を告げる。これは単にディスク空間のパーセンテージで、90/95%以内に入ったときsquidはキャッシュした項目の幾つかを削除し始めることを意味する。
#cache_swap_low 90
#cache_swap_high 95
私は単純にこれらのコメントを外したが、値は次のように変えた。理由は、私が持っているのは60GBハードディスクで1パーセントが数百MBになるからである:
cache_swap_low 97
cache_swap_high 98
これで、squidに、聴取IPポートと、使うメモリと、キャッシュ項目削除を始めるメモリ空間のパーセンテージを告げた。ここでファイルをセーブする。
次に最後から2番目で私の変えたオプションは、キャッシュディレクトリの位置と大きさを決めるので極めて重要だ。次のようなTAGがある:
cache_dir /var/squid/cache 100 16 256
これは、パス"/var/squid/cache"各トップレベルディレクトリが100MBを拘束するとの意味だ。16個のトップレベルディレクトリあり、その下に256個のサブディレクトリがある。
フィルタに移る前に、このファイルでひねらなければならないのは、アクセスログの使用だ。cache_dirのためコンフィギュアしたオプションの真下に、ロギングを許すオプションがある。一般的に次のロギングオプションを持っている:
上のログの各々は、プロキシサーバー運営の際それぞれ長所/短所がある。一般的に、保留するログは access logs とcache log だ。理由は簡単で私がstore とswap logには関心がないからだ。
ユーザーがおこなうリクエスト(ユーザーの行きたいウエブペイジ)をログするのはaccess logファイルだ。私が未だ高校にいた頃、このファイルは、どのユーザーが禁制サイトに入ろうとしているかを知るのに大変貴重だった。システム管理者にこのファイルを働かせることをお薦めする。大変役に立つ。
そこで、(TAGSのコメントを外して)次をおこなった:
cache_access_log /var/squid/logs/access.log
cache_log /var/squid/logs/cache.log
ログ名はそのままにすることを薦める。
明らかに、squid.confファイル内の基本オプションのほとんどを終わっただけなのは明らかだ。特定の状況のため膨大なオプションがある。それぞれのオプションには、良いコメントがあるので、特定のオプションの役割は分かる筈だ。難しくない。
この章でもまだ "/etc/squid.conf" を使うが、アクセス制御のためのコンフィギュレーションオプションに少し詳しく踏み込む。
アクセス制御は、システム管理者に、どのクライアントがプロキシサーバーに実際接続出来るかを、IPアドレスやポートなどで、制御する方法を与える。大きいネットワーク構成のコンピュータにとって有用である。
一般的にACL (アクセス制御リスト) はそれらに対し次の特性を持っている:
アクセス制御はすべてこれらに対し次のフォーマットを有する:
acl acl_config_name type_of_acl_config values_passed_to_acl
そこでコンフィギュレーションファイル内に、次の行を見付ける:
http_access deny all
そして、その上に次の行を加える
acl weekendmechnetwork 10.1.100.1/255.255.255.0
http_access allow weekendmechnetwork
"weekendmechnetwork"の名は、自分の選んだ名に変えることが出来る。ここでおこなっているのは、"weekendmechnetwork"の名を持つすべてのaclについて、255.255.255.0のnetmaskとともに、特定のIPアドレス10.1.100.1を用いると言っている。従って、ネットワーク上でクライアントに割り当てられた名は"weekendmechnetwork"となる。
"http_access allow weekendmechnetwork" の行は、ルールが有効で、squid自体で解析することが出来ることを示す。
次ぎにしなければならないのは、選ばれたクライアントにインターネットへのアクセスを許すことも見ることだ。これは全部のマシンをインターネットに接続するとは限らない場合に有用だ。
今まで付け加えたことの下で、イカのようなことを規定することが出来る:
acl valid_clients src 192.168.1.2 192.168.1.3 192.168.1.4
http_access allow valid_clients
http_access deny !valid_clients
ここで言っているのは、src IPアドレスがリストに載っているACL名 "valid_clients" については、"valid_clients" へのhttpアクセスを許し(http_access 許可valid_clients)、リストに載っていないその他のIPには許さない(http_access 拒否 valid_clients)ことである。
すべてのマシンにインターネットアクセスを許すのであれば、次のように規定する:
http_access allow all
しかし、ACLを更に拡張して、squidに一定のACLが特定の時期だけ働くようにすることが出来る。例えば:
1. acl clientA src 192.168.1.1 2. acl clientB src 192.168.1.2 3. acl clientC src 192.168.1.3 4. acl morning time 08:00-12:00 5. acl lunch time 12:30-13:30 6. acl evening time 15:00-21:00 7. http_access allow clientA morning 8. http_access allow clientB evening 9. http_access allow clientA lunch 10. http_access allow clientC evening 11. http_access deny all
Lines 1-3 は、マシンを特定するACL 名を設定する
Lines 4-6 は、規定時間制限(24時間制)でACL を設定する
Line 7 は、cliant A (でcliant Aのみ) に「朝」の時間帯にアクセスを許す意味
Line 8 は、cliant B (でcliant Bのみ) に「夕方」の時間帯にアクセスを許す意味
Line 9 は、cliant A (でcliant Aのみ) に「昼」の時間帯にアクセスを許す意味
Line 10 は、cliant C (でcliant Cのみ) に「夕方」の時間帯にアクセスを許す意味
Line 11 は、他のcliantのいずれかが接続使用とすると−拒否する意味
だがさらに、URL表現の中の特定のregexe に一致させることをSquidに命令して、事実上リクエストをビン(詳しくは−"&>/dev/null")の中に投げ入れることにより、ACLを用いる。
これをおこなうには、特定のパターンを持つ新ACLを規定することが出来る。例えば:
1. acl naughty_sites url_regex -i sex 2. http_access deny naughty_sites 3. http_access allow valid_clients 4. http-access deny all
Line 1 は、条項url_regex が−つまりURL内に含まれる語をチェックし結果−そのACLがその型であると言うACL 名" naughty_sites" には "sex" の語が結び付いていると言う。 -i は、ケースセンシティビティを無視する意味。
Line 2 は、 ACL "naughty_sites"からの何かを含むウエブサイトへのアクセスは全部のクライアントにきょひするとの意味
Line 3 は、"valid_clients"からのアクセスを許す意味
Line 4 は、その他すべてのリクエストを拒否する意味
「一つより多いregexはどう規定するか」との疑問があると思う。答は簡単だ。別のファイルに入れることが出来る。例えば、次の言葉をフィルタし、それがURLに現れたらアクセスを拒否したいとする:
sex porn teen
それをファイル(一度に一単語)
/etc/squid/bad_words.regex
に入れて、"/etc/squid.conf" の中で次のように規定する:
acl bad-sites url_regex -i "/etc/squid/bad_words.regex" http_access deny bad_sites http_access allow valid_clients http-access deny all
これで楽に生きられる。つまり必要なとき何時でも単語をリストに加えることが出来る。
SquidGuardと言う名のプログラムを使って、regexとドメイン名の両方をフィルタする遙かに易しい方法もある。後で詳しく述べる。
実際にSquidを走らせる最も重要な部分に差し掛かった。残念ながら、初めてsquidを初期化するのであれば、渡さなければならない幾つかのオプションがある。一般的にsquidに渡すべきオプションは、次の表に纏めることが出来る:
| フラッグ | 説明 |
| -z |
squidに必要な swap ディレクトリを作る。squidを初めて働かせるとき、又はキャッシュディレクトリが削除されたときだけ使う |
| -f |
このオプションは規定値"/etc/squid/conf" 以外に使用する代替ファイルを規定することが出来るが、使うことは稀である |
| -k reconfigure |
このオプションはsquidに、squidデーモン自体を停止することなく、コンフィギュレーションファイルをロードし直すことを命じる |
| -k rotate |
このオプションはsquidにログを回転して新しいものをスタートさせることを命じる。cron ジョブに役立つ |
| -k shutdown | Squidの実行を停止する |
| -k check | squid デーモンがあって働いていることを点検し確認する |
| -k parse | "-k reconfigure"と同じ |
利用することの出来るオプション全体は次の通りである:
用法: squid [-dhsvzCDFNRVYX] [-f config-file] [-[au] port] [-k signal]
-a port HTTP ポート番号を規定 (規定値: 3128).
-d level stderr にもまたデバッグを書き込む
-f file 与えられた config-file を、次ぎの代わりに利用する
/etc/squid/squid.conf
-h ヘルプメッセージをプリントする
-k reconfigure|rotate|shutdown|interrupt|kill|debug|check|parse
コンフィギュレーションファイルを解析して、働いている
copy (-k parseを除く) に信号を送って終了
-s syslogに対するロギングを有効にする
-u port ICP ポート番号を規定(規定値:3130)、0で無効にする
-v バージョンをプリント
-z swap デイレクトリを作る
-C 致命的な信号をキャッシュしない
-D 初期 DNS テストが出来ないようにする
-F フォアグラウンド急速記憶再構築
-N 非デーモン・モード
-R ポートに REUSEADDR を設定しない
-V 仮想ホストhttpd-アクセラレータ
-X 完全デバッグを強制
-Y 急速再ロード中に UDP_HIT or UDP_MISS_NOFETCH にだけ戻る
squid を初めて走らせるのであれば、ユーザー"root"にログインして、次をタイプする:
squid -z
これでキャッシュが作られる
続いて次のコマンドを送る:
squid
これで、プロキシサーバーが走る。上出来だ!!
簡単な紹介:SquidGuard 目次へ戻る
SquidGuardは、それを使ってsquidが自分に送られたリクエストをが外部SquidGuardデーモンに送る「リダイレクトプログラム」である。SquidGuardの仕事は、Squid自体より大きいフィルタリングが出来るようにすることである。
しかし、フィルタリングをおこなうのに、単純なフィルタにはSquidGuardは必要でないことを指摘しておかなければならない。
SquidGuard は(ほぼ十分に) http://www.squidguard.org/downloadから入手することが出来る。このサイトにはSquidGuardのコンフィギュアの仕方に関する情報が豊富にある。
Squidのように、SquidGuard はrpm と.tgz フォーマットの双方で入手することが出来る。
君のディストリビューションがRPMをサポートしていれば、次の方法でインストールすることが出来る:
su - -c "rpm -i ./SquidGuard-1.2.1.noarch.rpm"
RPM フォーマットをサポートしていないときは、次の方法でソースをダウンロードしてそれをコンパイルすることが出来る:
tar xzvf ./SquidGuard-1.2.1.tgz ./configure make && make install
ファイルは、"/usr/local/squidguard/"の中にインストールする。
メインの"/etc/squidguard.conf"を実際にいじる前に先ず、お馴染みの"/etc/squidguard.conf"を少々変更しなければならない。ファイルの中に次のTAGを置く:
#redirect_program none
このコメントを外し、語「none」をメインSquidGuardファイルへのパスで置き換える。メインファイルの場所が分からないときは、次のコマンドを送る:
whereis squidGuard
次いで、適切なパス名とファイル名を入れる。これで、次のようになる筈だ:
redirect_program /usr/local/bin/squidGuard
ファイルをセーブして、次をタイプする。
squid -k reconfigure
これでコンフィギュレーションファイルが再ロードされる。
ここで、面白いことが始まる。送られたリクエストをフィルタするのに、リダイレクトプログラムを使うことをsquidに告げたので、それに適合するルールを定義しなければならない。
SquidGuardのメインコンフィギュレーションファイルは "/etc/squidguard" である。ボックスの外で、これは次のように見える:
-------------------
(テキスト版)
logdir /var/squidGuard/logs
dbhome /var/squidGuard/db
src grownups {
ip 10.0.0.0/24 # range 10.0.0.0 - 10.0.0.255
# AND
user foo bar # ident foo or bar
}
src kids {
ip 10.0.0.0/22 # range 10.0.0.0 - 10.0.3.255
}
dest blacklist {
domainlist blacklist/domains
urllist blacklist/urls
}
acl {
grownups {
pass all
}
kids {
pass !blacklist all
}
default {
pass none
redirect http://localhost/cgi/blocked?clientaddr=%a&clientname=%n&clientuser=%i&clientgroup=%s&targetgroup=%t&url=%u
}
}
-------------------
しなければならないのは、コンフィギュレーションファイルを区切って、各部分の作用を説明することだ。
logdir /var/squidGuard/logs dbhome /var/squidGuard/db
1行目は、ログファイルの現れるディレクトリを設定し、存在しなければそれを作る。
2行目は、禁止サイト、表現などを記憶するディレクトリを設定する。
src grownups {
ip 10.0.0.0/24 # range 10.0.0.0 - 10.0.0.255
# AND
user foo bar # ident foo or bar
}
上のコードブロックは、多数のことを設定する。src "grownups" は、IPアドレス範囲を規定し、どのユーザーがこのブロックのメンバーであるかを示して定義する。便宜のため、一般的な用語"foo" と"bar"をここでは例として使用する。
user TAG は、squidプロキシバーにリクエストを送るサーバー上でidentサーバーが走っているときだけ使用することが出来、そうでないときは無効てあることに、注意しなければならない。
src kids {
ip 10.0.0.0/22 # range 10.0.0.0 - 10.0.3.255
}
別のブロックを設定する。今回は、"kids"と呼ばれ、これはIPアドレスの範囲で決定されるが、ユーザーはいないブロックである。
grownups とkids を"/etc/squid.conf"で見出されるようなものと同様のACL名と考えることが出来る。
dest blacklist {
domainlist blacklist/domains
urllist blacklist/urls
expression blacklist/expressions
}
ステートメントのこの部分は、特定のフィルタ処理に対するdestリストを定義するので、重要である。処理により、SquidGuardがフィルタ処理を適用する三つの主な方法がある:
1.domainlist−ドメインをそれだけ、1回に1行リストする、例えば:
nasa.gov.org squid-cache.org cam.ac.uk
2.urllist −実際に特定ウエブペイジを規定する("www"は省略)、例えば:
linuxgazette.com/current cam.ac.uk/~users
3.expression−URLの中で禁止すべき単語をregexする、次の通り:
sex busty porn
コードの最後のブロック:
acl {
grownups {
pass all
}
kids {
pass !blacklist all
}
default {
pass none
redirect http://localhost/cgi/blocked?clientaddr=%a&clientname=%n&clientuser=%i&clientgroup=%s&targetgroup=%t&url=%u
}
}
は、acl ブロック及び "grownups" のため、すべてのリクエストを通過させる−つまり、これらのURLの destブラックリストに含まれる/表現など許す、ことを命じる。
次いで、"kids" 部分のため、dest blacklistsを除くすべてのリクエストを通すことを命じる。この点で、URLがdest blacklistsに一致すると、defaultに送られる。
default部分は、リクエストが" grownups" 又は "kids" から来たものでないと見出したときは、ウエブサイトへのアクセスを許さず、エラーペイジなど別のウエブペイジに送り直すことを命じる。
これら送り直しステートメントと一緒に渡される変数は、リクエストの型などを規定し、次いでこれをcgiスクリプトが処理して、習慣的なエラーメッセージなどを作る。
フィルタを起こすためには、この時、redirectクローズに伴い又は伴わないで、次のコードがなければならないことに注意:
default {
pass none
}
もっと進んだコンフィギュレーション・オプションをこのファイルの中で使用することが出来る。その例はt http://www.squidguard.org/configurationにある。
これで Squid 及び SquidGuard双方に関する講義を終わる。情報はすべて、この文書に埋め込んだURL及び、私のウエブサイトにある http://www.squidproxyapps.org.uk/
重要ファイル:簡便 BASH バックアップスクリプト 目次へ戻る
「別のバックアップスクリプトはないか」との質問がある。そこに飛ぼう。
スクリプトは全く簡単だ。バックアップしたいファイル(及びディレクトリ)すべてを一覧するコンフィギュレーション・ファイル(平文)を使い、次いでそれらをgzippしたtarballの規定の場所に入れる。
BASHシェルスクリプトに慣れている人は、繰り返しだと思うだろうが、行間のコメントがシェルを習おうとする人の役に立つと思う。
-------------------
#!/bin/bash
#################################################
#Keyfiles - tar/gzip configuration files #
#Version: Version 1.0 (first draft) #
#Ackn: based on an idea from Dave Turnbull #
#Authour: Thomas Adam #
#Date: Monday 28 May 2001, 16:05pm BST #
#Website: www.squidproxyapps.org.uk #
#Contact: thomas@squidproxyapps.org.uk #
#################################################
#此処のコメントはDave Turnbullの利益のため:
#変数宣言
configfile="/etc/keyfiles.conf"
tmpdir="/tmp"
wrkdir="/var/log/keyfiles"
tarfile=keyfiles-$(date +%d%m%Y).tgz
method=$1 # "keyfiles"に渡されるオプション
submethod=$2 # "$1" と一緒に供給されるオプション
quiet=0 # 冗長の上でターン(規定値)
cmd=`basename $0` #ファイル名からパスを剥ぎ取る
optfiles="Usage: $cmd [--default (--quiet)] [--listconffiles] [--restore (--quiet)] [--editconf] [--delold] [--version]"
version="keyfiles: Created by Thomas Adam, Version 1.0 (Tuesday 5 June 2001, 23:42)"
#エラー点検を扱う・・・
if [ ! -e $configfile ]; then
for beepthatbell in 1 2 3 4 5; do
echo -en "\x07"
mail -s "[Keyfiles]: $configfile not found" $USER
done
fi
#作業用ディレクトリがあることを確認
[ ! -d $wrkdir ] && mkdir $wrkdir
#コマンド行経由で送られたオプションを解析
if [ -z $method ]; then
echo $optfiles
exit 0
fi
#コマンド行シンタックスを点検
check_syntax ()
{
case $method in
--default)
cmd_default
;;
--listconffiles)
cmd_listconffiles
;;
--restore)
shift 1
cmd_restore
;;
--editconf)
exec $EDITOR $configfile
exit 0
;;
--delold)
cd $wrkdir && rm -f ./*.old > /dev/null
exit 0
;;
--version)
echo $version
exit 0
;;
--*|-*|*)
echo $optfiles
exit 0
;;
esac
}
#ここから作業開始・・・
# "--default"設定を使用するファンクションを宣言
cmd_default ()
{
# $configfileに含まれるファイルすべてをar/gz
if [ $submethod ]; then
tar -cZPpsf $tmp/$tarfile $(cat $configfile) &>/dev/null 2>&1
else
tar -vcZPpsf $tmp/$tarfile $(cat $configfile)
fi
# デイレクトリの内容が空白であるとき・・・
if test $(ls -1 $wrkdir | grep -c -) = "0"; then
mv $tmp/$tarfile $wrkdir
exit 0
fi
for i in $(ls $wrkdir/*.tgz); do
mv $i $i.old
done
mv $tmp/$tarfile $wrkdir
}
# $configfileに含まれるファイルのリストを作る
cmd_listconffiles ()
{
sort -o $configfile $configfile
cat $configfile
exit 0
}
#ファイルを記憶し直す・・・
cmd_restore ()
{
cp $wrkdir/keyfiles*.tgz /
cd /
# quiet フラッグを点検
if [ $submethod ]; then
tar vzxfmp keyfiles*.tgz &>/dev/null 2>&1
rm -f /keyfiles*.tgz
exit 0
else
tar vzxfmp keyfiles*.tgz
rm -f /keyfiles*.tgz
exit 0
fi
}
# main ファンクションを呼び出す
check_syntax
-------------------
しなければならない変更は、次の変数に対してだと言えば充分だろう:
configfile="/etc/keyfiles.conf" tmpdir="/tmp" wrkdir="/var/log/keyfiles"
しかし、私のスクリプトは十分に賢く、$wrkdirの有無を点検して、なければ作る。
適切なパーミッションをおこなったが確認しなければならない、次のようにする:
chmod 700 /usr/local/bin/keyfiles
最も重要なファイルは、スクリプトのコンフィギュレーションファイルで、私の場合は次のようになっている:
-------------------
/etc/keyfiles.conf /etc/rc.config /home/*/.AnotherLevel/* /home/*/.fvwm2rc.m4 /home/solent/ada/* /root/.AnotherLevel/* /root/.fvwm2rc.m4 /usr/bin/header.sed /usr/bin/loop4mail /var/spool/mail/*
-------------------
このファイルが tar プログラムに渡された後、上のファイルのようにワイルドカードの使用が有効になる
スクリプトが走る度に、最新のバックアップファイルが作られる、つまり新しいファイルが生まれる前に"keyfiles-DATE.tgz"の名が、"keyfiles-DATE.tgz.old"に変わることを、言っておかなければならない。
これは、何時でもバックアップファイルを記憶する必要がある時のためで、私のスクリプトは、".tgz" 拡張子を点検することにより、どのファイルを使うかが分かる。
この特徴のため、ディレクトリから古いファイル全部を削除する "--delold"オプションを含めた。
プログラムを使うには、次のようにタイプする:
keyfiles --default
これはバックアップ処理を開始する。冗長を押さえたいなら、フラッグkeyfiles --default --quietを付け加える。
このプログラムが採用するその他のオプション、ほとんど自明である。
このバックアップスクリプトは、決して完全でなく、もっと良いものがある。ご意見を歓迎する。
プログラム検討:Nedit 目次へ戻る
遙かな昔、この雑誌の創始者John Fisk がこの欄に執筆しており、別の著者Larry Ayers が一連のプログラム検討をしていた。彼はNeditと言う名の新プログラムを簡単にのべたが、検討しなかった。そこで、私がおこなう。
私はNedit を3年ほど使っている。このスクリーンショットthis screenshotのような、Neditの典型的ウインドウであるX11にいるとき、全部の仕事をその中でおこなっている。
このプログラムでは、色々な特性を選択することが出来る。最も有名なのは以下のような多数の言語に関するシンタクス強調特性であろう。
何か変な理由で、上のリストに載っていない変な言語でプログラムするときは、自分のregexパターンを規定することが出来る。
Nedit もまた、大文字と小文字を区別するregexパターン一致により、複雑な検索をして方法を置き換えることが出来る。
一般的な検索/置き換えダイアログボックスは、次のようで、複雑な検索が出来るようになっている:
此処にある筈の図は原文でも何故か消えている
各メニューはトムオフっしてウインドウに残すことが出来る。これは特定のメニューを何度も使い、その度にクリックするのが面倒なとき、特に役立つ。えん
このプログラムは、有用なオプション幾つかを付けてロードすることが出来る、私は全部を使い切っていない。それでもが十分でないかのように、Neditは特注マクロを書いてもっと不思議な機能を定義することが出来るようになっている。
このプログラムを万人にお勧めする。Emacs / Vimに再投資しようとは思わないが、メモリを食い過ぎるて高価な"X11-Emacs"パケージに対する有望な代替物だと思う。
Nedit はhttp://www.nedit.org/から入手出来る。
お終いの時間 目次へ戻る
これで今月は終わり。こんなに長くなるとは思わなかった。私の大学生活は近く終わる。試験が5月末にある。その後、Linuxについてのアイデアを追求する夏が来る。
次回まで!
ご意見を下さい
下のe-メールアドレスをクリックしてご意見を下さい
mailto:thomas_adam16@yahoo.com
‥‥未来の道を照す。この記事は、ハイパーメディアの世界と、先駆者の業績に対し歴史的建アーキテクチャ的予想を加える。ハイパーメディアの考えは、45年前の世界的ウエブに遡るので、この記事はそれらの業績の記述から始める。ハイパーメディアなる用語の定義を集めた人はいないが、この記事では、先駆者の考えから導かれる定義を幾つか与える。
その後で、実際のハイパーメディアのアーキテクチャ革命の主な段階を説明する。その部分を読むときは、ソフトウエアの一般的発展方法を心に留めること(集中式からモジュール式へとは別に)。これがハイパーメディアシステムい発展に反映されてるのは当然だ。
1940年代:Vannevar Bush とMemex
40年代中頃、人類の知識の蓄積が急速に大きくなった。これにより、情報を効率よく認識し易い方法で記憶するのが極めて難しくなった。Bush [1] は、「情報過剰」の問題に気付き、情報の記憶、組織及び再現に関する空想的解決策を見出した。人間の脳特に記憶力のような連想の原理で働く機械を工夫した。Memex (Memory extensionの略)と言うこの機械は、Bushを後にテキストを扱うときハイパーテキスト及び何種類ものメディアを混ぜるときのハイパーメディアと呼ばれる分野の先駆者に仕上げた。今日、用語ハイパーテキストとハイパーメディアとは、同じ意味で用いられる。
ハイパーテキストの原理は文学で良く知られた概念だ。テキストを次々に真っ直ぐ読むのとと同時に、別の材料の脚注、注記、別の資料の参照に飛ぶことが出来る。Bushは、テキストのその部分に触ると、真っ直ぐ読むのから離れて、直接脚注、注記、又は別の資料に飛ぶことを考えた。この読み方は、情報管理の世界のハイパーテキストの定義に頼っている [2]。読者が入手出来ない資料など、読者が物理的に引照するが難しい場合や不能な場合は、電子的ハイパーテキストを用いて、情報を集めることが出来るので、文書の読み方が劇的に変わった。これを一歩進めて、異なる文書の間又は文書の一部に、新しいリンクを設け、リンクにコメント付けることが出来る。
機械的な方法その他の組合せで、Memexにこれらをさせるのが、Bushの夢だった。今日では、前の章を読むとき頭に浮かぶのは、W. W.W. [3]か又は、90年代中頃のビルゲーツの見通し「指で情報を」だった[4]。Memexは対照的に、情報を機械の中のマイクロフィルムに記憶するが、原理は同じだ。Memexに記憶された情報は、人手又はアルファベット順の検索でない付属検索器を用いまとめてリンクされる。付属検索器を用いるとデータ検索が直観的になる。この際、ハイパーテキストの別の定義が、情報を関連付けて組織する方法[2]となる。脳の中での関連付けが時間と共に又は使う回数と共に希薄になるのに対し、Memexの中の関連付けが長期にわたり威力を発揮する。
ハイパーテキストの両方の定義は、情報の集まりのナビゲーション方法につながっている。だからMemexは、ユーザーが読むのに加えて文書間を飛ぶ経験をさせることの出来るナビゲーション・ハイパーメディアと考えることが出来る。このことは、ハイパーテキストに前の物に加え、情報の非線型組織[2]との、別の定義(前の定義の拡張)を与えることが出来る。
1960年代:Douglas Engelbart とNLS
EngelbartはBushの記事を40年代後半に読んだが、Bushのハイパーテキストの概念を使う世界最初のシステムを開発するまでEngelbartの中で熟するのに15年を要した。NLS(oN-Line System)は(1)アイデアを扱うユーザー(2)各種文書間のリンクの作成(3)通信(4)テキスト処理(5)電子メールの送受信及び(6)ユーザーのシステム構成及びプログラムをサポートした。これは当時聞き慣れないことだった。この機能をユーザーが使い易いように改良するため、システムに当時としては画期的な技術を用いた。中でも、Engelbartは、画面を指してクリックするためマウスに似た品と、ユーザーインターフェイスを常に同じ方法であらわすためのウインドウマネージャとを発明した。ハイパーテキスト部分は、NLS全体の機能の本の一部に過ぎず、主な目的は、地理的に分散しているそれらをうまく共同させるのを助けるツールを提供することであった。今日では、この種のソフトウエアがグループウエアとして多数提案されている。
ユーザーインターフェイスは革命的で、その時代のコンピュータユーザーのレベルから、見て時代を遙かに先取りしていた。前には、ほとんどのプログラマは入力にパンチカード、出力にプリンタを使って間接的にコンピュータとインターフェイスしていた。NLSは全体として、来るべきシステムの予想に役立ち、80年代のAppleのGUI開発を刺激した。
1960年代:Ted NelsonとXanadu
Engelbarと同じく、NelsonもBushの記事[1]に奮起した。しかし、BushやEngelbartと違って、Nelsonは物理学と心理学の基礎があった。60年代始めに、作者が協働して著作、比較、校閲をおこなって著作を電子的に出版することの出来るシステムを想定した。
Xanaduのシステムは何度もリリースされたが、NelsonのXanaduは全く想像の域を出なかった。Xanaduは、それ自体でシステムではなく、別のシステムが守るべきアイデアに過ぎないので、Xanaduとは何かを正確に定義するのは難しい。その名前は、Xanaduの語を何も忘れない文学的記憶の世界[10]を指すのに使ったColeridgeの詩に由来する。実際、Xanaduの背景の考えの一つは、人の知識の大部分が存在する世界docuverseを作ることだった。60年代中頃にハイパーテキストの語を初めて作ったのもNelsonだ。だが彼の定義では、ハイパーテキストとハイパーメディアの両方を含んでいた。
Nelsonのもう一つのアイデアは、集合文書の変更がコピイ側文書にも自動的に伝わることなど、他の文書(又はその一部)を、参照によりコピイしたり、Nelsonのように仮想コピイを作って、参照する特殊な方法であった。この方法で、著者は全体文書著者部分の更新を作成する代わりに料金を請求することができる。このアイデアは、Nelsonの仮想コピイ機構が最初に禁止しようとした領域、著作権問題に物議を醸したが、ある程度今日のディープリンクに似ている。Xamaduプロジェクトからのアイデアの多くは結局w.w.w.及びハイパーメディアシステムにその道を見出すこととなった。
ハイパーメディア・アーキテクチャ
異なる種類のハイパーメディア・システムのアーキテクチャを説明するとき、三つの成分が常に存在する。巨大な一体型から成分ベースのシステムへの革新が起こった理由を良く理解して貰うため、これらの成分と目的を簡単に説明する。早期のハイパーメディア・シテムでも、ユーザーに情報を示す面倒を見るトップのアプリケーション層とともに、古典的な3層モデルを使っている。この層の下にリンク層があり、システムのモデルを仕上げ、管理構造とデータを取り扱う。これは連想と、期間限定の構造である連想をあらわすのに必要な情報である。データは、他方で、文書の実際の内容に関連する。最後に、記憶成分が、システム次第で、構造のみから構造と文書内容双方にわたる範囲の情報の記憶を取り扱う。発達は旋回しながら起こり、新世代毎に、以前にはシステムの核の部分であった機能性を、自分の成分に因数分解した(図参照、枠で囲んだ部分がハイパーメディアシステムの核の部分である成分をあらわす)。説明は一部[5]から取った。
一体型システム
早期のシステムの中の文書構造は、一体型の物であった(図1の左)。分割はユーザーに見えないけれども、3層全部が一つの論理過程にふくまれていた。一体型システムは、アプリケーション・プログラム・インターフェイス(API)も、構造やデータを記憶する方法を記述するプロトコルも公表しない、クローズドシステムと見なされる。これは他のシステムが一体型システムと交信してデータを交換するのを著しく困難にする。システムに記憶された情報の編集などの基本的機能でさえも、僅かなデータフォーマットをサポートするだけの内部アプリケーションが管理した。これは、例えばワープロで作った文章を一体型システムに直接記憶するのを不可能にした。少なくとも、文書内容を内部エディタにコピイしてセーブするまでは駄目だった。
システムがサポートするファイルフォーマットは、開発者が有用と認めたものに限られていた。ワープロで作った文書を引き渡すると、特殊なフォーマット(テキストの一部を太字にする、フォントの変更など)は廃棄された。これはユーザーを悩ませた。ハイパーテキストの機能を利用しようとすると、強力で慣れたアプリケーション環境を諦めてハイパーメディアシステムの内部アプリケーションを使わなければならなかった。ハイパーメディアの設計者は、ハイパーメディア・ソフトウエア開発の専門家だが、ワープロなど別の種類のソフトウエアの専門家ではないので、理想的なソリューションには程遠かった。
引渡問題と共に、システムが関連付けられるデータフォーマットの数が限られていることに関連する問題が生じた。関連する両文書又は両端は、システムの境界内になければならない、つまり、一体型システム内に記憶しなければならない。システムからのデータ取込も不様だ。システムは、包括的ハイパーメディア・システムでサポートすることの稀な自分のフォーマットでデータを記憶するので、取込中にデータが失われるからだ。
これらの欠点はあっても、一体型システムは80年代に広く使われた、その時期に使われたアプリケーションがデータ交換やお互いの交信に神経質でなかったためだろう。一体型ハイパーメディアシステム例は、KMS [2,6]、Intermedia [7]、Notecards [8]及びウインドウズ・ヘルプ・ファイルに使われたマイクロソフト Winhelp システムだ。 厳密に言うと、Winhelp システムその他数多くのヘルプシステムは、在来のハイパーテキストと違う主用途を有するが、兎に角ハイパーメディア機能を使っている。
![]() |
その他のシステムは、対照的に、文書の相手先に特定のフォーマットを課さない。しかし、それでも、あるAPIを呼び出すためにはアプリケーションのソースコードを変更する必要がある。だから、クライアント/サーバーベース・システムは90年代始めから、アプリケーション成分をハイパーメディア・システムの一体部分としないことにより一体型システムの問題点を解決した。LSSシステムの例はSunのLink Service [9]であるが、W.W.W.は、文書をシステムの一部として、ファイルシステム内のファイルとして記憶するHBHSシステムの例である。
オープン・ハイパーメディア・システム
OHSは、クライアント/サーバー概念の発展なので、OHSとクライアント/サーバー・システムは多数の特徴が共通である。クライアント/サーバー・システムをLSSとHBMSの項に分類出来る場合は、OHSはこれら一つの子孫となる。OHSは、リンク成分だけから成るので(図1の右)、(1)成分はアプリケーションの範囲を超えて使用される機能を含む(2)異なるプラットホームをまたいで働く(3)分散される(4)プロトコルとAPIを公開する、との点から、ミドルウエアと呼ばれることが多い。OHSは、文書の記憶が最早システムの核の一部ではないので、集中記憶がない点で、クライアント/サーバー・システムから区別することが出来る。
データが構造とは別に記憶されるので、テキスト、HTML、グラフィックなどほぼ任意のフォーマットの間のつながりをサポートすることが出来る。アプリケーションが文書関連の構造を必要とするとき、リンクサービスからアプリケーションに送られてデータの適用される。こうして多数のアプリケーションがシステムと会話することが出来る。データ記憶用に特定のプロトコル、つまりW.W.W.上のHTML、使う必要がないからである。詳しく言うと、構造情報は、多数の属性/値の組を含み、多数の属性はデータ型によって変化する。頭で考えると、テキストデータではオフセットが重要なので、座標を規定ればよい。
OHSは、一体型及びクライアント/サーバー・システムが持ち込んだ問題を幾つか解決したが、理想からは遠い。各OHSが独自のプロトコルとAPIを定義するので、すべてのOHSが同じ機能をサポートする訳ではない。LSSシステムの文書は、一般的に既存文書の間で作られた関連付けのみを準備するが、HBMSシステムの文書は、上述のLSS特性に加えて、バージョンや並行制御など内容関連機能もまた含む。その結果は、(1)特定OHSを頭においた書かれたアプリケーションは、別のシステムでは働かない(2)プロトコルとAPIが異なるため、システムを越えて情報を共有することは出来ない(3)最小プロトコルとAPIを規定する共通標準がないため、各システムは自分のAPIを実行して、個々のシステムが互いに連絡することは出来ないようにする、である。さらに、極めて僅かな他のドメインが存在するけれども、ほとんどのOHSは、ナビゲーショナル・ハイパーメディアを頭において設計されている。LSSに由来するOHSの例はMicrocosm [12]で、HBMSに由来するものはHyperform [11]である。
成分ベースのOHS
成分ベースのオープン・ハイパーメディア・システム(CB-OHS)は、「簡単な」オープン・ハイパーメディア・システムに良く似ている。しかし、名前の通り、成分の観念に大きく重点を置く。成分問題の外に、ここで注意すべきことは、この種のシステムが数種の構造領域をサポートし、そのデータを異なる位置に記憶することである。だから、OHSとは主としてリンク成分の点で異なる。
OHSに比べ、第一世代CB-OHS(1G CB-OHS)は、標準の導入により、個々の成分間の協力が掛ける問題を解決しようとした。今の所、ナビゲーショナル・ドメインにけるアプリケーションと構造サービスの連絡方法を規定する合意済み標準があり、追加標準を作成中である。1G CB-OHSのもう一つの目標は、新ドメイン、つまり分類上の又は空間的なドメイン、をサポートする新構造サービス(つまり新成分)の付加によりシステムを拡張して別のドメインもサポートするのを可能にすることである。代わりに、既存成分を改造してOHSを用いる場合のように幾つかのドメインを扱うようにする。CB-OHSに比べると、OHSは構造サービスを一つだけ含む。しかし、既存成分をこのように改造するのは、決して綺麗で融通性のある解決策ではない。然しすべての構造成分に共通なのは、同じAPIを通じて記憶成分にアクセスすることである。この意味は、新構造サービスが自動的に、バージョニング同時制御又は外の何か記憶成分の提供しなければならない「本来の」機構であることだ。
1世代システムがこれらの目標を満足するため、構造サービスは多数のプロトコルとAPIをクライアント(ブラウザ又はハイパーメディア・システムと連絡したい何かのアプリケーション。システムはオープンなハイパーメディア定義に拘泥するので、任意の型のアプリケーションになる)が利用出来るようにする。図2は、それぞれが構造的ドメインをあらわす三つの構造成分を持つアーキテクチャを示す。構造的ドメインはとりわけ、特殊抽象作用、つまりノード、リンク及びナビゲーショナル・ドメインの中の前後関係、抽象作用を扱う。前に述べたように、特殊抽象作用は、各ドメインの中で、既存のものに機能を混ぜるのに代わり、新成分の有力な候補にする。
![]() |
構造成分は記憶成分(ハイパーメディア記憶と言う)と通信するが、その成分は最早単一処理境界内にはないので、通信処理には追加の作業が必要である。ローカル通信は、ある形の処理内通信(IPC)又はローカルプロトコル呼出(LPC)で処理することが出来るが、ネットワークを越えると物事はややこしくなる。ネットワーク間通信をサポートするため、カスタム成分フレームワークに多くの作業が注ぎ込まれた。これがCB-OHSの第一世代と第二世代との間の主な相違でもある。第一世代CB-OHSはカスタムフレームワークを使ったが、第二世代CB-OHSはCOMやCORBAのような一般フレームワークを使う。そこで、開発者はハイパーメディア機能の開発に集中し、低いレベルの通信処理の詳細を無視することが出来る。既存アプリケーション統合に伴う問題は依然として残る。成分フレームワーク使用のため既存アプリケーションを改造するのは、そう簡単でないからだ。
構造成分とアプリケーションとの間のものなど、標準の定義は、オープン・ハイパーメディア・システム・ワーキング・グループ(OHSWG)の努力の結果だ。標準の展開につれ、あらゆるレベルのユーザーの役に立つ[13]。エンドユーザーは、ハイパーメディア機能を今日のあらゆるアプリケーションの内容になっている切り取り、コピイ、貼り付けと同じように考えるようになる[12]。未来のある時期には、「リンク開始」「リンク終了」などのメニュー項目を各アプリケーションに追加するようになり、それらの実行は今日の切り取り、コピイ、貼り付けより少しも難しくなくなるだろう。内容の著作者に取っては、文書と構造がプラットホームとハイパーメディア・システムの境界を越えて再使用出来るので、共通標準が重宝なものになるだろう。最後に、前述の編集機能の他に、開発者は、実際のシステムに関係なく、合意標準に従う限り、標準化システムが提供するものにも集中することが出来るだろう。
まとめ
ハイパーメディア・システムは、常に大きくなる情報の蓄積を、単にアルファベット順に記憶するのよりましな方法で組織する必要のため出現した。Bushが、人の記憶作業の方法に似た機能の機械の考えを述べて以来、人の知識は何千倍にもなり、W.W.W.が多くのハイパーメディア・システムに取って代わって、先駆者の夢を極めて多く実現した。しかし、同時に、W.W.W.が早期の包括的システムに比べ極めて簡単なシステムであることには、何の価値もない。然しこの簡単さ自体が、公衆に対しハイパーメディア機能を浸透させる成功の秘訣だった。
アーキテクチャは、他のすべてのソフトウエアのアーキテクチャと同様に次第に発展した。一体型システムは、他のシステムの経験を認める真剣であり過ぎた。それ以来、物事は大きき変化し、今日のシステムは、各種のフォーマットのデータを受け渡しする。受け渡しのための共通仕様は、SGML又はXMLやHTMLなどの誘導物のようなW3C標準であることが多い。このほかに、各種システムを越えて機構を旨く再使用する能力を、システムに加えよう。
W.W.W.の基本フォーマットであり、今日使用される支配的ハイパーメディア・システムであり、構造とデータの両者をまとめ、W.W.W.がハイパーメディアの感覚ではオープンと認められないHTMLに価値はない。W.W.W.を(成分ベースの)オープンなハイパーメディア・システムに使用との試みが幾つか(成功裡に)なされた。ハイパーメディア領域のすべては、研究中の大きい分野で、この記事で簡単に触れたが、システム及び概念に関する沢山の優れた資料が手に入る。
著作権 (C) 2002年 Ronnie Holm。e-メールで、この記事を使った場所をお知らせ下さい。この記事全部を逐語的に訳して配布することは、この通告を守る限り、どのメディアにも許します。
文献目録
競争社会にはアプリケーションを出来るだけ素速く開発するとの決定的先端がある。Python の堅牢性とGTKの素朴な力とを結合した PyGTK を使うとそれが出来る。この記事は、PyGTKを使って科学用電卓を作る指導書だ。
1.PyGTKとは?
PyGTK ソース・ディストリビューションから引用しよう。
Well, let me quote from the PyGTK source distribution:
「このアーカイブには、gtkをPythonプログラムで使うことの出来る
モジュールが含まれる。現在は、完成した 結合だ。
バージョン番号は低いが、このソフトウエアは極めて有用で
複雑さが中位のプログラムを書くのに役立つ」
- README, pygtk-0.6.4
2.何をするか?
pygtkを使って小型科学用電卓を構築する。各段階を詳細に説明する。この処理の段階全部を終えると、pygtkが良く解る。記事の終わりに完成ソースコードへのリンクがある。
3.準備すべきパケージと基本知識
このパケージは殆どのLinuxディストリビューションと一緒に入手出来る。私の説明は、Linux RedHat 6.2 マシンに搭載したPython 1.5.2 に基づく python プログラムミングを知らなくとも、心配ない!記事に示された指導に従うだけでよい。
このパケージの新しいバージョンは下記から入手することが出来る:
4.始めよう
この指導書は三つの段階に分かれる。コードとそれに対応する出力を各段階で示す。
5.段階1−ウインドウの構築
先ず、ウインドウを作る必要がある。ウインドウとは容器だ。ボタン、テーブルなどがウインドウに入る。エディタを使ってファイル stage1.py を開く。これに次の行を書き込む:
from gtk import *
win = GtkWindow()
def main():
win.set_usize(300, 350)
win.connect("destroy", mainquit)
win.set_title("Scientific Calculator")
win.show()
mainloop()
main()
1行目は、gtkと言う名のモジュールからメソッドを取り込む。つまり、これでgtkライブラリにあるファンクションを使うことが出来る。
続いて、GtkWindow型のオブジェクト作りwinと名付ける。その後、ウインドウのサイズを設定する。第一引数は幅で、第二引数は高さだ。またウインドウのタイトルも設定する。次ぎに、showの名のメソッドを呼び出す。このメソッドはすべてのオブジェクトの場合に存在する。特定オブジェクトのパラメータを設定した後には、必ずshowを呼び出さなければならない。特定オブジェクトに関するshowを呼び出した後にのみ、ユーザーが見られるようになる。オブジェクトを論理的に作成しても、オブジェクトに関するshowを呼び出さないと、オブジェクトは物理的に見えるようにならない。
ウインドウの信号消去をファンクション mainquit に接続する。mainquit はgtkの内部ファンクションで、これを呼び出すと今走っているアプリケーションを終了することが出来る。信号を気にすることはない。今は、ウインドウを消去するとき(ウインドウ頭の十字印をクリックして)は何時でもmainquit が呼び出されることを理解するだけでよい。つまり、ウインドウズを消去すると、アプリケーションからも離れる。
mainloop() もまたgtkライブラリの内部ファンクションだ。mainloopを呼び出すと、立ち上がったアプリケーションがループの中で、イベントが起こるのを待つ。ここではウインドウが画面上に現れて待つだけだ。 'mainloop' の中で我々の動作を待っている。アプリケーションがループを出るのは、ウインドウを消去してときだけだ。
ファイルをセーブする。エディタを終わってシェルプロンプトに戻る。プロンプトで:
python stage1.py
とタイプする。出力を見るには、Xwindow にいなければならないのを想起されたい。
出力の画面を下記に示す。
6.段階2−テーブルとボタンの構築
第二のファイル、stage2.py を 書こう。file stage2.py に次のコードを書く
from gtk import *
rows=9
cols=4
win = GtkWindow()
box = GtkVBox()
table = GtkTable(rows, cols, FALSE)
text = GtkText()
close = GtkButton("close")
button_strings=['hypot(','e',',','clear','log(','log10(','pow(','pi','sinh(','cosh(','tanh(','sqrt(','asin(',
'acos(','atan(','(','sin(','cos(','tan(',')','7','8','9','/','4','5','6','*','1','2','3','-', '0','.','=','+'
]
button = map(lambda i:GtkButton(button_strings[i]), range(rows*cols))
def main():
win.set_usize(300, 350)
win.connect("destroy", mainquit)
win.set_title("Scientific Calculator")
win.add(box)
box.show()
text.set_editable(FALSE)
text.set_usize(300,1)
text.show()
text.insert_defaults(" ")
box.pack_start(text)
table.set_row_spacings(5)
table.set_col_spacings(5)
table.set_border_width(0)
box.pack_start(table)
table.show()
for i in range(rows*cols) :
y,x = divmod(i, cols)
table.attach(button[i], x,x+1, y,y+1)
button[i].show()
close.show()
box.pack_start(close)
win.show()
mainloop()
main()
変数 rows と cols はそれぞれボタンの行と列を記憶するのに用いられる。新しいオブジェクトのために−テーブル、ボックス、テキストボックス及びボタンを作る。 GtkButton に対する引数は、ボタンのラベルである。だから closeは、"closed" とラベルのついたボタンである。
アレー button_strings は、ボタンのラベルを記憶するのに用いられる。ここでは科学用電卓のキイに現れる記号を用いる。変数 button は、ボタンのアレーだ。mapファンクションがボタンの rows*cols 番号を作る。ボタンのラベルは、アレー button_strings から取る。だから、i番ボタンは button_strings からのi番文字列をラベルとして有する。iの範囲は、0からrows*cols-1までである。
ボックスをウインドウに入れる。このボックスにテーブルを入れる。そのテーブルの中にボタンを入れる。ウインドウ、テーブル及びボタンを倫理的に作った後、それに対応する show を呼び出す。win.add を使って、 box をウインドウに加える。
text.set_editable(FALSE) を使って、テキストボックスを編集不能にする。これはタイプしてテキストボックスに外から何かを加えることは出来ないことを意味する。text.set_usize は、テキストボックスのサイズを設定し、text.insert_defaults は、空白文字列を規定値としてテキストボックスに挿入する。このテキストボックスを box の始めに詰め込む。
テキストボックスの後でテーブルをボックスに挿入する。テーブルの引数設定は平凡だ。forループで四つのボタンを9行に入れる。ステートメント y,x = divmod(i, cols) は、iの値を cols で割って、商をyに剰余をxに記憶する。
最後に close ボタンをボックスに入れる。pack_start がオブジェクトを、ボックス内で利用出来る次の自由空間に入れることを想起されたい。
ファイルをセーブして
python stage2.py
とタイプする。出力の画面を下記に示す。
7.段階3−電卓用バックグラウンドの構築
アプリケーションに電卓の作用をさせるため、幾つかのファンクションを書かなければならない。このファンクションをバックグラウンドと言う。これらは、scical.py にタイプする行である。これが最後の段階だ。scical.py は、完成出力を含む。プログラムを書きに示す:
from gtk import *
from math import *
toeval=' '
rows=9
cols=4
win = GtkWindow()
box = GtkVBox()
table = GtkTable(rows, cols, FALSE)
text = GtkText()
close = GtkButton("close")
button_strings=['hypot(','e',',','clear','log(','log10(','pow(','pi','sinh(','cosh(','tanh(','sqrt(','asin(','acos(','atan(','(','sin(','cos(','tan(',')','7','8','9','/','4','5','6','*','1','2','3','-', '0','.','=','+']
button = map(lambda i:GtkButton(button_strings[i]), range(rows*cols))
def myeval(*args):
global toeval
try :
b=str(eval(toeval))
except:
b= "error"
toeval=''
else : toeval=b
text.backward_delete(text.get_point())
text.insert_defaults(b)
def mydel(*args):
global toeval
text.backward_delete(text.get_point())
toeval=''
def calcclose(*args):
global toeval
myeval()
win.destroy()
def print_string(args,i):
global toeval
text.backward_delete(text.get_point())
text.backward_delete(len(toeval))
toeval=toeval+button_strings[i]
text.insert_defaults(toeval)
def main():
win.set_usize(300, 350)
win.connect("destroy", mainquit)
win.set_title("Scientific Calculator: scical (C) 2002 Krishnakumar.R, Share Under GPL.")
win.add(box)
box.show()
text.set_editable(FALSE)
text.set_usize(300,1)
text.show()
text.insert_defaults(" ")
box.pack_start(text)
table.set_row_spacings(5)
table.set_col_spacings(5)
table.set_border_width(0)
box.pack_start(table)
table.show()
for i in range(rows*cols) :
if i==(rows*cols-2) : button[i].connect("clicked",myeval)
elif (i==(cols-1)) : button[i].connect("clicked",mydel)
else : button[i].connect("clicked",print_string,i)
y,x = divmod(i, 4)
table.attach(button[i], x,x+1, y,y+1)
button[i].show()
close.show()
close.connect("clicked",calcclose)
box.pack_start(close)
win.show()
mainloop()
main()
新変数toevalを導入した。この変数は、計算すべき文字列を記憶する。計算すべき文字列は一番上のテキストボックスに存在する。=ボタンを押したときこの文字列を計算する。これはファンクションmyevalを呼び出しておこなう。Pytohonファンクションevalを文字列内容を計算し、結果をテキストボックスにプリントする。文字列の計算が(構文エラーなどで)出来ないときは、文字列を'error'プリントする。この処理のためtryとexceptを用る。
clear、 close 及び =以外のその他のボタンを押すと、ファンクションprint_stringがトリガされる。このファンクションは、先ずテキストボックスをクリアする。押されたボタンに対応する文字列を、変数toevalに付加してtoevalをテキストボックスに表示する。
ここで close ボタンを押すと、ファンクション calcclose が呼び出され、これがウインドウを壊す。clear ボタンを押すと、ファンクション mydel が呼び出され、テキストボックスがクリアされる。ファンクション main では、for ループに三つのステートメントを加えた。これらは、対応するファンクションをボタンに割り当てるためのものである。 = ボタンを myevalファンクションに付属させ、 clear を mydel に付属させるなどである。
こうして、完全な科学用電卓が使えるようになった。シェルプロンプトで scical.py とタイプするだけで、科学用電卓が働く。
最終アプリケーションの絵を下に示す。
8.まとめ
各段のコードは下記のリンクをクリックするとダウンロードすることが出来る。
これらはすべて .txt .txt 拡張子を有する。この拡張子を除去してプログラムを走らせる。例えば、実行前に stage1.py.txt を stage1.py に変更する。
pygtk パケージについて来る examples ディレクトリには沢山の例がある。これらは、Linux, RehHat 6.2 では、/usr/doc/pygtk-0.6.4/examples/ ディレクトリの下で見付かる。これらのプログラムを走らせて、そのソースコードを読む。それは、複雑なアプリケーションの開発に大いに役立つであろう。
Krishnakumar R.(著者紹介)
Copyright © 2002, Krishnakumar R..Copying license http://www.linuxgazette.com/copying.htmlPublished in Issue 78 of Linux Gazette, May 2002
GUI開発ライブラリの広大な世界の中に、超然として、Trolltech ASの開発したC++用 'Qt' がある。 'Qt' は1996年に紹介されて以来、このライブラリを使って各種アプリケーション用に優れたインターフェイスが開発された。
Qtは、クロスプラットホームで、MS/Windows,Unix/X11 (Linux, Sun Solaris, HP-UX, Digital Unix, IBM AIX, SGI IRIX and many other flavors),Macintosh ( Mac OS X ) 及び Embedded プラットホームをサポートする。これとは別に、'Qt' はオブジェクト指向、コンポーネントベース、プログラマの選択に任された多数のウィジェットを有する。'Qt' は「Qtプロフェッショナル」と「Qtエンタープライズ」と両方のバージョンで商業的に入手することが出来る。無料版はQtの非売バージョンで(http://www.trolltech.com/).からダウンロードすることが出来る。
開始
先ずライブラリをダウンロードする。これから取り上げる例ではLinux用Qt/X11バージョンをダウンロードしたと、仮定する。
インストールするにはスーパーユーザー特権が必要なので、'root'.にいることを確認する。
/usr/local ディレクトリにuntarする:
[root@Linux local]# tar -zxvf qt-x11-free-3.0.1
[root@Linux local]# cd qt-x11-free-3.0.1
次ぎに、使用に必要なオプションを付けてライブラリをコンパイルしインストールする。'Qt' ライブラリは、必要に合った特注オプションを付けてコンパイルすることが出来る。基本特性とは別に、gif読取、スレディング、STL、リモートコントロール、Xinerama、XftFreeタイプ及びXセッション管理をコンパイルする。
前に進む前に、正しい位置をポイントする環境変数を次のように設定するの忘れないこと:
QTDIR=/usr/local/qt-x11-free-3.0.1
PATH=$QTDIR/bin:$PATH
MANPATH=$QTDIR/man:$MANPATH
LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
export QTDIR PATH MANPATH LD_LIBRARY_PATH
この情報を、ホームディレクトリにある .profile に含むことが出来る。
[root@Linux qt-x11-free-3.0.1]# ./configure -qt-gif -thread -stl -remote -xinerama -xft -sm
[root@Linux qt-x11-free-3.0.1]# make install
すべて旨く行ったら、 'Qt' ライブラリがインストールされた。
'Qt' を用いる第一ステップ
'Qt'ライブラリを用いてC++でプログラムを書くには、仕事を容易にするため、'Qt'ライブラリで利用出来る重要なツールとユティリティを理解しなければならない
Qmake は、'.pro' ファイルに基づく情報を用いて makefiles するのに用いる。
簡単なプロジェクトファイルの見掛けは次のようだ:
SOURCES = hello.cpp
HEADERS = hello.h
CONFIG += qt warn_on release
TARGET = hello
ここで、'SOURCES' はアプリケーション用実行ソース全部を定義するのに使うことが出来る。一つ以上のソースファイルがあるときは次のように定義する:
SOURCES = hello.cpp newone.cpp
又は代わりに次のようにする:
SOURCES += hello.cpp
SOURCES += newone.cpp
同様に'HEADERS'はソースに所属するヘッダファイルを規定するのに用いる。'CONFIG' 部分はアプリケーション構成に付いての情報をqmakeに渡すのに便利である。このプロジェクトファイルの名は、アプリケーションの実行可能プログラムと同一でなければならない。今の場合は 'hello.pro' である。.
Makefile は、次のコマンドを送って作成する:
[root@Linux mydirectory]# qmake -o Makefile hello.pro
Qt Designer
Qt Designer は、'Qt' ライブラリを用いてユーザーインターフェイスを視覚的に設計しコードするのに用いる。WYSIWYGインターフェイスは、ユーザーインターフェイスを微調整し、各種ウィジェットを用いて実験するためにある。このデザイナは、CUI用のソース全体をさらに強化するため何時でも作成する能力がある。 'Qt Designer' の詳細は一緒に来る資料で調べること。
Hello World!
基本的 'Hello World' プログラムの理解から始めよう。任意のソースエディタを用いて次のコードを書く:
#include <qapplication.h>
#include <qpushbutton.h>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QPushButton hello( "Hello world!", 0 );
hello.resize( 100, 30 );
a.setMainWidget( &hello );
hello.show();
return a.exec();
}
このコードを平文ファイル('hello.cpp')としてセーブする。このコードを、次のようにproject file (.pro)を作ってコンパイルする:
TEMPLATE = app
CONFIG += qt warn_on release
HEADERS =
SOURCES = hello.cpp
TARGET = hello
このファイルを 'hello.pro' としてソースファイルと同じディレクトリにセーブし、Makefile の作成を続ける。
[root@Linux mydirectory]# qmake -o Makefile hello.pro
Compile it using 'make'
[root@Linux mydirectory]# make これで初めての'Qt' をテストする準備が出来た。'X' にいることを条件に、プログラムを実行可能に立ち上げることが出来る [root@Linux mydirectory]# ./hello 次のようなものが現れる筈だ:
書き上げたコードのそれぞれを理解しよう
最初の2行には、QApplication とQPushButton クラスの定義が含まれる。
アプリケーション全体に QApplication オブジェクト一つだけがなければならないことを常に忘れないこと。
他のC++プログラムと同じく、main() ファンクションはプログラムへのエントリポイントで、argc はコマンド行引数、 argv はコマンド行引数のアレーである。
次ぎにこれらQt の受け取った引数を下のように渡す。
QApplication a(argc, argv)
次ぎにQPushButton オブジェクトを作り、二つの引数、ボタンのラベルと親ウインドウ(0 、つまりこの場合は自分自身のウインドウ)、を用いてそのコンストラクタを初期化する。
次のコードを用いてボタンのサイズを変える:
hello.resize(100,30);
Qt アプリケーションは、任意選択で、それに関連するmain widgetを持つことが出来る。main widgetを閉じると、アプリケーションが終わる。
main widgetを次のように設定する:
a.setMainWidget( &hello );
次ぎに main widget が見えるように設定する。見えるようにするには常に show() を呼び出さなければならない。
hello.show();
次ぎに、制御を最終的にQt にわたす。ここで忘れてはならない重要点は、アプリケーションが生きて、アプリケーションの存在を返答するまでexec()が走り続けることである。
例えば、ローカルマシンにウインドウを作って示したいとすると、次のように書くことが出来る:
リスト 1: example1.cpp
#include <X11/Xlib.h>
#include <unistd.h>
main()
{
// ディスプレーを開く
Display *d = XOpenDisplay(0);
if ( d )
{
// ウインドウを作る
Window w = XCreateWindow(d, DefaultRootWindow(d), 0, 0, 200,
100, 0, CopyFromParent, CopyFromParent,
CopyFromParent, 0, 0);
// ウインドウを示す
XMapWindow(d, w);
XFlush(d);
// ウインドウを見るに十分なだけスリープ
sleep(10);
}
return 0;
}
次のコマンドでプログラムをコンパイルすることが出来る:
prompt$ g++ test.cpp -L/usr/X11R6/lib -lX11 prompt$ ./a.out
すると画面に次のウインドウが10秒間現れる:
この記事の目的は、Xlibアプリケーションを開発するとき使用することの出来る簡単なクラスを示すことにある。ボタンが一つ付いたウインドウを持つアプリケーション例を作る。そのボタンは、Xlibライブラリだけを用いて開発するカスタムボタンである。
「 QTやGTKのようなウィジェットライブラリを使っては?」と自問自答されるだろう。尤もな疑問だ。私はQTを束区、Linuxプラットホーム用を目標にC++アプリケーションを開発するとき、極めて役に立つと思う。
これらのクラスを開発した理由は、Xウインドウシステムを良く理解するためだ。それにはQTやGTKなどの覆いの下で何が行われているかを理解せざるを得なかった。仕上げたとき、私の作ったクラスが実際に役立つことが解った。
だから、この記事から学んで、自分のアプリケーションに活用して頂きたい。
コードに入ろう。この章ではXlibの基本的特徴を検討する。
最初に作ったクラスは、ディスプレーの開閉を担当する display クラスだ。example1.cppで、ディスプレー・プロパティをXCloseDisplay()で閉じなかったのに気付かれただろう。このクラスを用いて、プログラムを出る前に閉じられる。我々の例は次の通りだ:
リスト2: example2.cpp
#include <unistd.h>
#include "xlib++/display.hpp"
using namespace xlib;
main()
{
try
{
// ディスプレーを開く
display d("");
//ウインドウを作る
Window w = XCreateWindow((Display*)d,
DefaultRootWindow((Display*)d),
0, 0, 200, 100, 0, CopyFromParent,
CopyFromParent, CopyFromParent, 0, 0);
// ウインドウを示す
XMapWindow(d, w);
XFlush(d);
// ウインドウを見るに十分なだけスリープ
sleep(10);
}
catch ( open_display_exception& e )
{
std::cout << "Exception: " << e.what() << "\n";
}
return 0;
}
華々しいことは何もない、ディスプレーを開いて閉じただけだ。 implementation で、display くらすがDisplay*演算子を定義するのに気付かれるだろう。しなければならないのは、オブジェクトを実際のXlibディスプレー・ポインタを得るようキャストしすることだ。
try/catch にも注意のこと。この記事のクラスは全部信号エラー状態に対しカスタム除外を入れる。
次ぎにしたいのはウインドウ作成を簡単にすることだ。そこで、mixに window クラスを加えた。このクラスは、コンストラクタの中にウインドウを作って示し、デストラクタでそのウインドウを壊す。我々の例は、次のようになる(event_dispatcherに注意、次ぎにこの問題に移る):
リスト3 : example3.cpp
#include "xlib++/display.hpp"
#include "xlib++/window.hpp"
using namespace xlib;
class main_window : public window
{
public:
main_window ( event_dispatcher& e ) : window ( e ) {};
~main_window(){};
};
main()
{
try
{
// Open a display.
display d("");
event_dispatcher events ( d );
main_window w ( events ); // top-level
events.run();
}
catch ( exception_with_text& e )
{
std::cout << "Exception: " << e.what() << "\n";
}
return 0;
}
main_window クラスが xlib::window の引き継ぎであることに注意。 main_window オブジェクトを作るとき、実際のXlibウインドウを作るベースclass' コンストラクタが呼び出される。
最後の例にある event_dispatcher クラスに気付かれただろう。このクラスはアプリケーションの待ち行列からイベント取り外して、正しいウインドウに送る。
このクラスは次のように定義される:
リスト4 : event_dispatcher.hpp
class event_dispatcher
{
// constructor, destructor, and others...
[snip...]
register_window ( window_base *p );
unregister_window ( window_base *p );
run();
stop();
handle_event ( event );
}
event_dispatcher は、イベントをwindow_base インターフェイスを介してwindowクラスに渡す。この記事の中のwindewをあらわすクラスはすべて、このクラスから導かれ、送り主からのメッセージを受け取ることが出来る。これらがregister_window を用いてレジスタされると、メッセージの受信を始める。 window_base は次のように宣言され、これらから導かれるクラスはすべてこれらのメソッドを定義しなければならない。
リスト5 : window_base.hpp
virtual void on_expose() = 0;
virtual void on_show() = 0;
virtual void on_hide() = 0;
virtual void on_left_button_down ( int x, int y ) = 0;
virtual void on_right_button_down ( int x, int y ) = 0;
virtual void on_left_button_up ( int x, int y ) = 0;
virtual void on_right_button_up ( int x, int y ) = 0;
virtual void on_mouse_enter ( int x, int y ) = 0;
virtual void on_mouse_exit ( int x, int y ) = 0;
virtual void on_mouse_move ( int x, int y ) = 0;
virtual void on_got_focus() = 0;
virtual void on_lost_focus() = 0;
virtual void on_key_press ( character c ) = 0;
virtual void on_key_release ( character c ) = 0;
virtual void on_create() = 0;
virtual void on_destroy() = 0;
これが実際に働くかを見よう。ウインドウでButtonPress イベントを扱う。main_window classに次のコードを加える:
リスト6 : example4.cpp
class main_window : public window
{
public:
main_window ( event_dispatcher& e ) : window ( e ) {};
~main_window(){};
void on_left_button_down ( int x, int y )
{
std::cout << "on_left_button_down()\n";
}
};
コードをコンパイルして例を走らせ、ウインドウの中をクリックする。働く! event_dispatcherがButtonPress メッセージを受け取って、あらかじめ定義した on_left_button_down メソッドを経由してウインドウに送る。
次ぎにウインドウに描画する。Xウインドウシステムは、引き込む"graphics context" の概念をていぎするので、当然我々も graphics_context.と言う名のクラスを作った。以下がクラスの定義だ:
リスト 7 : graphics_context.hpp
class graphics_context
{
public:
graphics_context ( display& d, int window_id );
~graphics_context();
void draw_line ( line l );
void draw_rectangle ( rectangle rect );
void draw_text ( point origin, std::string text );
void fill_rectangle ( rectangle rect );
void set_foreground ( color& c );
void set_background ( color& c );
rectangle get_text_rect ( std::string text );
std::vector get_character_widths ( std::string text );
int get_text_height ();
long id();
private:
display& m_display;
int m_window_id;
GC m_gc;
};
このクラスにウインドウidと、display オブジェクトを渡すと、drawing メソッドを使って好きなことを描くことが出来る。試して見よう。我々の例に次を付け加える:
リスト8 : example5.cpp
#include "xlib++/display.hpp"
#include "xlib++/window.hpp"
#include "xlib++/graphics_context.hpp"
using namespace xlib;
class main_window : public window
{
public:
main_window ( event_dispatcher& e ) : window ( e ) {};
~main_window(){};
void on_expose ()
{
graphics_context gc ( get_display(),
id() );
gc.draw_line ( line ( point(0,0), point(50,50) ) );
gc.draw_text ( point(0, 70), "I'm drawing!!" );
}
};
on_expose() メソッドは、ウインドウが表示、つまりexposeされたとき何時でも呼び出される。このメソッドでは、ウインドウのクライアント領域に直線とテキストを描く。この例をコンパイルして走らせると、以下のような画面が現れる筈だ:
graphics_context クラスは、この記事の後の方で何度も使う。
上のコードに helper クラス、point and line、があるのに気付かれただろう。これらは私の作った小さいクラスで、すべて形状に関する。今は必要ない用に思えるが、後で変形などの複雑な操作をおこなうとき役立つ。例えば、."line_x += 5; line_y += 5;"と命じるより、"line.move_x(5)"と命じる方が楽で、エラーも起こり難い。
簡単な材料が揃ったので、再使用出来る実際のウィジェット作成に移ろう。アプリケーションで使用することの出来るコマンドボタンの作成に集中する。このボタンの要件は次の通りだ:
これらは簡単なコントロールのように思えるが、これら全部が意味することは些細なことではない。以下の節で説明する。
先ず、このコマンドボタンに別のウインドウを作らなければならない。コンストラクタがshow メソッドを呼び出し、これが create メソッドを呼び出し、これがウインドウ作成をおこなう:
リスト9 : command_button.hpp
virtual void create()
{
if ( m_window ) return;
m_window = XCreateSimpleWindow ( m_display, m_parent.id(),
m_rect.origin().x(),
m_rect.origin().y(),
m_rect.width(),
m_rect.height(),
0, WhitePixel((void*)m_display,0),
WhitePixel((void*)m_display,0));
if ( m_window == 0 )
{
throw create_button_exception
( "could not create the command button" );
}
m_parent.get_event_dispatcher().register_window ( this );
set_background ( m_background );
}
window クラスのコンストラクタに大変良く似ている。Xlib API XCreateSimpleWindow()を用いて先ずウインドウを作り、続いて, event_dispatcher に自分を登録してイベントを自分で受け取るようにし、最後にバックグラウンドを設定する。
XCreateSimpleWindow()に対する呼出の中に親ウインドウのidを渡すのに注意。Xlibに我々のコマンドボタンが親ウインドウの子ウインドウであることを告げている。
4.3 "pressed" と"not pressed" ドロー状態の実行
コマンドボタンはevent_dispatcher に自分を登録したので、自分を引き出す必要があるとき on_expose()イベントを受け取る。両状態を引き出すのに、我々はgraphics_context クラスを用いる:
以下は、"not pressed" 状態のために使うコードである:
リスト10 : command_button.hpp
// bottom
gc.draw_line ( line ( point(0,
rect.height()-1),
point(rect.width()-1,
rect.height()-1) ) );
// right
gc.draw_line ( line ( point ( rect.width()-1,
0 ),
point ( rect.width()-1,
rect.height()-1 ) ) );
gc.set_foreground ( white );
// top
gc.draw_line ( line ( point ( 0,0 ),
point ( rect.width()-2, 0 ) ) );
// left
gc.draw_line ( line ( point ( 0,0 ),
point ( 0, rect.height()-2 ) ) );
gc.set_foreground ( gray );
// bottom
gc.draw_line ( line ( point ( 1, rect.height()-2 ),
point(rect.width()-2,rect.height()-2) ) );
// right
gc.draw_line ( line ( point ( rect.width()-2, 1 ),
point(rect.width()-2,rect.height()-2) ) );
後で、最終的にこのコードをコンパイルすると、ボタンは次のような見掛けになる:
代わりに、ボタンを押すときは、それを引き出すのに次のコードを用いる:
リスト11 : command_button.hpp
gc.set_foreground ( white );
// bottom
gc.draw_line ( line ( point(1,rect.height()-1),
point(rect.width()-1,rect.height()-1) ) );
// right
gc.draw_line ( line ( point ( rect.width()-1, 1 ),
point ( rect.width()-1, rect.height()-1 ) ) );
gc.set_foreground ( black );
// top
gc.draw_line ( line ( point ( 0,0 ),
point ( rect.width()-1, 0 ) ) );
// left
gc.draw_line ( line ( point ( 0,0 ),
point ( 0, rect.height()-1 ) ) );
gc.set_foreground ( gray );
// top
gc.draw_line ( line ( point ( 1, 1 ),
point(rect.width()-2,1) ) );
// left
gc.draw_line ( line ( point ( 1, 1 ),
point( 1, rect.height()-2 ) ) );
完成すると次のような見掛けになる:
これは極めて簡単な問題のように思える−コントロールの上でマウスを押したとき"pressed"を、上がっているとき"not pressed" 状態を呼び出す−だけだ。だが、これは正しくない。コントロールの上でマウス左ボタンを押したままにして枠の外に動かすと、今マウス左ボタンが押されていても、コマンドボタンは "not pressed" 状態を引き出す。
command_button クラスは、これを扱うのに、二つのメンバー変数−m_is_downとm_is_mouse_over−変数を用いる。最初、コントロールの上でマウスを押したとき ( on_left_button_down()参照), down 状態にしてコントロールをリフレッシュする。その結果、コマンドボタンは自分をpressed状態に引き込む。何時か、マウスがコントロールの枠の外に動くと(on_mouse_exit()参照), m_is_mouse_over が偽に設定され、コントロールがリフレッシュされる。その結果、コマンドボタンは自分を "not pressed" 状態に引き込む。このとき、マウスがコントロールの枠の中に動くと、m_is_mouse_over が真に戻って、コントロールはpressに引き込まれる。マウスボタンを解放されると、自分を"not pressed" 状態に設定して、自分をリフレッシュする。
これは極めて簡単な問題だ。このコマンドボタンを使う人がテキストを得て表示出来るようにする。コードを示す:
リスト12 : command_button.hpp
std::string get_name() { return m_name; }
void set_name ( std::string s ) { m_name = s; refresh(); }
refresh() は、コントロール自体に新しいテキストを書かせるためここに置いた。
このコマンドボタンを使う人が、何処をクリックしたか判るようにする。そのため、"on_click()"イベントを作る。以下が command_button_baseクラスの定義だ:
リスト 13 : command_button_base.hpp
namespace xlib
{
class command_button_base : public window_base
{
public:
virtual void on_click () = 0;
};
};
ここで基本的に言っていることは「ウインドウの行うイベントすべてに加えてもう一つ on_click()をサポートする」ということだ。このボタンを使う人は、これから新しいクラスを導き出し、 on_click() クラスを実行して、適切な動作をさせることが出来る。
この記事がお役に立てば幸いだ。Xlibの色々な特性を説明し、C++クラスにまとめて、sjぴらおmpXlib開発を容易にした。この記事又はXlib開発一般に付いての質問、ご意見、ご教示がe-メールされたい email me.