ふにゃるんv2

もとは、http://d.hatena.ne.jp/Wacky/

IronPythonの「-i」オプションを使って、グラフコントロールを操作してみる

今回は、自分がIronPythonを使ってコードを書く際のやり方を、グラフコントロールを使って紹介してみます。
(結構長いエントリなので注意して下さいね)

IronPythonを使ってコードを書く場合

IronPythonを使ってコードを書く場合、自分は、「-i」オプションと「-X:TabCompletion」オプションを多用しています。

IronPythonの世界」より(P.53)

  • iオプション:スクリプトを実行したあとに、対話モードに入る(ipyのみ)
  • X:TabCompletionオプション:TABキーによる補完の有効化(ipyのみ)


このオプションを使って、以下の作業をインクリメンタル?に行います。
1.エディタでコードを記述
2.コマンドラインでコードを実行
3.コード実行が進んだら対話モードに入る
4.対話モードで、調べごと(引数を変えて呼び出し、コード追加の試し事)を行う
5.メドがついたら、1.に戻ってコードを追加

1
1 posted by (C)wacky


スクリプト言語使っていて、何が便利かと言うと、4.の作業、途中まで作成したコードに対して、試しごとを繰り返せるという事です。
対話モードに入ると、オブジェクトの構造を調べる dir 関数や、オンラインヘルプ代わりの help 関数を使って、調べごとをしながら作業を進められるのが便利です。


Visual Studioを使うと、確かに強力なデバッグ機能が搭載されていて非常に便利なのですが、ちょっとしたトイプログラムや簡単ユーティリティを作るのに、そこまで大げさなモノは要らないかな。と。
そういう時、IronPythonは便利という訳です。

ZedGraph(グラフ)コントロールの紹介

どんなやり方で、IronPythonを使ってプログラミングを行うか、試してみましょう。
今回は、別用でグラフが欲しいなと思ったので、.NET用のグラフコントロールを使ってみます。


.NETで使えるグラフコントロールって何かな?と思って調べると、ZedGraphがヒットしましたので、これを使ってみます。


このグラフコントロールは 2D用ですが、結構多彩な表現が可能なようですし、手ごろなサンプルがあったのもポイント高いです。
どんな事が可能か?は、プロジェクトのダウンロードページで、幾つかサンプルをダウンロードしてみて下さい。
どんな事が可能か確認するには、「zedgraph interactive demo」が一番判りやすいと思います。

2
2 posted by (C)wacky

チュートリアルサンプルの移植(1):チャートコントロールの表示

まずは、チュートリアルのサンプルを移植してみる事から始めてみましょう。
以下の準備を行います。

  • IronPython 1.1
  • .NET 2.0用のZedGraphのアセンブリモジュール(zedgraph_dll_v510_460.zip から zedgraph.dllを取り出せます)


移植元となるチュートリアルは、以下のサンプルソースを元にします。


まずは、ウィンドウフォームを開いて、そこにグラフコントロールを貼り付けてみましょう。

#!/usr/bin/env python
# coding: cp932
# 
# usage: ipy -X:TabCompletion -i test.py
import clr
import sys
sys.setdefaultencoding("cp932")
#if sys.version.find("IronPython") != 0:
#	sys.path.append(r"C:\tool2\Python24\Lib")

clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
from System.Drawing import Size, Color
from System.Windows.Forms import Form, Application
from System.Threading import Thread
from System.Threading import ThreadStart
from System.Threading import AutoResetEvent
from System.Math import *

clr.AddReferenceToFile("ZedGraph.dll")
from ZedGraph import *

# フォーム上に、チャートコントロールを貼り付ける
f = Form()				# ウィンドウフォームを生成
g = ZedGraphControl()	# チャートコントロールを作成
f.Controls.Add(g)		# チャートコントロールをフォームに組み入れる
f.ShowDialog()			# ウィンドウフォームを表示


上のコードをコマンドプロンプトから以下のように呼び出します。

$ ipy -X:TabCompletion -i test.py

すると、以下のように表示されます。
3
3 posted by (C)wacky


