IronPython事始め

概要

WindowsプログラミングはVBを使って仕事上必要なアプリを作成した事はありますが,Windowsのプログラミング経験はあまりありません。 引き出しを増やすためにIronPythonを使ったプログラミングを試し、情報をまとめる事にしました。

できるだけ実際に動かすことができるように、各サンプルコード単体で動くように心がけています。

1. プログラミング環境

今回作成した環境は以下の通りです。

2. サンプルの実行方法

テストした環境のフォルダ構造は次のようになっています。

作業フォルダの構造

C:\IronPython26\proj.test\
|-- 01_form.ipy
|-- 02_form_menustrip.ipy
|-- 03_04_form_flowlayout.ipy
|-- 05_form_databinding.csv
|-- 05_form_databinding.ipy
|-- 06_form_tooltip.ipy
|-- 07_form_filedialog.csv
|-- 07_form_filedialog.ipy
|-- 08_csvparser.csv
|-- 08_csvparser.ipy
|-- form_button.ipy
`-- messagebox.ipy

IronPythonの本体であるipy.exeipyw.exeは作業フォルダの上(C:\IronPython26\)にあるため、作成したスクリプト(*.ipyファイル)は次のようにコマンドプロンプトから実行しました。

> cd C:\IronPython26\proj.test
> ..\ipy 01_form.ipy  

ファイルの拡張子をpyではなくipyにしているのは、CPythonとの見分けをつけるためで深い意味はありません。

3. 参考書

書籍「オープンソース×Windowsスクリプティング IronPythonの世界」を手元に置いていますが、 この本で説明されていないところをまとめて行こうと思います。

またCPythonとの連携については マイコミジャーナルの記事が参考になると思います。

MSDN .NET APIのサンプルコードについて

IronPythonで.NETなVBやC#のコードを動かす時には、だいたいそのまま書き換える事ができます。 注意しておくのは次のような事でしょうか。

1. boolean値の扱い

C#のサンプルをみていると、truefalseが出てきますが、IronPythonではVisualBasicと同様にTrueFalseが使えます。

2. イベントハンドラの受け渡し

MSDNの.NET APIリファレンスにあるコード例では引数にイベントハンドラを指定していますが、その部分の引数は省いて、次の行で.Clickプロパティにメソッドへのリファレンスを追加しています。

オリジナルのVisualBasicコード抜粋

  ...
  Dim windowNewMenu As New ToolStripMenuItem("New", Nothing, 
                 New EventHandler(AddressOf windowNewMenu_Click))
  ...

変更後

  ...
  windowNewMenu = ToolStripMenuItem("New", None)
  windowNewMenu.Click += windowNewMenu_onclick
  ...

ただしToolStripMenuItemクラスのコンストラクタは0〜3つの引数を取ります。 無理にNoneを渡さなくともToolStripMenuItem("New")で十分です。

3. null値の取り扱い

いくつかの実現方法があるようですが、まったく同じ値を取り出す方法としてSystem.IntPtr.Zeroを使う方法が以前からあるようです。

先ほどのSystem.Windwos.Forms.ToolStripItemクラスのコード例には、VisualBasicでNothing、C#でnullを渡しています。 IronPythonで試したところNoneで期待通りの動きをしています。

もしAPIで明示的にIntPtr型のnull値を渡す必要がある場合には、System.IntPtr.Zeroが使えるはずですが、評価した場合にIntPtrを想定していない場合にどんな動きをするのかわからないので、まずはNoneを試すのが良さそうです。

説明の流れ

Microsoftの.NET APIリファレンス( http://msdn.microsoft.com/ja-jp/library/system.windows.forms(VS.80).aspx)をみると、アプリケーションを作成する際に使用する要素は次のように分類できるようです。

コントロールが2箇所に出てきますが、前者はButtonBaseは含むがButtonは含まないなど純粋に描画に関するもので、後者はGUIとして操作する対象を主に表わしているようです。 担当が違うために分かれているだけなのでしょうか。あまり気にせず先に進みます。

ここから先はこの分類に従ってサンプルプログラムを作成して行きます。

コントロール、ユーザーコントロール、フォーム

次のようなタイトルバーに"Hello World!"と表示するだけのフォームを作成して、これからいろいろ試すための土台を作ります。。

1. 画面キャプチャ

$alt

2. サンプルコード:空のウィンドウ

01_form.ipy

#
# IronPython Example:  Form
# - 
# @author: YasuhiroABE <yasu@yasundial.org>
# @see: Form Class Example from MSDN API Reference
#
import clr
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
import System
from System.Windows.Forms import Form
from System.Drawing import Size

form = Form()
form.Size = Size(300,200)
form.Text = "Hello World!"

System.Windows.Forms.Application.Run(form)

Sizeオブジェクトを使用してウィンドウサイズを指定してみました。 直接form.Size.Width = 300のように入力をしても変更を反映することはできませんでした。

まだ画面に表示できただけですが、次からいろいろ追加していきます。

メニュー、ツールバー

Formのサンプルにメニューを追加してみます。 メニューを表示して"New"を選択すると、ポップアップメニューを表示します。

1. 画面キャプチャ

$alt $alt

2. サンプルコード:空のウィンドウにメニューをつける

MSDNのC#のサンプルそのままではありませんが、イベントハンドラのdef windowNewMenu_onclick(sender, arge)を登録するところは明確に違うところです。 サンプルではイベントハンドラをToolStripMenuItemのコンストラクタの引数に指定しますが、IronPythonでは.Clickに代入します。

02_form_menustrip.ipy

# coding=Shift_JIS
# IronPython Example:  MenuStrip
# - 
# @author: YasuhiroABE <yasu@yasundial.org>
# @see: MenuStrip Class Example from MSDN API Reference
#
import clr
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
import System
from System.Windows.Forms import Form
from System.Windows.Forms import DockStyle
from System.Windows.Forms import MenuStrip
from System.Windows.Forms import MessageBox
from System.Windows.Forms import ToolStripMenuItem
from System.Drawing import Size

## setup form object
form = Form()
form.Text = "Hello World!"
form.Size = Size(300,200)
form.IsMdiContainer = True

## setup windowMenu object
windowMenu = ToolStripMenuItem("Window")
## setup windowNewMenu
windowNewMenu = ToolStripMenuItem("New", None)
def windowNewMenu_onclick(sender, arge):
  MessageBox.Show("Thank you for clicking on me!", "windowNewMenu_onclick")
  pass
windowNewMenu.Click += windowNewMenu_onclick
windowMenu.DropDownItems.Add(windowNewMenu)

## setup windowMenu object
ms = MenuStrip()
ms.MdiWindowListItem = windowMenu
ms.Items.Add(windowMenu)
ms.Dock = DockStyle.Top

## formオブジェクトへのms変数の追加は最後にします
form.MainMenuStrip = ms
form.Controls.Add(ms)

System.Windows.Forms.Application.Run(form)
## end ##

ここでは"Window"->"New"メニューを追加して、"New"メニューにイベントハンドラをセットしています。 form.IsMdiContaineはサンプルから引き継いだ記述で、コメントアウトしても動くのですが効果について理解していないのでそのままにしてあります。

サンプルからToolStripについての記述は削除してしまいましたが、基本的にはサンプルを引き継ぐ事で可能だと思います。>

3. 日本語でのコメントの入力について

普通に日本語を含めると、次のようなエラーメッセージが表示されます。

File "02_menustrip.ipy", line
SyntaxError: Non-ASCII character '\x83' in file 02_menustrip.ipy
on line 40, but no encoding declared; see http://www.python.org/peps/pep-0263.ht
ml for details

先頭に# coding=Shift_JISを入れることで回避できます。

コントロール (省略)

コントロールにはTextBoxやButtonなどユーザに対する操作を提供するクラスが含まれます。 単独で使うものでもないので、次のパートから他のクラスと組み合せて使うことにします。

レイアウト

FlowLayoutPanelレイアウトとButtonコントロールを使ったプログラムを作成します。

"Add"ボタンを押す事で、FlowLayoutPanelのデフォルトルールに沿ってボタンが配置されます。 ウィンドウの形を変えながら、ボタンを増減させると左から右に向かって(フロー方向LeftToRight)ボタンが増えていく様子がわかると思います。

1. 画面キャプチャ

$alt $alt

2. サンプルコード:Layoutに沿ってボタンを追加する

MSDNのAPIリファレンスにあるサンプルコードを元にしました。 MSDNの例ではフロー方向をTopDownなどに変更する事もできるようになっていますが、そこは省略しています。

03_04_form_flowlayout.ipy

# coding=Shift_JIS
# IronPython Example:  FlowLayoutPanel
# - 
# @author: YasuhiroABE <yasu@yasundial.org>
# @see: FlowLayoutPanel Class Example from MSDN API Reference
#
import clr
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
import System
from System.Windows.Forms import Form
from System.Windows.Forms import FlowLayoutPanel
from System.Windows.Forms import Button
from System.Windows.Forms import MessageBox
from System.Windows.Forms import DockStyle
from System.Drawing import Size

## init Form object
form = Form()
form.Text = "Hello World!"
form.Size = Size(300,200)

## init FlowLayoutPanel object
flowLayout = FlowLayoutPanel()
flowLayout.Size = form.Size
flowLayout.Dock = DockStyle.Fill
flowLayout.AutoScroll = True

## 生成したButtonオブジェクトを記憶するリストです
buttonList = []
## setup add button
addButton = Button()
addButton.Text = "Add"
def addButton_click(sender, arge):
  b = Button()
  b.Text = "No. " + str(len(buttonList))
  buttonList.append(b)
  flowLayout.Controls.Add(b)
  pass
addButton.Click += addButton_click
flowLayout.Controls.Add(addButton)

## setup del button
delButton = Button()
delButton.Text = "Delete"
def delButton_click(sender, arge):
  if(len(buttonList) == 0): return
  b = buttonList.pop()
  flowLayout.Controls.Remove(b)
  pass
delButton.Click += delButton_click
flowLayout.Controls.Add(delButton)

form.Controls.Add(flowLayout)
System.Windows.Forms.Application.Run(form)

3. コントロールの初期サイズについて

FlowLayoutPanelのインスタンスを単純にFormオブジェクトに追加しただけではサイズがおかしくなるので、flowLayout.Size = form.Sizeで初期サイズをウィンドウの大きさと同じに設定しています。 さらにウィンドウのリサイズに合わせて動くためにflowLayout.Dock = DockStyle.Fillを追加しています。

データ、データバインディング

ここではCSVファイルからデータを読み込み、DataGridViewに表示します。 SQLを使った方が本格的で良いのかもしれませんが、冗長ですし、いろいろな参考書にも具体例が載っているので今回はお手軽な方法を取る事にしました。

1. 画面キャプチャ

$alt

2. 入力するCSVファイル:05_form_databinding.csv

今回は次のようなCSVファイル(05_form_databinding.csv)をあらかじめ作成しておきました。

05_form_databinding.csv

name,age,address
yasu,20,AizuWakamatsu
abe,25,Yokohama

3. サンプルコード:DataGridViewにデータを入力する

"05_form_databinding.csv"ファイルを読み込み、DataGridViewに表示するアプリケーションを作成します。

05_form_databinding.ipy

# coding=Shift_JIS
# IronPython Example:  DataGridView
# - 
# @author: YasuhiroABE <yasu@yasundial.org>
# @see: DataGridView Class Example from MSDN API Reference
#
import clr
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
clr.AddReferenceByPartialName("System.Data")
import System
from System.Windows.Forms import Form
from System.Windows.Forms import DataGridView
from System.Data import DataTable
from System.IO import StreamReader
from System.Drawing import Size

form = Form()
form.Size = Size(400,180)
form.Text = "Hello World!"

## 静的にファイル名を指定します
filename = "05_form_databinding.csv"

## setup DataTable object
dt = DataTable()
reader = StreamReader(filename)
cols = []
for c in reader.ReadLine().split(","):
  dt.Columns.Add(c, System.Type.GetType("System.String"))
  cols.append(c)
  
l = reader.ReadLine()
while(l != None):
  row = dt.NewRow()
  counter = 0
  for item in l.split(","):
    row[cols[counter]] = item
    counter += 1
  dt.Rows.Add(row)
  l = reader.ReadLine()
reader.Close()

## setup DataGridView object
datagridview = DataGridView()
datagridview.DataSource = dt
datagridview.Size = form.Size

form.Controls.Add(datagridview)
System.Windows.Forms.Application.Run(form)
## end ##

4. CSVファイルの取り扱いについて

コードをみれば単純にカンマで区切っただけで、いわゆるRFC4180なCSVファイルの処理ができるわけではありません。 残念ながらIronPythonにはCPythonでは使えるcsv.pyが附属しないため、IronPythonで使えるCSVファイルの取り扱いについては、また後で試すつもりです。

コンポーネント

ToolTipが代表に挙げられているので、これを使ってみようと思います。 継承しているクラスがControlクラスから継承していないクラスを分類する方法がないので、どこからどこまでがこのカテゴリに入るのかいまいち不明です。

1. 画面キャプチャ

マウスをボタンの上に載せると、"My button"というToolTipが表示されます。

$alt

2. サンプルコード:ボタン上へのToolTipの表示

06_form_tooltip.ipy

# coding=Shift_JIS
# IronPython Example: ToolTip
# - 
# @author: YasuhiroABE <yasu@yasundial.org>
# @see: ToolTip Class Example from MSDN API Reference
#
import clr
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
import System
from System.Windows.Forms import Form
from System.Windows.Forms import Button
from System.Windows.Forms import ToolTip
from System.Drawing import Size

form = Form()
form.Size = Size(300,200)
form.Text = "Hello World!"

button = Button()
button.Text = "OK"

toolTip = ToolTip()
toolTip.AutoPopDelay = 5000
toolTip.InitialDelay = 1000
toolTip.ReshowDelay = 500
toolTip.ShowAlways = True
toolTip.SetToolTip(button, "My button")

form.Controls.Add(button)
System.Windows.Forms.Application.Run(form)

コモンダイアログボックス

コモンダイアログボックスにはいろいろありますが、ここではOpenFileDialogを使ってみます。 CSVファイルの指定がコードで行なわれていた部分で、任意のファイルを指定できるように作り替えてみました。

1. 画面キャプチャ

実行するとOpenFileDialogが起動し、CSVファイルの入力を促します。

$alt $alt

2. サンプルコード:ダイアログボックによる入力ファイルの指定

今回は次のような新しいCSVファイルを読み込ませる事にしました。

07_form_filedialog.csv

name,age,address,phone
yasu,20,AizuWakamatsu,22-xxxx
abe,25,Yokohama,982-xxxx
hoge,30,somewhere,xxx-xxxx

CSVファイルを変数filenameに指定する部分を書き換えて次のようになりました。 CSVファイルを処理する部分は変更していません。

07_form_filedialog.ipy

# coding=Shift_JIS
# IronPython Example:  OpenFileDialog + DataGridView
# - 
# @author: YasuhiroABE <yasu@yasundial.org>
# @see: OpenFileDialog Class Example from MSDN API Reference
#
import clr
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
clr.AddReferenceByPartialName("System.Data")
import System
from System.Windows.Forms import Form
from System.Windows.Forms import DataGridView
from System.Windows.Forms import DockStyle
from System.Data import DataTable
from System.IO import StreamReader
from System.Drawing import Size
from System.Windows.Forms import OpenFileDialog
from System.Windows.Forms import DialogResult
from System.Windows.Forms import MessageBox

form = Form()
form.Size = Size(400,180)
form.Text = "Hello World!"

## 静的にファイル名を指定します
#filename = "05_form_databinding.csv"
filename = ""
fdialog = OpenFileDialog()
fdialog.InitialDirectory = "C:\\IronPython26"
fdialog.Filter = "CSV file (*.csv)|*.csv|All files (*.*)|*.*"
fdialog.FilterIndex = 1
fdialog.RestoreDirectory = True
if fdialog.ShowDialog() == DialogResult.OK:
  filename = fdialog.FileName
else:
  MessageBox.Show("Please input proper filename.","Error Dialog")
  sys.exit(1)

## setup DataTable object
dt = DataTable()
reader = StreamReader(filename)
cols = []
for c in reader.ReadLine().split(","):
  dt.Columns.Add(c, System.Type.GetType("System.String"))
  cols.append(c)
  
l = reader.ReadLine()
while(l != None):
  row = dt.NewRow()
  counter = 0
  for item in l.split(","):
    row[cols[counter]] = item
    counter += 1
  dt.Rows.Add(row)
  l = reader.ReadLine()
reader.Close()

## setup DataGridView object
datagridview = DataGridView()
datagridview.DataSource = dt
datagridview.Dock = DockStyle.Fill
datagridview.Size = form.Size

form.Controls.Add(datagridview)
System.Windows.Forms.Application.Run(form)
## end ##

ここまでのまとめ

.NETライブラリのドキュメントに従って、GUIプログラミングの概要について触れてきました。 しかし.NET寄りの内容でIronPython特有の事象にはほとんど触れてきませんでした。

次に作成する文書では、解説書でわざわざ取り上げられないようなIronPythonに特化したTipsをまとめていこうと思います。

戻る


Created: 2010-03-11, Last modified: 2010-03-19

2009,2010 © Yasuhiro ABE <yasu@yasundial.org>

Valid XHTML + RDFa RDFa it (RDF/XML)!

正当なCSSです!

Creative Commons License www.yasundial.org by Yasuhiro ABE is licensed under a Creative Commons Attribution 2.1 Japan License. Permissions beyond the scope of this license may be available at http://www.yasundial.org/info/license.html.