MessageBoxを表示する

 「※どうしてもスクリプトがうまく動かない時」で紹介しているErrorCheck.pyを改造してエラー表示をWindowsでお馴染みのMessageBoxで表示するようにしてみたいと思います。

 スタートはErrorCheck.pyから

 ErrorCheck.pyの中身は次の通りです。

# coding: shift-jis

import sys
import traceback

try:
  import badhello ←テストターゲットのスクリプトは"badhello.py"

except:
  traceback.print_exc() ←エラーを表示させている行

raw_input()

 簡単ですね。エラーが起きるかどうかtry〜except文で評価して、エラーが起きたらtrace.backモジュールの関数を使ってエラーを表示しなさいというものです。

  traceback.print_exc() 
    ↓
  ex = traceback.format_exc()
  print ex

print_exc()関数の役割を2つに分けるとformat_exc()関数とprint文になります。
format_exc()関数はエラー表示の文字列を返して、文字列exに保存し、
print文で画面表示させます。このprint文の代わりに、MessageBoxを使います。

 MessageBoxはSystem.Windows.Formsの中にありますので、まずはSystem.Windows.Formsを使えるようにします。

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import * ←いろいろなオブジェクトを抜き出す

次にprint文の箇所をMessageBoxに変更します。

MessageBox.Show(ex,"エラー",MessageBoxButtons.OK,MessageBoxIcon.Error)

MessageBoxから始まる部分は全部System.Windows.Formsから抜き出した物です。
スクリプトErrorCheck2.pyの全体を表示するとこんな感じ

# coding: shift-jis

import sys
import traceback

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import *

try:
  import badhello

except:
  ex = traceback.format_exc()
  MessageBox.Show(ex, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)

繝。繝繧サ繝シ繧クBox縺ァ繧ィ繝ゥ繝シ繧定。ィ遉コ

こんなのが出れば成功です。

 MessageBoxのオーナーを決める

 次のようなスクリプトファイルbadhello2.pyがあったとします。

# coding: shift-jis

print "あなたの名前を教えてください。"
name = raw_input()
print "Hello, "+ name + "!"
print 1.0/0
raw_input()

 これを先程作ったErrorCheck2.pyでimportして試してみると、確かにエラーが出るのですが、MessageBoxが入力しているWindowの後ろに行ってしまいます。普通エラーWindowは一番前に来なければいけません。

 MicroSoftのホームページでは、MessageBoxについて次のように書かれています。

MessageBox.Show メソッド (IWin32Window, String, String, MessageBoxButtons)
指定したオブジェクトの前に、指定したテキスト、キャプション、およびボタンを表示するメッセージ ボックスを表示します。

 ということでIWin32Windowを指定すれば良いことが分かります。ではIWin32Windowとは何かというと

IWin32Window インターフェイス
Win32 HWND ハンドルを公開するためのインターフェイスを提供します。

ということなので、現在のProcessのWindowハンドルを入手して、IWin32Windowのプロパティに代入すると次のようになります。

# coding: shift-jis
import sys
import traceback

from System.Diagnostics import * ←Processクラスを使用するため

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import *

#IWin32Windowを継承したサブクラスを作る
class WinWrapper(IWin32Window):
  def __init__(self, oHandle):
    self.oHand = oHandle

  def get_Handle(self):
    return self.oHand

proc = Process.GetCurrentProcess()
oParentHandle = WinWrapper(proc.MainWindowHandle)

try:
  import badhello2

except:
  ex = traceback.format_exc()
  MessageBox.Show(oParentHandle, ex,
  "エラー",MessageBoxButtons.OK,MessageBoxIcon.Error)

 スクリプトにも書いたように次の今回のポイントはIWin32Windowを継承するところにあります。

class WinWrapper(IWin32Window):
  def __init__(self, oHandle):
    self.oHand = oHandle

  def get_Handle(self):
    return self.oHand

 そもそもインタフェースっているのは何かというと、例えばクラスを人に例えて、あなたのお父さんだとします。あなたのお父さんは、あなたという"子供"のプロパティを持っています。と同時にあなたのお父さんには"親"というプロパティも持っています。これはあなたのお父さんを"家系"という見方で見た場合です。"社会人"という見方で見れば勤めている"会社"があるわけで、"給料"というプロパティと"地位"というプロパティがあります。また"健康診断"という見方であれば"身長","体重","視力","血圧"などの見方があります。
 同じ"あなたのお父さん"と人物であるのですが、いろいろな見方が存在し、それにより当然あるべきプロパティというものが出てきます。この"見方"のことをインターフェースと呼びます。
 つまり、あるクラスがIWin32Windowというインターフェースを持っているということは、IWin32Windowという見方をして良いことを示しています。ここでIWin32Windowという見方ができるということは、Handleというプロパティを持っていることが条件です。
 そこでIWin32Windowを継承するWinWrapperにはHandleプロパティを受け渡す仕組みを作る必要があります。具体的にはget_Handle(self)を定義すれば良いです。

★Handleを受け渡す記述の仕方はプログラム言語によって異なるようです。Microsoftの記述を見る限りでは、Iron PythonはJ#と同じように記載すれば良いようです。さすがJythonの作者と同じって感じですね。

 Python2.6らしく作ってみよう

 上の記述は、IronPython2.0でも対応できます。IronPython2.6以降ではPython2.6と互換をもっているので、Handleプロパティの記述を"@property"や"@property.setter"を使ってやってみました。

# coding: shift-jis

import sys
import traceback

from System.Diagnostics import *

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import *

class WinWrapper(IWin32Window):
  @property ←プロパティという記述が使えます。
  def Handle(self):
    return self._Handle

  @Handle.setter ←プロパティ設定用関数であることを示している。
  def Handle(self, value):
    self._Handle = value

proc = Process.GetCurrentProcess()
oParentHandle = WinWrapper() ←初期値ではHandleを設定しない。
oParentHandle.Handle = proc.MainWindowHandle ←プロパティの代入で設定


try:
  import badhello2

except:
  ex = traceback.format_exc()
  MessageBox.Show(oParentHandle, ex,"エラー",MessageBoxButtons.OK,
  MessageBoxIcon.Error)

上記が一番IronPythonらしい表現だと思います。
ではでは。