コードのfrom行までは、.NETクラスやZedGraphコントロールの各クラスにアクセスする為の設定です。(C#で言えばusing、VBではImportsみたいなものです)
ウィンドウフォームの作成、チャートコントロールの貼り付け、表示は、最後の4行だけです。
本当、.NETのコントロールの貼り付けは簡単でいいですねぇ。

チュートリアルサンプルの移植(2):ウィンドウのリサイズに合わせる

とりあえず、チャートコントロールを貼り付けましたが、ウィンドウフォームの大きさに合ってません。
という訳で、一旦ウィンドウフォームを閉じます。


すると、コードの最終行を実行した直後の状態で、対話モードに入ります。
作成したオブジェクトと、参照可能なクラス群は、「dir()」とすれば簡単に見られます。

>>> dir()
['Abs', 'Acos', 'AlignH', 'AlignP', 'AlignV', 'Application', 'ArrowObj', 'Asin',
 'Atan', 'Atan2', 'AutoResetEvent', 'Axis', 'AxisLabel', 'AxisType', 'Bar', 'Bar
<略>
tring', 'Truncate', 'ValueHandler', 'X2Axis', 'XAxis', 'XDate', 'Y2Axis', 'Y2Axi
sList', 'YAxis', 'YAxisList', 'ZOrder', 'ZedGraphControl', 'ZedGraphException',
'ZoomState', 'ZoomStateStack', '_', '__builtins__', '__doc__', '__file__', '__na
me__', 'clr', 'f', 'g', 'sys']

最後の方で、ウィンドウフォームの実体である「f」変数と、チャートコントロールの実体である「g」変数が見つかります。


この変数に再度設定して、フォームを再表示してみましょう。
まずは、チャートコントロールのサイズをウィンドウのクライアント領域に合わせてみましょう。
まず、現在のチャートコントロールのサイズをチェックしてみましょう。

>>> g.Size
<System.Drawing.Size object at 0x000000000000002C [{Width=150, Height=138}]>


では、ウィンドウフォームの(クライアント領域の)サイズは?

>>> f.ClientSize
<System.Drawing.Size object at 0x000000000000002F [{Width=292, Height=266}]>


合ってませんね。
という訳で、チャートコントロールのサイズを、ウィンドウフォームのサイズに合わせてみましょう。

>>> g.Size = f.ClientSize
>>> g.Size
<System.Drawing.Size object at 0x000000000000002E [{Width=292, Height=266}]>


はい、設定終わりました。
再度、ウィンドウフォームを表示して確認します。

>>> f.ShowDialog()

4
4 posted by (C)wacky


これで、しめしめと思ったら大間違いで、ウィンドウフォームを引き伸ばすと、下図の有様となります。
5
5 posted by (C)wacky


こういう時、Visual Studioでは、プロパティウィンドウのAnchorプロパティで設定します。
という訳で、設定しましょう。

>>> g.Anchor
<System.Windows.Forms.AnchorStyles object at 0x0000000000000032 [Top, Left]>
>>> g.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | Ancho
rStyles.Bottom
Traceback (most recent call last):
  File , line 0, in <stdin>##58
NameError: name 'AnchorStyles' not defined


名前が無い!と言われます。
確かに、「dir()」した際、AnchorStyles列挙体が参照可能になってません。
from文で参照可能にします。

>>> from System.Windows.Forms import AnchorStyles
>>> g.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | Ancho
rStyles.Bottom
>>> g.Size = f.ClientSize
>>> f.ShowDialog()

今度はうまく行きました。
6
6 posted by (C)wacky


早速、対話モードでの成果をソースコードに反映しましょう。

#!/usr/bin/env python
# coding: cp932
# 
# usage: ipy -X:TabCompletion -i test.py
import clr
import sys
sys.setdefaultencoding("cp932")
#if sys.version.find("IronPython") != 0:
#	sys.path.append(r"C:\tool2\Python24\Lib")

clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
from System.Drawing import Size, Color
from System.Windows.Forms import Form, Application, AnchorStyles
from System.Threading import Thread
from System.Threading import ThreadStart
from System.Threading import AutoResetEvent
from System.Math import *

clr.AddReferenceToFile("ZedGraph.dll")
from ZedGraph import *

# フォーム上に、チャートコントロールを貼り付ける
f = Form()
g = ZedGraphControl()
g.Size = f.ClientSize
g.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom
f.Controls.Add(g)
f.ShowDialog()

チュートリアルサンプルの移植(3):グラフの描画

次は、一気にグラフの描画まで一気に進んでしまいましょう。


チュートリアルに掲載されている元のC#のコードは、以下の通りです。

private void CreateGraph( ZedGraphControl zgc )
{
   GraphPane myPane = zgc.GraphPane;

   // Set the titles and axis labels
   myPane.Title.Text = "My Test Date Graph";
   myPane.XAxis.Title.Text = "X Value";
   myPane.YAxis.Title.Text = "My Y Axis";

   // Make up some data points from the Sine function
   PointPairList list = new PointPairList();
   for ( double x = 0; x < 36; x++ )
   {
      double y = Math.Sin( x * Math.PI / 15.0 );

      list.Add( x, y );
   }

   // Generate a blue curve with circle symbols, and "My Curve 2" in the legend
   LineItem myCurve = myPane.AddCurve( "My Curve", list, Color.Blue,
                     SymbolType.Circle );
   // Fill the area under the curve with a white-red gradient at 45 degrees
   myCurve.Line.Fill = new Fill( Color.White, Color.Red, 45F );
   // Make the symbols opaque by filling them with white
   myCurve.Symbol.Fill = new Fill( Color.White );

   // Fill the axis background with a color gradient
   myPane.Chart.Fill = new Fill( Color.White, Color.LightGoldenrodYellow, 45F );

   // Fill the pane background with a color gradient
   myPane.Fill = new Fill( Color.White, Color.FromArgb(220, 220, 255), 45F );

   // Calculate the Axis Scale Ranges
   zgc.AxisChange();
}


IronPythonへの移植は、ほとんど右にならえです。
気をつける点と言えば、以下ぐらいです。

  • 最後の「;(セミコロン)」を削除
  • ブレース({})の代わりに、インデントを使う
  • 型が無いので、型宣言を排除
  • オブジェクトの生成に、newキーワードは不要


先のC#のコードをIronPythonに移植すると、以下のようになります。
ちょっと変わったVBのコードと思えば、そんなに違和感ないと思います。

#!/usr/bin/env python
# coding: cp932
# 
# usage: ipy -X:TabCompletion -i test.py
import clr
import sys
sys.setdefaultencoding("cp932")
#if sys.version.find("IronPython") != 0:
#	sys.path.append(r"C:\tool2\Python24\Lib")

clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
from System.Drawing import Size, Color
from System.Windows.Forms import Form, Application, AnchorStyles
from System.Threading import Thread
from System.Threading import ThreadStart
from System.Threading import AutoResetEvent
from System.Math import *

clr.AddReferenceToFile("ZedGraph.dll")
from ZedGraph import *

# フォーム上に、チャートコントロールを貼り付ける
f = Form()
g = ZedGraphControl()
g.Size = f.ClientSize
g.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom
## 移植部分:始まり
myPane = g.GraphPane
myPane.Title.Text = "My Test Date Graph"
myPane.XAxis.Title.Text = "X Value"
myPane.YAxis.Title.Text = "My Y Axis"

list = PointPairList()
for x in range(36):
	y = Sin(x * PI / 15.0)
	list.Add(x, y)

myCurve = myPane.AddCurve("My Curve", list, Color.Blue, SymbolType.Circle)
myCurve.Line.Fill = Fill(Color.White, Color.Red, 45)
myCurve.Symbol.Fill = Fill(Color.White)

myPane.Chart.Fill = Fill(Color.White, Color.LightGoldenrodYellow, 45)

myPane.Fill = Fill(Color.White, Color.FromArgb(220, 220, 255), 45)

g.AxisChange();

## 移植部分:終わり
f.Controls.Add(g)
f.ShowDialog()


早速動かしてみましょう。
7
7 posted by (C)wacky

作ったグラフのデータを抜き取ってみる

さて、チュートリアルの移植が出来た訳ですが、一体内部がどうなっているのか、対話モードを使って 少し調べてみましょう。


まず、グラフのデータが、どのように格納されているのか、内部を覗いてみましょう。
ソースコードとヘルプを眺めると、

  • myPaneが、一つのグラフを指す(myPane.Titleでグラフのタイトル設定しているから想定できる)
  • グラフ上の線は、myCurveで行う(myPane.AddCurveでグラフの線を追加しているから想定できる)
  • グラフの各点は、listで管理(forループで作った配列データを格納しているから想定できる)

しているらしい事が読めてきます。


試しに、myCurveとlistを dir関数で、内部を覗きましょう。
色んなメソッド、プロパティが用意されている事が判ります。

>>> dir(myCurve)
['AddPoint', 'BaseAxis', 'Clear', 'Clone', 'Color', 'Comparer', 'Draw', 'DrawLeg
endKey', 'Equals', 'Finalize', 'GetBarWidth', 'GetCoords', 'GetHashCode', 'GetOb
jectData', 'GetRange', 'GetType', 'GetXAxis', 'GetYAxis', 'GetYAxisIndex', 'IsBa
r', 'IsLine', 'IsOverrideOrdinal', 'IsPie', 'IsSelectable', 'IsSelected', 'IsVis
ible', 'IsX2Axis', 'IsY2Axis', 'Label', 'Line', 'Link', 'MakeDynamicType', 'Make
Unique', 'MemberwiseClone', 'NPts', 'Points', 'Reduce', 'ReferenceEquals', 'Remo
vePoint', 'Symbol', 'Tag', 'ToString', 'ValueAxis', 'YAxisIndex', '__class__', '
__doc__', '__getitem__', '__init__', '__module__', '__new__', '__reduce__', '__r
educe_ex__', '__repr__', '_isOverrideOrdinal', '_isSelectable', '_isSelected', '
_isVisible', '_isX2Axis', '_isY2Axis', '_line', '_points', '_symbol', '_yAxisInd
ex', 'schema', 'schema2']
>>> dir(list)
['Add', 'AddRange', 'AsReadOnly', 'BinarySearch', 'Capacity', 'Clear', 'Clone',
'Contains', 'ConvertAll', 'CopyTo', 'Count', 'Enumerator', 'Equals', 'Exists', '
Finalize', 'Find', 'FindAll', 'FindIndex', 'FindLast', 'FindLastIndex', 'ForEach
', 'GetEnumerator', 'GetHashCode', 'GetRange', 'GetType', 'IndexOf', 'IndexOfTag
', 'Insert', 'InsertRange', 'InterpolateX', 'InterpolateY', 'LastIndexOf', 'Line
arRegression', 'MakeDynamicType', 'MemberwiseClone', 'Reduce', 'ReferenceEquals'
, 'Remove', 'RemoveAll', 'RemoveAt', 'RemoveRange', 'Reverse', 'SetX', 'SetY', '
SetZ', 'Sort', 'Sorted', 'SplineInterpolateX', 'SumX', 'SumY', 'ToArray', 'ToStr
ing', 'TrimExcess', 'TrueForAll', '__class__', '__doc__', '__getitem__', '__init
__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__seti
tem__', '_sorted']


とりあえず、list変数を for文で列挙してみましょう。

>>> for v in list:
...   print v
...
( 0, 0 )
( 1, 0.207911690817759 )
( 2, 0.4067366430758 )
( 3, 0.587785252292473 )
<略>
( 34, 0.743144825477394 )
( 35, 0.866025403784438 )

確かに、先のソースコードのforループで作成したデータらしいです。


ところで、dir(list)した時、Addメソッドがあると判りました。
ここに、データを新規に追加できるでしょうか?
ヘルプを呼び出してみましょう。

>>> type(list)
<type 'PointPairList'>
>>> help(PointPairList.Add)
Help on method-descriptor Add

 |  Add(...)
 |          Add(self, PointPair point)
 |          Add(self, PointPairList pointList)
 |          Add(self, Array[float] x, Array[float] y)
 |          Add(self, Array[float] x, Array[float] y, Array[float] z)
 |          Add(self, float x, float y)
 |          Add(self, float x, float y, str tag)
 |          Add(self, float x, float y, float z)
 |          Add(self, float x, float y, float z, str tag)

なるほど、Add(X座標, Y座標)の値を追加する以外にも、色んな呼び出し方法があるようです。


とりあえず、適当なデータを追加してみましょう。

>>> list.Count
36
>>> for x in range(36, 50):
...   list.Add(x, 5)
...
>>> list.Count
50


list変数の数量が増加しました。
早速、フォームを再描画してみます。

>>> g.AxisChange()
>>> f.ShowDialog()

9
9 posted by (C)wacky

なるほど、この機能を使えば、数秒おきに計測データの追加&描画が出来そうですね。

最後に

ちょっと長くなりましたが、IronPythonを使って ちゃかちゃかトイプログラムを作る様子が、少しでも伝わりましたでしょうか?


IronPythonの良い点&便利な点は、「ちょっと動かす」→「調べ事」→「コードに反映」を繰り返し行うのに丁度良い道具が揃っている点にあります。
しかもコンパクトです。
何せ、数ファイルを適当なフォルダにコピーするだけで動くのですから。


自分は、このチャリンコ感覚が結構好きです。
他の皆さんは、どんな使い方をされているのですかね?