TkPlot.pl
- Perl
WebのCGIやテキスト処理に使われるインタープリタ形のスクリプト言語。
文法はCに近いが、正規表現による文字列処理が標準で使えること、ハッシュ(連想配列)や型制限のない変数など、柔軟なプログラムが可能。
CPAN(Comprehensive Perl Archive
Network)をはじめとして、多くのPerlモジュールが入手可能で、その中にはネットワーク処理、データベース処理、PDFや画像ファイル処理など、あらゆる分野のモジュールが含まれている。
- Perl/Tk
Perlはスクリプト言語のため、標準ではCUI (Character User
Interface:文字列でコマンドを打ち込み、文字列で結果を表示する)でしか使えない。このため、UnixのX
Window上でGUI (Graphics User Interface: MS-Windowsのよに、
メニューやボタンからやりたいことを入力する)アプリケーションを作るスクリプト言語であるTcl/TkをPerlに移植したのがPerl/Tkである。Tclは、X
Window用に開発されたスクリプト言語で、現在はMS-Windowsにも移植されている。
- サブルーチン
プログラム中で、ある一定のかたまりのプログラムコードを、ブラックボックスとして使えるように分離したもの。
- 関数
もともと、FORTRAN、BASICなどでは、返り値を返すサブルーチンを特別扱いして関数と読んでいた。
Cではこの区別がなくなったため、サブルーチンと関数を区別せず、関数と呼んでいる。
Perlでは関数をsubで指定するため、サブルーチンと呼ぶ方が自然?
- モジュール
Perlのサブルーチンの組を集めて一つの名前空間に収めたもの。通常1つのファイル中につくり、ファイル名はモジュール名.pm、ファイルの先頭に
package モジュール名
を指定することで、他のスクリプトから use
モジュール名; とすることで使えるようになる。
- オブジェクト指向プログラミング
オブジェクト指向プログラミングの基本的な考え方は、一つの対象に対して、それが必要とする変数と動作をまとめて管理しようとするものである。
たとえば、Carというオブジェクトを考えたとき、変数としては
$CarType, $Size, $Weight, $Color, $MaxSpeedなど、動作としてはStart(),
Stop(), OpenDoor()などが考えられる。
オブジェクト指向プログラミングでは、動作をさせるサブルーチンを「メソッド」、変数を「属性」とよぶ。
- メソッド、属性
上を参考
- クラス
オブジェクトの属性とメソッドを定義したもの。
- オブジェクト
Perlでは、クラスを変数として実体化させたものをオブジェクトと呼ぶ(厳密には、上で書いている「オブジェクト」と意味が違う)。
C++などでは、クラスのインスタンスと呼ぶ(?)。
- Object Perl
C++、Javaなどのオブジェクト指向言語が有名になっているが、Perlでは、最小限の言語拡張によって、同様のオブジェクト指向型のプログラムを作れるようになっている。特にハッシュ(連想配列)をベースにしたクラスの作成法は、C++ではまねのできない柔軟なプログラムを用意にする。
- ハッシュ(連想配列)
通常の配列は、添え字として数字しか取らず、
$a[10]などとしてアクセスするが、添え字に文字列を指定するものを、Perlではハッシュと呼び、$a{'abc'}などとしてアクセスする。他の言語では連想配列と呼ぶことも多い。
\\lucid.msl.titech.ac.jp\pool\CalculationServer\Windows\D\Programs\Perl
の内容を、適当なディレクトリィにコピーします(ここにObject PerlライブラリィとTkPlotが含まれています)。
他にあった方が良いプログラム(エディターやPostScript
Viewerなど)も含まれているので、
\\lucid.msl.titech.ac.jp\pool\CalculationServer\Windows\D\Programs
をすべて
d:\Programs
にコピーすることを薦めます。この場合には、それぞれのディレクトリィは、
になります。
Perl/Tk と神谷が作ったObject Perlライブラリィを使って作った、汎用グラフ表示プログラムです。
現在のところ、次のデータに対応しています。
対応ファイルは、TkPlotのインストールディレクトリィ下のdlibに下記の仕様のモジュールを追加することによって拡張できます。
モジュールの例は、TkPlotディレクトリィのdlibディレクトリィ内にあります。
| プログラム |
データタイプ |
ファイル名
(ワイルドカード的表示) |
備考 |
識別子
($FileType, $TargetData) |
モジュール |
| Rietan2000
|
測定XRD図形 |
*.int |
|
Rietan Intensity File |
Crystal::Rietan, dlib::TkRietan
|
| シミュレーションパターン |
*.pat |
|
Rietan Pattern File (IGOR)
Rietan Pattern File (RietPlot)
Rietan Pattern File (gnuplot) |
| フィッティングパターン |
*.pat |
|
Rietan Fitting Pattern File |
| WIEN2k
|
PDOSファイル |
*.dos(1|2|3)?(ev)?(up|dn)?
& *.int |
|
WIEN2k PDOS files |
Crystal::WIEN2k, dlib::TkWIEN2k |
|
誘電関数ファイル |
*.epsilon(up|dn)? |
|
WIEN2k epsilon file |
| バンド構造ファイル |
*.spaghetti(up|dn)?_ene |
|
WIEN2k spaghetti Band file |
| VASP |
OUTCAR出力の
エネルギー収束過程 |
OUTCAR |
|
VASP Output file,
EnergyConvergence |
Crystal::VASP, dlib::TkVASP |
OUTCAR出力の
エネルギー準位収束過程 |
OUTCAR |
|
VASP Output file,
EnergyLevels |
| Virtual NanoLabo |
出力ファイル |
* |
|
ATK 2.0 Output |
Crystal::TranSIESTA,
dlib::TkVNL |
| DVXa |
GPGL出力 |
?07 |
|
DVXa GPGL File |
Crystal::DVXa, dlib::TkDVXa |
| その他 |
CSVファイル
X,Yデータファイル |
* |
|
|
Sci::GeneralDataFile, CSV,
dlib::TkGeneralDataFile |
また、同様のモジュールを作ることにより、TkPlot.plの機能をベースに、別のアプリケーションを作ることもできます。ためしに、TkPlotディレクトリィのDraw.batを実行してみてください。このモジュールはclib\TkDraw.pmです。
| プログラム |
説明 |
備考 |
モジュール |
| Drawアプリケーション |
マウスで線画を描き、PostScriptファイルに保存する |
|
clib::TkDraw |
| リアルタイム測定アプリケーション |
Startボタンを押すと、リアルタイムでデータを追加し、
グラフを再描画し、同時にデータファイルに保存する |
|
clib::TkGPIBTest |
- PerlとPerl/Tkが動く環境であれば、コンピュータ、OS、コンパイラに依存せずに使える。
(PerlはVer5.8で、Windows, Fedora Core4、Redhat
Linux7で動作を確認。)
Microsoft Visual C++は、Windowsでしか使えない。
将来バージョンとの互換性も不安(もっとも、Perlでも将来の互換性は保証できない)。
- エディターのみでプログラムを書き、拡張できる。
速くなったとはいえ、やっぱりコンパイルするのは面倒くさい。
- 開発環境にお金がかからない。
Microsoft Visual C++は高い。
- Graphics User Interface (GUI)を作るのが簡単
Perl/Tkが使える。コード(モジュール)の再利用性も高い。
Visual C++のコードの再利用は面倒くさい。
JavaのGUIはどれを使ったらいいかわからない(3Dなど、高度な処理はJavaの方が良いか?)。
- Perlの強力な文字列処理機能(正規表現など)を使えるので、データファイル処理が楽。
C++などだと、専用の処理関数をたくさん作らないと対応しきれない
- Perlの強力なハッシュ変数(連想配列)を使えるので、データ処理が楽。
C++などでも、自分でクラスを作れば対応できる。
- Perlの膨大なモジュールを使える。
特にシステム管理、ネットワーク処理、データベース処理など
- 必ずコンソールから起動するため、デバッグにprintなどを使えるので、デバッグがしやすい面がある。
Visual
C++でもコンソールを使えないことはないが、手続きが面倒。
- 基本的にPerlはインタープリターなので、動作が遅い。
(正確には、起動時にコンパイルしますが、それでも純粋なコンパイラ型言語に比べると圧倒的に遅い)
- 科学計算用のライブラリィが少ない。
Fortranにはlapackなど、多くのライブラリィがある。C++にもlapackなどは移植されているが、充分ではない。
Perlについては不明だが、あてにはできない
(もっとも、C => Perlの移植は楽そう)。
- プログラムの文法チェックが甘いことと、コーディングが柔軟すぎて、デバッグがしにくい面がある。
C++では文法チェックが割りとしっかりとしているので、バグを見つけやすいことがある。
ただし、反面、柔軟なコーディングはできない(それぞれはトレードオフの関係)。
- 日本語を使いにくい。
- 特殊なパス名(日本語を含む、途中に空白文字があるなど)を扱いにくい(あるいは、扱えない)。
- Perl/Tkが提供していない機能の実装は面倒くさい。
例: 縦書きの文字列を書く、点線などを描く、他のアプリケーションからのドラッグ&ドロップを実装する
対応する予定がない(できない)
- 縦書きの文字がかけない。Perl/Tkの仕様か?
- 点線などが描けない。Perl/Tkの仕様か?
- 動作が遅い。Perl/Tkなので仕方がないので、PCを速くして対応する。
対応予定
- Log, 1/xなどのスケール表示が変。近いうちに対応予定。
- Rietan フィッティング .pat
ファイルが2相以上を含む場合に未対応。近いうちに対応予定。
- 軸の数値が逆転したグラフがかけない。たとえば、X軸の左端を1.0、右端を-1.0にするなど。
- Log, 1/xなどのスケール表示が変なのを直す。
- Rietan フィッティング .pat
ファイルが2相以上を含む場合に対応させる。
- 軸の数値が逆転したグラフを描けるようにする。
- 複数の目盛りを相関させる機構を作る(例:
1000/TとTをそれぞれの目盛りに表示させる)。
- 試みにフィッティングプログラムモジュールを作ってみる。
Perl関係
- perl.exe (ActivePerlで動作確認)
perl.exeのあるディレクトリィはPATH変数で指定されている必要があります。
コマンドコンソール (Windowsでは cmd.exe、Linuxではktermなど)で、
$ perl --version
と入力し、PATHが通っているかどうか確認できます。
- Perl/Tk (ActivePerl
5.8以降では標準装備。Linuxでは、自分でインストールする必要がある。)
$ perl -e "use Tk"
と入力し、エラーメッセージが出なければ、インストールされています
- Math::Matrixモジュール
(とりあえず、なくても動きます。自分でインストールする必要がある)
$ perl -e "use Math::Matrix"
と入力し、エラーメッセージが出なければ、インストールされています
- Math::MatrixRealモジュール
(自分でインストールする必要がある)
$ perl -e "use Math::MatrixReal"
と入力し、エラーメッセージが出なければ、インストールされています
- Jcodeモジュール (自分でインストールする必要がある?)
$ perl -e "use Jcode"
と入力し、エラーメッセージが出なければ、インストールされています
ライブラリィおよびTkPlot
以下のファイルは、\\lucid.msl.titech.ac.jp\pool\CalculationServer\Windows\D\Programs\Perlにあります。
- Object Perlライブラリィ
- TkPlotファイル
TkPlot.pl: TkPlotスクリプト本体
TkPlotApp.pm: TkPlotアプリケーションクラス
TkPlotDoc.pm: TkPlotドキュメントクラス
TkPlotWindow.pm: TkPlotメインウィンドウクラス
RietPlot.bat (Unixでは RietPlot)など: TkPlot.plを Rietan2000用に起動するスクリプト
TkPlot/dlib下のファイル: データ処理モジュール
TkPlot/clib下のファイル: その他のアプリケーション処理モジュール
あった方が良いプログラム
- エディター
初期化ファイル(TkPlot.ini)の編集やモジュールの作成に必要です。
- PostScript Viewer
TkPlotでは、直接プリンターに印刷できません。
グラフなどを印刷するには、GhostScript, Adobe
Illustratorなど、PostScriptを表示・印刷できるソフトウェアが必要です。
この説明でわからない場合、下のTkPlot.plのインストールを読んでください。
- Perl/TkPlotとPerl/libを適当なディレクトリィにコピーする。
- perlをインストールする。
Windows用のActivePerlは、 http://www.activestate.com/Perl.plex?hdr=1
からダウンロードできます。
- Perl/TkPlotディレクトリィに移り、setup.batを実行します。この際、インターネットにつながっている必要があります。
- RietPlot.bat, VAPPlot.bat, WIENPlot.batなどから起動します。
この説明でわからない場合、下のTkPlot.plのインストールを読んでください。
- Perl/TkPlotとPerl/libを適当なディレクトリィにコピーする。
- perlをインストールする(通常はすでにインストールされているはず)。
- Jcode、Math::Matrix、Math::MatrixRealから、
Jcode-2.03.tar.gz
Math-Matrix-0.4.tar.gz
Math-MatrixReal-2.01.tar.gz
をダウンロードし、Perl/TkPlotディレクトリィにコピーします。
- Perl/TkPlotディレクトリィに移り、./installを実行します。
% sh ./install
- シェルスクリプト RietPlot, VAPPlot, WIENPlot
などから起動します。
Draw.batで実行チェック
- Perl/TkPlotディレクトリィに移り、Draw.batを実行してみてください。
これが動かないときは、エラーメッセージを教えてください。
#エラーメッセージは、コマンドラインからDraw.batを実行しないとわかりません
- Draw.batが起動したら、マウスの左ボタンを押したまま動かします。
線が書けるはず。それだけです。
RietPlot.batで実行チェック
- Rietan2000のファイルを表示するには、RietPlot.batを実行してみてください。
- Perl/TkPlot/SampleData/Rietan2000
にサンプルファイルがあるので、
「PATH」ボタンを使って、このディレクトリィ内のファイルを選択します。
- その後、その下のリストボックスから、EuCUSeF.pat
を選びます。
データ量が多いので、Pentium4 3GHzでも、グラフの表示に数秒かかります。
- 表示されたら、グラフ上の線の上にマウスカーソルをおいたり、
グラフ枠内でドラッグしたりしてみてください。
その他:
- GPGLView.bat: DVXaのlvlplt, dospltなどの?07ファイルを表示
- WIENPlot.bat: WIEN2kのspaghetti出力のバンド構造ファイル*.spaghetti_eneを表示
- VASPPlot.bat: VASPの出力ファイルの一部をグラフ表示
- TestPlot.bat: モジュール作製時のテスト用。TkPlot\SampleData\Test.tesだけを読める
TkPlotのインストール:Windows, Unixの場合
\\lucid.msl.titech.ac.jp\pool\CalculationServer\Windows\D\Programs\Perlを適当なディレクトリィ
$TKPERLDIR にコピーします。
以後、
Object Perlライブラリィのディレクトリィは
$TKLIBDIR (= $TKPERLDIR/lib)、
TkPlotのディレクトリィは $TKPLOTDIR (= $TKPERLDIR/TkPlot)
と表します。
PerlとPerlモジュールのインストール:Windowsの場合
- ActiveState.comのPerlサイトから
ActivePerlのStandard Distributionをダウンロードし、インストールします
(Ver5.8以降を推奨)。
- PATHにperl.exeのあるディレクトリィを追加します。
- Math::Matrixモジュールをインストールします。cmd.exeを起動し、
c:> ppm install Math-Matrix
と入力します。
- Math::MatrixRealモジュールをインストールします。cmd.exeを起動し、
c:> ppm install Math-MatrixReal
と入力します。
- Jcodeモジュール
をインストールします。cmd.exeを起動し、
c:> ppm install Jcode
と入力します。
PerlとPerlモジュールのインストール:Unixの場合
- ほとんどの場合、perlは標準でインストールされています。
そうでない場合はインストールし、PATHにperl.exeのあるディレクトリィを追加します。
- Perl/Tk
をダウンロードし、インストールします。rootユーザーになり、
# perl Makefile.PL; make; make install
を実行します。
- Jcode、Math::Matrixと、Math::MatrixRealモジュールをCPANからダウンロードします。
それぞのページは
Jcode
Math::Matrix
Math::MatrixReal
です。
インストールは、それぞれのアーカイブを展開して、展開ディレクトリィに移り、
# perl Makefile.PL; make; make install
を実行します。
- 環境変数TkPerlDirに、上記の$TKPERLDIR (Object PerlライブラリィおよびTkPlotの入っているディレクトリィ)を指定します。
例 (bashの場合):
ライブラリィ: $TkPerlDir/lib (以下、$TKLIBDIR)
TkPlot: $TkPerlDir/TkPlot (以下、$TKPLOTDIR)
にそれぞれのファイルがある場合は、
export TkPerlDir=$HOME/bin
を実行します。
ファイルの変更:Windows, Unixの場合
- $TKPLOTDIR/TkPlot.plの
use lib 'd:/Programs/Perl/lib';
use lib 'd:/Programs/Perl/TkPlot';
use lib "/home/tkamiya/bin/lib";
use lib "/home/tkamiya/bin/TkPlot";
の行を、
use lib '$TKLIBDIR';
use lib '$TKPLOTDIR';
に変更します($TKLIBDIR、$TKPLOTDIRは実際のディレクトリィパスに置き換えます)。
引数
- --style=$STYLE
現在、$STYLEとしては、Rietan、GPGL、VASP、WIEN、Testが使えます。
TkPlot.plを$STYLE用の設定で起動します。
この場合、TkPlot.iniの[$STYLE]セクションから初期化情報を得、ウィンドウタイトルも
"$STYLE Plot"になります。
また、$TKPLOTDIR/dlibディレクトリィ下に Tk$STYLE.pmがあれば、一番最初にロードします。
- --LoadModule=$ModulePath
起動時に読み込むアプリケーション処理モジュールを、$TKPLOTDIRからの相対パスで指定します(拡張子の.pmはつけません)。
例: --LoadModule=clib/TkDraw
- --vertical
ファイルペインとグラフ表示ペインを縦置きに配置します。この場合、ファイル内容を示すテキストボックスなどは省略されます。
このオプションが指定されていない場合、各ペインを横置きに配置し、ファイル内容テキストボックスなどを表示します。
- --ReadPrev
前回のWorking Directoryを読み込んで起動します。
- ファイル名
指定されたファイルを読み込んで起動します。
以上の引数を使うことにより、アプリケーションに特化したウィンドウ構成でTkPlot.plを起動できます。
例: Rietan2000
Unix: perl -w -x -S $TKPLOTDIR/TkPlot.pl --style=Rietan --vertical --ReadPrev $@
Windows: perl -w -x -S $TKPLOTDIR\TkPlot.pl --style=Rietan --vertical --ReadPrev %*
例: VASP
Unix: perl -w -x -S $TKPLOTDIR/TkPlot.pl --style=VASP --ReadPrev $@
Windows: perl -w -x -S $TKPLOTDIR\TkPlot.pl --style=VASP --ReadPrev %*
例: Draw
Unix: perl -w -x -S $TKPLOTDIR/TkPlot.pl --style=Draw --LoadModule=clib/TkDraw --vertical --ReadPrev %*
Windows: perl -w -x -S TkPlot.pl --style=Draw --LoadModule=clib\TkDraw --vertical --ReadPrev %*
注: Perlのオプションは次のとおり
-w 詳細なエラー、警告メッセージを表示する
-x
-S スクリプトの先頭行を、#! までスキップする
- なるべく、上記のアプリケーション用に用意されたスクリプトから起動しましょう。
- 起動したら、「Path」ボタンを押し、データファイルのあるディレクトリィを選択します。
起動時オプションに--ReadPrevが指定されている場合、前回使ったディレクトリィがすでに選択されています。
- ディレクトリィ内にあるファイルのリストが、Files:
ドロップダウンリストボックスに表示されます。
- Files:
ドロップダウンリストボックスでファイルを選択すると、データを読み込んで表示します。
- 表示されたグラフのデータ上にマウスを移動させると、データの説明がバルーンヘルプで表示されます。
Rietan2000のフィッティングデータを表示させると、一番下のグラフ枠にピーク位置がでますが、このピーク位置上にマウスを移動させると、回折指数、Kα1、Kα2の区別、回折角が表示され、XRDプロファイル上にピーク位置を示すグレーラインが表示されます。
- スケール上でダブルクリックすると、表示範囲を変更するダイアログが出ます。
- グラフ枠内を始点にしてマウスをドラッグすると、その範囲を拡大するかどうかのポップアップメニューが出ます。ポップアップメニューからは、表示範囲変更ダイアログを表示させることができます。
- 表示範囲を元に戻すには、メニューの"View"=>"Restore
scale"を選びます。
- 印刷はできませんが、グラフは、"Save as
PostScript"で、PostScript形式で保存できます。
表示にはGhostViewやAdobe Illustratorが使えます。Adobe
Acrobatを使うと、PDFファイルにも変換できます。
注:読み込むファイルの種類は、TkPlot.iniの
FileMask=(.*\.int$|.*\.pat$)
で指定できます。FileMaskの指定は正規表現であることに注意してください。
Windowsでファイルを指定する際のワイルドカードは、
*
(あらゆる文字列) は .*
?
(あらゆる一文字)は .
.
(ピリオド)は \.
で指定します。
また、^はファイル名の先頭、$は最後を意味します。
全体を()で囲って、|で分離すると、複数のファイルマスクを指定できます。
上の例( FileMask=(.*\.int$|.*\.pat$)
)では、ワイルドカードで、*.int と *.pat
を指定していることになります。
下記の仕様のデータ処理用Perlモジュールを、$TKPLOTDIR/dlibに保存することで、TkPlot.plが扱えるデータを拡張することができます。
($TKPLOTDIR/dlib/TkTest.pmを参考のこと)
- データ処理モジュールを$TKPLOTDIR/dlibに作ります。モジュールファイル名は必ず
"Tk" から始まり、モジュール名 + ".pm"
とします。
たとえば、XXDataのデータを処理する場合、モジュール名は"TkXXData"、モジュールファイル名は
"TkXXData.pm"です。
- 下記のうち、【必須ではない】と書かれているものは、基本的な動作をする関数がMyTk::TkDataに定義されているので、特別な処理をする場合以外は必須ではありません。
- 【必須】TkXXData.pmは、MyTk::TkDataの継承クラスとします。
use以下は、必要なモジュールをロードしています(use
stict以外は、$TKLIBDIR以下にあります)。
package TkXXData
use MyTk::TkData;
@ISA = qw(TkData);
use strict;
use Utils;
use MyTk::GraphFrameArray;
use GraphData;
- 【必須】ファイルタイプをチェックするサブルーチン CheckFileType
を追加します。
(CheckFileTypeは静的メンバー関数なので、TkXXData::CheckFileType($filename)
として呼び出す必要があります)
CheckFileType
では、ファイル名$filenameを受け取り、そのファイルがXXData.pmで処理するファイルであれば、そのタイプを文字列で返します。
対応しない場合、undefを返します。
sub CheckFileType
{
my ($path) = @_;
# パス名をドライブ、ディレクトリィ、拡張子つきファイル名、拡張子、最後のディレクトリィ名、
# 拡張子なしのファイル名に分解する
my ($drive, $dir, $filename, $ext, $lastdir, $filebody)
= Deps::SplitFilePath($path);
# ファイル名、拡張子名、ファイルの内容などからファイルタイプを判断し、
# 対応するファイルタイプであれば、ファイルタイプを文字列で返す
if($ext =~ /^\.test$/i) {
return "Test data file";
}
# 対応しないファイルタイプであれば、undefを返す
return undef;
}
- 【必須】次のメンバー関数を定義します。
#コンストラクタ(new)は、アプリケーションオブジェクトとCanvasウィジェットオブジェクトを受け取り、保存します。
sub new
{
my ($module, $app, $canvas) = @_;
my $this = {};
bless $this;
$this->SetApplication($app);
$this->SetCanvas($canvas);
return $this;
}
#デストラクタ
sub DESTROY
{
my $this = shift;
$this->SUPER::DESTROY(@_);
}
- 【必須】データを読み込むサブルーチンReadを追加します。ファイル名$filenameを受け取り、
ファイルタイプを $this->{'FileType'} に保存
ファイル名をSetFileNameで保存
し、データを読み込みます。
対応していないファイルタイプの場合、undefを返します。
sub Read
{
# Readはファイル名$filenameのみを引数として受け取る($thisは自分自身のオブジェクトへのリファレンス)
my ($this, $filename) = @_;
# 初期化(お約束)
$this->ClearAll();
# CheckFileTypeを呼び出し、ファイルタイプをチェックし、$this->{'FileType'}に格納する
my $FileType = $this->SetFileType(CheckFileType($filename));
# 対応するファイルタイプでなければ、undefを返す
return undef unless($FileType eq "Test data file");
#ファイルを読み込み用にオープンする
my $infile = new JFile($filename, "r");
#オープンに失敗したらundefを返す
return undef unless($infile);
#ファイル名を格納
$this->SetFileName($filename);
#1行スキップ
my $line = $infile->ReadLine();
#X,Yデータ用の配列
my (@X, @Y);
for(my $i = 0 ; ; $i++) {
my $line = $infile->ReadLine();
last unless(defined $line);
#print "line: $line\n";
#X,Yデータを読み込み、配列に格納する
my ($x, $y) = ($line =~ /(\S+)\s+(\S+)/);
#print "i=$i x,y=$x, $y\n";
$X[$i] = $x;
$Y[$i] = $y;
}
$infile->Close();
#データはGraphDataArrayオブジェクトのGraphDataオブジェクトとして格納する(お約束)
$this->SetDataArray(new GraphDataArray);
#GraphDataオブジェクトを作る(お約束)
my $Data = new GraphData;
#GraphDataオブジェクトをGraphDataArrayオブジェクトに追加する(お約束)
$this->DataArray()->AddGraphData($Data);
#GraphDataオブジェクトにデータを設定(これらはAssignDataメンバー関数で、グラフ枠のキャプションや
#データ上のバルーンヘルプの表示利用される
$Data->SetTitle("Test Data");
$Data->SetXCaption("X Axis");
$Data->SetYCaption("Y Axis");
$Data->{"x0_Name"} = "X0 Data";
$Data->{"y0_Name"} = "Y0 Data";
#GraphDataオブジェクトにX,Yデータの配列を設定する。
#要素名を"x0"、"y?" (?は0からの連番)にすると、AssignDataメンバー関数で自動的にグラフ表示用の
#X,YデータにAssignされる。
#AssignData関数を自分で作る場合、要素名は自由に決めて構わない
$Data->{"x0"} = \@X;
$Data->{"y0"} = \@Y;
#GraphDataのデータ("x?","y?")の最大値、最小値を計算する
$Data->CalMinMax();
return 1;
}
- 最低限、以上の関数を作るだけで動作します。
- 複雑なデータを読み込む場合、複雑なグラフを作る場合は、次のメンバー関数を作ります。
- 【単純なグラフの場合、必須ではない】
グラフ枠を作るメンバー関数
sub CreateGraphFrame
{
my ($this, $canvas) = @_;
#各パラメータの設定
$this->SetCanvas($canvas) if($canvas);
$canvas = $this->Canvas() unless($canvas);
my $App = $this->App();
my $font = $App->{'GraphFrameFont'};
my @font = split(/,/, $font) if($font);
my $w = $canvas->width();
my $h = $canvas->height();
my $FileType = $this->FileType();
# GraphFrameArrayを作り、$this->{'GraphFrameArray'}に格納する
my $GraphFrameArray = $this->{'GraphFrameArray'}
= new GraphFrameArray($this->mw(), $canvas);
# GraphFrameArrayにCanvasの大きさを設定する
$GraphFrameArray->SetCanvasSize($w, $h);
#グラフ枠を一つ作り、$GraphFrame0に受け取る
$GraphFrameArray->AddGraphFrame();
my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0);
#TkPlot.iniから、0番のグラフ枠のサイズを受け取り、設定する
my $FramePosStr0 = $App->{"GraphFrame0Position"};
$GraphFrame0->SetPositionByStr($FramePosStr0);
# $GraphFrame0のX軸、Y軸目盛りオブジェクトを取得する
my $XScale0 = $GraphFrame0->GetXScale(0);
my $YScale0 = $GraphFrame0->GetYScale(0);
#X,Y軸のキャプションを設定する。
$GraphFrame0->SetXCaption("Energy");
$GraphFrame0->SetYCaption('PDOS');
#$GraphFrame0の表示値範囲を初期化する
$GraphFrame0->SetViewRange(0, 0, 1, 1);
}
- 【単純なグラフの場合、必須ではない】
XXData->Read()で読み込んだデータ配列を、グラフ表示するデータに関連付けます
sub AssignGraphData
{
my ($this) = @_;
#グラフ枠配列を$GraphFrameArrayに取得します。
my $GraphFrameArray = $this->GetGraphFrameArray();
#最初のグラフ枠を$GraphFrame0に取得します。
my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0);
#XXData->Read()で読み込んだデータを、DataArray()から受け取ります
my $pDataArray = $this->DataArray();
#DataArrayから、最初のデータを取得します。
my $Data = $pDataArray->GetGraphData(0);
#$Dataにあるデータ数を取得します
my $nData = $Data->nData();
#$Dataの名前を取得します
my $title = $Data->Title();
#$Dataから、'x0'の要素のデータ配列のリファレンスを取得します
my $pX0 = $Data->GetDataArray('x0');
#$Dataから、'y?'の要素のデータ配列のリファレンスを取得し、X,Y軸データの組として
#$GraphFrame0に追加します
for(my $i = 0 ; $i<$nData ; $i++) {
my $pY = $Data->GetDataArray("y$i");
last unless($pY);
#X,Y軸データの組として$GraphFrame0に追加します
#引数は順番に、
# Xデータ配列のリファレンス、Yデータ配列のリファレンス、
# データ線幅、データ線色、
#
シンボル型、シンボルサイズ、シンボル色、シンボル線幅、シンボル線色、
#
オプション("XAutoSkip"で、画面の解像度に合わせてXデータを間引きする)、
# X軸の名前、Y軸の名前
my $nData = $GraphFrame0->AddGraphData($pX0,
$pY,
1, "auto",
"", 6, "red", 0, "red",
"XAutoSkip",
"Energy", $Data->{"y${i}_Name"});
}
# 必要であれば、$GraphFrame0のX,Yキャプションを再設定する
# (CreateGraphFrameでも設定しているので、やらなくてもいい)
$GraphFrame0->SetXCaption($Data->{"x0_Name"});
$GraphFrame0->SetYCaption('PDOS');
# $GraphFrame0内にあるデータの最大・最小値を計算
$GraphFrame0->CalMinMax();
# $GraphFrame0の表示範囲を設定する
$GraphFrame0->AdjustViewRange(0.05, 0.05, 0.05, 0.05);
#
必要があれば、データ種独自の表示範囲設定サブルーチンを呼び出す
$this->AdjustViewRange();
}
- 【必須ではない】データ種独自の表示範囲を設定するメンバー関数AdjustViewRange
を作成する
以下のサブルーチンでは、何もしません。
たとえば、2つのグラフ枠がある場合に、X軸を同期させたいときなどに使います。
TkPlot.pl内で表示範囲がリセットされたときに呼び出されます。
以下の例では、何もしていません。例としては$TKPLOTDIR/dlib/TkRietan.pmを参考のこと。
sub AdjustViewRange
{
my ($this) = @_;
my $GraphFrameArray = $this->GetGraphFrameArray();
my $pGraphFrame = $GraphFrameArray->GetpGraphFrameArray();
my ($xmin, $xmax) = $pGraphFrame->[0]->GetViewXRange();
}
- 【必須ではない】グラフ描画サブルーチンDrawを作成します。
通常のグラフを表示するのであれば、初期化作業をした後$GraphFrameArray->Draw()を呼び出すだけですが、
複雑なグラフを書く場合、ここに描画ルーチンを追加します。例としては$TKPLOTDIR/dlib/TkDVXa.pmを参考のこと。
sub Draw
{
my ($this, $canvas) = @_;
#各変数の設定
$this->SetCanvas($canvas) if($canvas);
$canvas = $this->Canvas() unless($canvas);
my $mw = $this->mw();
my $GraphFrameArray = $this->GetGraphFrameArray();
my $pGraphFrame = $GraphFrameArray->GetpGraphFrameArray();
my @GraphFrame = @$pGraphFrame;
my $App = $this->App();
my $font = $App->{'GraphFrameFont'};
my @font = split(/,/, $font) if($font);
my $w = $canvas->width();
my $h = $canvas->height();
#フォントの設定
if($font) {
$canvas->SetFont(\@font);
$GraphFrameArray->SetFont(\@font);
}
#現在のCanvasサイズを設定します。
$GraphFrameArray->SetCanvasSize($w, $h);
#Balloonをリセットします(機能していないみたい??)
$this->mw()->Balloon()->detach($canvas);
#$GraphFrameArrayを描画します。
$GraphFrameArray->Draw($canvas);
#必要であれば、$GraphFrameArrayで描画できない描画ルーチンを書きます
}
- 【必須ではない】 メインウィンドウの左(--verticalオプションを指定した場合は上)ペインに、ウィジェットを追加できます。
ウジェットは引数で渡されたフレームウィジェット$Frameの下に作ります。
まず、ウジェットを追加するサブルーチンを追加します。
この例では、Textウィジェットを追加することを考えています。
sub AddWidget
{
my ($this, $Frame) = @_;
return undef unless($Frame);
#メインウィンドウの更新を抑止する
$this->mw()->packPropagate(0);
$this->{'FileContentText'} = $Frame->Scrolled(
'MyText', -height => 3, -scrollbars => 'e',
)->pack(-side => 'top', -expand => 'yes', -fill => 'both');
#メインウィンドウの更新を再開する
$this->mw()->packPropagate(1);
return 1;
}
- 【必須ではない】 次に、別のデータを読み込んだときの初期化のために、作成したウィジェットを削除する関数を追加します。
sub DeleteWidget
{
my ($this, $Frame) = @_;
return undef unless($Frame);
#すでにTextウィジェットを作っていたら、削除する
if($this->{'FileContentText'}) {
$this->{'FileContentText'}->packForget();
$this->{'FileContentText'}->destroy();
undef $this->{'FileContentText'};
}
return 1;
}
- 【必須ではない】 データを読み込んだときに、左ペインのファイル内容セクションリストボックス、テキストボックスへの追加ができます。
セクションリストボックスを使う例としては、$TKPLOTDIR/dlib/VASP.pmを参考のこと。
OUTCARを読み込んだとき、セクションリストボックスに選択肢を追加しています。
セクションリストボックスの選択肢が変更されると、ファイル名と選択された文字列を引数としてRead関数が呼び出されます。
ファイル内容テキストボックスを使う例としても、$TKPLOTDIR/dlib/VASP.pmを参考のこと。
ファイルを読み込んだ後、テキストボックスにデータの数値を表示しています。
sub SetFileInfo
{
my ($this, $ListBox, $TextBox) = @_;
return undef unless($ListBox);
return undef unless($TextBox);
#リストボックス、テキストボックスをクリアー
$ListBox->ClearAll();
$TextBox->ClearText();
my $App = $this->App();
#フォント設定
my @Font = split(/,/, $App->{"FileContentFont"});
$TextBox->configure(-font => \@Font) if(@Font > 0);
#Tk用モジュールオブジェクトを取得
my $DataArray = $App->{'DataArray'};
return undef unless($DataArray);
#TkモジュールオブジェクトからPlotDataオブジェクトを取得
my $pDataArray = $DataArray->DataArray();
return undef unless($pDataArray);
#最初のデータはPlotData->GetGraphData(0)で取得できる
my $Data = $pDataArray->GetGraphData(0);
return undef unless($Data);
my $nData = $Data->nData();
my $title = $Data->Title();
$TextBox->AddText("Title: $title\n");
$TextBox->AddText("$nData\n");
#Xデータ配列の数を取得
my $nX = $Data->nXColumn();
#Yデータ配列の数を取得
my $nY = $Data->nYColumn();
for(my $i = 0 ; $i < $nData ; $i++) {
my $str = "";
for(my $j = 0 ; $j < $nX ; $j++) {
#$j番目のXデータ配列の$i番目のデータを取得
my $nY = $Data->nYColumn();
my $x = $Data->x($j, $i);
next unless(defined $x);
$str = "$str\t$x";
}
for(my $j = 0 ; $j < $nY ; $j++) {
#$j番目のYデータ配列の$i番目のデータを取得
my $y = $Data->y($j, $i);
next unless(defined $y);
$str = "$str\t$y";
}
$TextBox->AddText("$str\n");
}
return 1;
}
データ処理モジュールのメンバー関数からは、たとえば
$this->App()
でアプリケーションオブジェクト、
$this->mw()
でメインウィンドウオブジェクトが取得できるので、Perl/Tkの機能を使えば、あらゆる操作が可能なはずです。
その他
GPIBシミュレートデモ
TkPlotディレクトリィのGPIBTest.batを実行。
「Choose」ボタンで保存するデータファイル名を選び、「Start」ボタンを押すだけ。
ただ、問題があり、Perl/TkのCanvasの内部構造では、作ったウィジェットやアイテム(線や円などの図形)をsigned
integerの連番で管理しているようで、不要なアイテムを削除しても、作ったアイテムが累積3万2千個ていどになると強制終了してしまいます。今のプログラムでは、1点測定するごとにアイテムを全部削除して作り直しているので、800点程度測ってグラフを書き直すと(1/2*n*(n+1)~3万2千で勘定が合う)、強制終了します。
これを直すのは結構大変なので、測定点が多い場合は、10点ごとに書き直すとかの工夫が必要そうです。
GPIBTestのモジュールは、TkPlot/clib/TkGPIBTest.pmです。
必要なことは、
- CreateWidgets()でウィジェットをつくり、ボタンと関数をbindしておく。
- ファイル選択ボタン=>ChooseFile()
- Start,Clearボタン=>StartButton()
- グラフの枠とデータの配列はInitCreateGraphFrameArray()で作っておく。
- Startボタンが押されたら、StartButton()が呼ばれるので、
- この中にデータの取得と、データ配列、グラフの枠のupdateの
- ルーチンを書き、Draw()する。同時に、ファイルにデータを保存する。
だけです。ただ、StartButton()内でループをまわすと、ウィンドウがすべて応答しなくなるので、実際にはafter($itime)を使って、$itimeごとに測定・再描画ルーチンMeasure()を呼び出しています。
インストールスクリプトについて
- lib : 私が作ったPerlモジュールライブラリィ
- TkPlot : TkPlot.pl関係のプログラム、モジュール、サンプルデータ
を適当なディレクトリィの下にコピーしてください。
インストールについては、上記のPerlディレクトリィにある
を参考にしてください。
すでにPerlがインストールされている場合でも、最低、Jcodeモジュールをインストールする必要があります。Windowsの場合、TkPlot/setup.batを起動すると、ppmを使ってJcode,
Math::Matrix, Math::MatrixRealを自動的にインストールし、上記のlib,TkPlotディレクトリィをモジュール検索パスに追加します。ですから、基本的に、setup.batを実行するだけで使えるはずです。その後は、RietPlot.batなどから起動する際に毎回、モジュール検索パスをupdateします。
そのため、次回からは、TkPlotをupdateしたり、ディレクトリィを変えても、setup.batを実行する必要はありません。
linuxの場合は、モジュールを手動でインストールしてください。上記の3つはCPANにあります。その後は、sh
setup
をするようになっていますが、実際に行っているのは、*Plot,*Viewのスクリプトにchmod
+xをしているだけです。RietPlotなどのスクリプトは、TkPlot.plのモジュール検索パスをupdateしたのち、TkPlot.plを実行しています。