#=============================================== # TkGPIBTest #=============================================== package TkGPIBTest; # TkPlotModuleの継承クラスとする use TkPlotModule; @ISA = qw(TkPlotModule); use strict; use Utils; use MyTk::GraphFrameArray; use GraphData; #============================================================ # コンストラクタ、デストラクタ(お約束) #============================================================ sub new { my ($module, $app, $canvas) = @_; my $this = {}; bless $this; $this->SetApplication($app); return $this; } sub DESTROY { my $this = shift; $this->SUPER::DESTROY(@_); } #============================================================ # 継承クラスで定義しなおす静的メンバー関数 #============================================================ #============================================================ # 継承クラスで定義しなおすグラフ表示関係メンバー関数 #============================================================ #============================================================ # 継承クラスで定義しなおすウィンドウ関係メンバー関数 #============================================================ # 以下、標準の動作をしたい場合は、mw()の標準関数を呼び出す # 何もせずにundefを返すと、それぞれのウジェットを作成しない # ウジェット全体の作成 # mw()->InitCreateWidgets()を呼び出した後、 # ウィンドウタイトルを変更している sub CreateWidgets { my ($this) = @_; my $mw = $this->mw(); my $ret = $mw->InitCreateWidgets(); my $Frame = $mw->{'DataClassFrame'}; # データを保存するファイル名を選択する my $Frame0 = $Frame->MyFrame()->pack(-fill => 'x'); $this->{'FileLabel'} = $Frame0->MyLabel( -text => 'Save File:' )->pack(-side => 'left'); $this->{'FileEntry'} = $Frame0->MyEntry( -width => 50, -takefocus => 1, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $this->{'FileEntry'}->focus(); $this->{'FilePathButton'} = $Frame0->MyButton( -text => '&Choose', -takefocus => 1, -command => [ \&ChooseFile, $this, $this->{'FileEntry'} ], )->pack(-side => 'left'); # 測定インターバルを入力するテキストボックス(Entry) my $Frame1 = $Frame->MyFrame()->pack(-fill => 'x'); $this->{'IntervalLabel'} = $Frame1->MyLabel( -text => 'Interval:' )->pack(-side => 'left', -fill => 'x'); $this->{'IntervalEntry'} = $Frame1->MyEntry( -width => 30, -takefocus => 1, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $this->{'IntervalEntry'}->SetText("500"); $this->{'IntervalUnitLabel'} = $Frame1->MyLabel( -text => 'ms' )->pack(-side => 'left'); # X関数を入力するテキストボックス(Entry) my $Frame3 = $Frame->MyFrame()->pack(-fill => 'x'); $this->{'XFunctionLabel'} = $Frame3->MyLabel( -text => 'X Function:', )->pack(-side => 'left', -fill => 'x'); $this->{'XFunctionEntry'} = $Frame3->MyEntry( -width => 50, -takefocus => 1, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); # XFunctionの変数は$x,$yで与える # ""で文字列を囲むと、$x,$yを変数と見て展開しようとするのでエラーになる # ''を使うと、$ を特殊文字とみなさない $this->{'XFunctionEntry'}->SetText('$x*$x'); # Y関数を入力するテキストボックス(Entry) my $Frame4 = $Frame->MyFrame()->pack(-fill => 'x'); $this->{'YFunctionLabel'} = $Frame4->MyLabel( -text => 'Y Function:', )->pack(-side => 'left', -fill => 'x'); $this->{'YFunctionEntry'} = $Frame4->MyEntry( -width => 50, -takefocus => 1, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $this->{'YFunctionEntry'}->SetText('($x*$x+$y*$y)*exp(-$x)'); # Start(Stop)ボタンとClearボタン my $Frame2 = $Frame->MyFrame()->pack(-fill => 'x'); $this->{'StartButton'} = $Frame2->MyButton( -text => 'Start', -takefocus => 1, -command => [ \&StartButton, $this, "Start" ], )->pack(-side => 'left', -fill => 'x'); $this->{'ClearButton'} = $Frame2->MyButton( -text => 'Clear', -takefocus => 1, -command => [ \&StartButton, $this, "Clear" ], )->pack(-side => 'left', -fill => 'x'); # メインウィンドウのタイトルを設定 $mw->SetTitle("GPIB Simulation demo for TkPlot.pl"); return $ret; } # ファイル選択ペインを作成しない sub CreateSelectFilePane { my ($this) = @_; # return $this->mw()->CreateSelectFilePane(); return undef; } # ファイル内容リストボックス、テキストボックスを作成しない sub CreateFileContentPane { my ($this) = @_; # return $this->mw()->CreateFileContentPane(); return undef; } # 初期のグラフ枠を作成する # 注:InitCreateGraphFrameArray は アプリケーション起動時に一度しか呼ばれない #   CreateGraphFrame は グラフ描画モジュールで、データファイルの読み込みのたびに呼び出される sub InitCreateGraphFrameArray { my ($this, $canvas) = @_; # 引数から$canvasが渡されなかった場合のため、$this->Canvas()を呼び出す $canvas = $this->Canvas($canvas); # 頻繁に使う変数を取得 # TkApplicationオブジェクト my $App = $this->App(); # TkPlotModuleオブジェクト。今回の場合は、TkGPIBTestオブジェクト自身が返る my $pDataArray = $App->TkData(); # Iniファイルの設定からフォントを設定する my $font = $App->{'GraphFrameFont'}; my @font = split(/,/, $font) if($font); # キャンバスの描画幅と高さ my $w = $canvas->width(); my $h = $canvas->height(); # グラフ枠の配列オブジェクトを作り、{'GraphFrameArray'}に格納 my $GraphFrameArray = $this->{'GraphFrameArray'} = new GraphFrameArray($this->mw()); # 念のため、グラフ枠の配列オブジェクトにもキャンバスサイズを設定 $GraphFrameArray->SetCanvasSize($w, $h); # グラフ枠を一つ作り、$GraphFrame0に取得 $GraphFrameArray->AddGraphFrame(); my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0); # Iniファイルの設定からグラフ枠の位置を設定する my $FramePosStr0 = $App->{"GraphFramePosition"}; $GraphFrame0->SetPositionByStr($FramePosStr0); # グラフ枠の目盛りにつける文字列を設定する $GraphFrame0->SetXCaption("X"); $GraphFrame0->SetYCaption('Y'); # グラフ枠の表示範囲を設定する。 $GraphFrame0->SetViewRange(0, 0, 1, 1); # データの配列をつくる my @X; my @Y; # データの配列を$GraphFrame0に結びつける # AddGraphDataには、データ配列のリファレンスを渡す #  0,'black'は、幅 0、色が'black'の線を描く => 線を描かない #  "dot", 1, "red", 0, "red",は、"red"色のdotを描画する #  "XAutoSkip"は、データ量が多いときに、画面の解像度に合わせて描画データをスキップする #  最後の"x","y"は、X,Yデータにつける名前。ほとんど意味はない $GraphFrame0->AddGraphData(\@X, \@Y, 0, 'black', "dot", 1, "red", 0, "red", "XAutoSkip", "x", "y"); return undef; } #======================================================================= # bindされた応答関数 #======================================================================= #ファイル選択ボタンを押したときに呼ばれる sub ChooseFile { my ($this, $widget) = @_; my $fmask = '*.*'; my $defstr = ''; my $message = 'Choose file'; my $dir = ''; my $filepath = Dialog::OpenFileDialog($this->mw(), 'save', $fmask, $defstr, $message, $dir); if($filepath) { if($widget) { $widget->SetText($filepath); } } return $filepath; } #Start, Clearボタンが押されたときに呼ばれる sub StartButton { my ($this, $action) = @_; #$actionに、どのボタンが押されたかの情報が入る if($action =~ /start/i) { my $button = $this->{'StartButton'}; my $text = $button->GetText(); # Startボタンが押されたとき、Startボタンの状態が"Start"か"Stop"に # なっているかによって処理を変える if($text =~ /start/i) { print "Press for Start\n"; #ファイル名を取得し、すでに存在するかどうかをチェック my $FileName = $this->{'FileEntry'}->GetText(); #ファイル名が選択されていないときは、メッセージを出して処理を中止 if(!$FileName) { $this->mw()->messageBox( -icon => "info", # "info" "warning" "question" -type => "Ok", # "YesNo" "YesNoCancel" "OkCancel" "RetryCancel" -title => 'Error on FileName', -message => "FileName is not selected.\nChoose it."); return undef; } #ファイルが存在するときは、上書きの確認 if(-e $FileName) { my $ret = $this->mw()->messageBox( -icon => "question", # "info" "warning" "question" -type => "YesNo", # "YesNo" "YesNoCancel" "OkCancel" "RetryCancel" -title => 'Confirm for overwrite', -message => "$FileName exists.\nOverwrite?"); return unless($ret eq 'Yes'); } #ファイルを書き込みようにオープンする $this->{'OutFileClass'} = new JFile($FileName, "w"); if(!$this->{'OutFileClass'}) { $this->mw()->messageBox( -icon => "info", # "info" "warning" "question" -type => "Ok", # "YesNo" "YesNoCancel" "OkCancel" "RetryCancel" -title => 'Error', -message => "Can not write to $FileName."); return undef; } $button->SetText("Stop"); $this->{'DoStop'} = 0; $this->{'Count'} = 0; # データの読み込みをループ処理で行うと、ボタンの更新などができないので、 # タイマー(mw()->after())を使って$itime ms後にMeasure()関数を呼び出す my $itime = $this->{'IntervalEntry'}->GetText(); $this->mw()->after($itime, [\&Measure, $this]); } elsif($text =~ /stop/i) { print "Press for Stop\n"; # ファイルをオープンしていたら閉じて、変数をundefする if($this->{'OutFileClass'}) { $this->{'OutFileClass'}->Close(); undef $this->{'OutFileClass'}; } $button->SetText("Start"); $this->{'DoStop'} = 1; } } elsif($action =~ /clear/i) { print "Press for Clear\n"; # グラフ配列オブジェクトから、データ$dataオブジェクトを取り出す。 my $GraphFrameArray = $this->{'GraphFrameArray'}; my $GraphFrame = $GraphFrameArray->GetGraphFrame(0); my $data = $GraphFrame->GetPlotData(0); # $dataに登録されたデータを削除する。 # $data内のデータの最小・最大値は自動的に更新される $data->ClearAll(); # $GraphFrame内のデータの最小・最大値も更新する $GraphFrame->CalMinMax(); # 再描画 $this->Draw(); } } sub Measure { my ($this) = @_; #"Stop"ボタンがおされると、'DoStop'が1になる #このとき、何もせずに帰る return if($this->{'DoStop'}); my $GraphFrameArray = $this->{'GraphFrameArray'}; my $GraphFrame = $GraphFrameArray->GetGraphFrame(0); my $data = $GraphFrame->GetPlotData(0); #Start: GPIBシミュレーションルーチン:GPIBでデータを$x,$yに受け取る my $x = rand(); my $y = rand(); #End: GPIBシミュレーションルーチン #カウンター変数を取得 my $c = $this->{'Count'}; #データをコンソールに表示 $x = Utils::Round($x, 4); #有効数字を4桁に切り詰める $y = Utils::Round($y, 4); #有効数字を4桁に切り詰める print "$c: ($x,$y)\n"; #データをファイルに書き込む if($this->{'OutFileClass'}) { $this->{'OutFileClass'}->Write("$c\t$x\t$y\n"); } #データをコンソールに表示(普通は次の1行だけ) # $data->AddData($x, $y); #Start:ちょっと遊び:$x,$yからX,YFunctionテキストボックスの関数を評価し、 #       プロットする my $xfunc = $this->{'XFunctionEntry'}->GetText(); my $yfunc = $this->{'YFunctionEntry'}->GetText(); # $funcの変数は$x,$yで与えられている # $xは0~1なので、0~10の値に変換する $x = $x * 10; $y = $y * 10; my $x1 = eval($xfunc); my $y1 = eval($yfunc); $data->AddData($x1, $y1); #End:ちょっと遊び #データと$GraphFrameの最大・最小値を更新 $data->CalMinMax(); $GraphFrame->CalMinMax(); #データの範囲にあわせて$GraphFrameの描画範囲を変えたい場合、 #AdjustViewRangeを呼ぶ。とりあえず、0.05には意味はない $GraphFrame->AdjustViewRange(0.05, 0.05, 0.05, 0.05); #ここで呼び出す必要はないが、表示範囲を固定する場合、 #SetViewRangeを呼ぶ。 # $GraphFrame->SetViewRange(0, 0, 1, 1); #再描画 $this->Draw(); #カウンター変数を更新 $this->{'Count'} += 1; #このままで終わると、一回しかMeasure関数が呼び出されないので、 #繰り返し測定をするため、再度after()で$itime ms後にMeasure関数を呼び出す。 my $itime = $this->{'IntervalEntry'}->GetText(); $this->mw()->after($itime, [\&Measure, $this]); } 1;