IronPythonを使ってみる

もう少し複雑な例

これまでばらばらに実装してきた機能をまとめて"mylib.py"ファイルを作成しようと思います。

1. 実行例

$alt

$alt

2. プログラムの比較

これまではスクリプトの本体は部品となるFormやButtonの作成と配置だけを行なってきましたが、今回はイベントハンドラも実装しているため、本体全体で一つのクラスを作成しています。 実質的な本体処理は最後の2行で行なわれています。

本体スクリプトだけで以前作成したスクリプトと同程度の分量がありますが、これは動的に配置する"No. X"ボタンをクリックした時に図のようなボタン名入りのMessageBoxを表示するようにしているためです。

オリジナル:03_04_form_flowlayout.ipy

# coding=Shift_JIS
# @author: YasuhiroABE <yasu@yasundial.org>
#
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))
  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)

a07_lib\__init__.py

__all__ = ["MyForm", "MyButton", "MyFlowLayoutPanel"]
from a07_lib.mylib import *

スクリプト本体:a07_flowlayout.ipy

# coding=Shift_JIS
# @author: YasuhiroABE <yasu@yasundial.org>
#
import System
from a07_lib import *

class Main:
  def __init__(self):
    self.form = MyForm("Hello World!")
    self.layout = MyFlowLayoutPanel()
    self.callback_buttonList = []
    self.putButtons()
    pass

  def putButtons(self):
    addButton = MyButton("Add", "add new button")
    addButton.Click += self.callback_addButton
    self.layout.Controls.Add(addButton)

    delButton = MyButton("Del", "delete the last button")
    delButton.Click += self.callback_delButton
    self.layout.Controls.Add(delButton)
    pass

  def callback_addButton(self, sender, arge):
    b = MyButton("No. " + str(len(self.callback_buttonList)), "")
    b.Click += self.callback_button
    self.callback_buttonList.append(b)
    self.layout.Controls.Add(b)
    pass

  def callback_delButton(self, sender, arge):
    if(len(self.callback_buttonList) == 0): return
    b = self.callback_buttonList.pop()
    self.layout.Controls.Remove(b)
    pass

  def callback_button(self, sender, arge):
    msg = "Button: " + sender.Text + " clicked"
    System.Windows.Forms.MessageBox.Show(msg, "Notice")
    pass

  def run(self):
    self.form.Controls.Add(self.layout)
    self.form.run()

## main ##
main = Main()
main.run()

ライブラリ: a07_lib\mylib.py

# encoding=Shift_JIS
# @author: YasuhiroABE <yasu@yasundial.org>
#
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.Windows.Forms import FlowLayoutPanel
from System.Windows.Forms import DockStyle
from System.Windows.Forms import ToolStripMenuItem
from System.Windows.Forms import MenuStrip
from System.Windows.Forms import MessageBox
from System.Drawing import Size
from System.Drawing import Point

__all__ = ['MyForm','MyButton','MyFlowLayoutPanel']

size = Size(300,200)

class MyForm(Form):
  def __init__(self, title):
    self.Size = size
    self.Text = title
    self.IsMdiContainer = True
    pass

  def setupMenu(self):
    self.ms = MenuStrip()
    self.windowNewMenu = ToolStripMenuItem("New", None)
    self.windowNewMenu.Click += self.windowNewMenu_onclick
    self.windowMenu = ToolStripMenuItem("Window")
    self.windowMenu.DropDownItems.Add(self.windowNewMenu)

    self.ms.MdiWindowListItem = self.windowMenu
    self.ms.Items.Add(self.windowMenu)
    self.ms.Dock = DockStyle.Top
    self.MainMenuStrip = self.ms
    self.Controls.Add(self.ms)
    pass

  def windowNewMenu_onclick(self, sender, arge):
    MessageBox.Show("Thank you for clicking on me!", 
                                   "windowNewMenu_onclick")
    pass

  def run(self):
    self.setupMenu()
    System.Windows.Forms.Application.Run(self)
  pass

class MyButton(Button):
  def __init__(self, name, tooltip):
    self.Text = name
    self.toolTip = MyToolTip()
    self.toolTip.SetToolTip(self, tooltip)
    pass
  def setLocation(self, x , y):
    self.Location = Point(x,y)
  pass

class MyToolTip(ToolTip):
  def __init__(self):
    self.AutoPopDelay = 5000
    self.InitialDelay = 1000
    self.ReshowDelay = 500
    self.ShowAlways = True
    pass
  pass

class MyFlowLayoutPanel(FlowLayoutPanel):
  def __init__(self):
    self.Size = size
    self.Dock = DockStyle.Fill
    self.AutoScroll = True
    pass
  pass

## end ##

3. コールバックメソッドのデザイン

今回はスクリプト本体にイベントハンドラ用の(call_back_*)メソッドを作成しました。 メニューのようにMyFlowLayoutPanel側にメソッドを作成することもできましたが、柔軟性を確保するためにイベントハンドラは基本的にライブラリ側には置かないのが良いのだろうと思います。

4. 閑話休題:プログラミングスタイル

Pythonはインデントに依存するため、if文やメソッド定義をくくるためにブレースなどの特別な記号は使いません。 その代りにスクリプトではpass文を使っています。 これ自体に機能はありませんが、インデントが崩れたり、インデントレベルの違うコードをコピーしてきた時でもpassを目印にインデントを調整する事ができるようになります。

エディタが狂ってしまい行頭の空白が全て潰れてしまった様子を想像してみてください。 pass文は非常にコストの低い予防策になるでしょう。 全てのインデントが下がる手前に挿入しなければいけないので、少し難しいですけどね。

5. このセクションのまとめ

勝手に追加したコードのせいもありますが、以前作成したスクリプトとの行数での比較でみると2倍前後の差があります。 これで効率が高まったといえるのかどうか微妙な数字ですが、個人的には余計な機能を追加するぐらいの余裕はでてきたのかなと感じています。

ライブラリを作成する意義に迫るためにも、次回はテストについて考えていきます。


Created: 2010-03-13, 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.