雑記帳

ブログを使わずにArrangeNoteで作っている公開雑記帳

Jun 23, 2007 - IronPython 2.0A1でWPFを使う (5)

Windowsのウィンドウアプリケーションを作るとき、ボタンやメニューにアクセスキー(半角英数字に下線が付いているアレ)をきちんと設定してやることはWindowsらしい操作性を持つために重要だと思います。

Visual C++のリソースファイル(*.RC)や.NET FrameworkのWindows Formsの場合、アクセスキーは「開く(&O)」のように&記号で指定していました。 WPFの場合、アクセスキーは「開く(_O)」のように下線記号で指定します。

Windows SDKのドキュメントによると、ボタンなどのコントロールにアクセスキーを割り当てるにはAccessTextクラスを使うと記載されています。

btn = Button( Content=AccessText( Text='クリック(_K)' ) )

しかし、AccessTextクラスのインスタンスを使わなくてもアクセスキーを設定できました。

btn = Button( Content='クリック(_K)' )

下線文字を通常の文字のように表示するときは、下線記号を2つ続けて書きます。

最近は本業でもC#を使う機会が増えてきまして、 IronPythonのコンソールを使えば簡単に.NET Frameworkのクラスライブラリを動かして色々試せるおかげでとても助かっています。 IronPythonが無かったらXmlReaderの使い方を理解するのにもっと時間がかかっていたと思うorz

Jun 20, 2007 - IronPython 2.0A1でWPFを使う (4)

「XAMLプログラミング」の例1.4.5のようにIronPythonでもデータバインディングを行うにはどうすればよいのだろうかと思って作ったスクリプトが↓です。

import wpf
from System.Windows import *
from System.Windows.Controls import *
from System.Windows.Media import *
from System.Windows.Data import *

win = Window( Title='例1.4.5', Width=300, Height=200 )
panel = StackPanel( Width=200, Height=100, Margin=35, Orientation=Orientation.Vertical )
win.Content = panel

myInput = TextBox()
textBlock = TextBlock( Foreground=Brushes.Red )
textBlock.SetBinding( TextBlock.TextProperty, Binding( 'Text', Source=myInput ) )
for x in ( Label( Content='名前を入力してください:' ), myInput, Label( Content='あなたの入力した名前は:' ), textBlock ):
	panel.Children.Add( x )

win.Show()
Application().Run( win )

私の知識不足のせいもあるかと思いますが、XAMLほど簡単にはなりませんでした。 StackPanelに子要素を定義するために panel.Children.Add メソッドを呼び出したり、明示的にSetBindingメソッドを呼び出したりと、綺麗な方法とは言い難いです。

それでも、データバインディングの定義は1行で書けるのですから、難しくはないと思います。 コントロールの数が増えると、記述が面倒なことになりそうですけど...

Jun 14, 2007 - IronPython 2.0A1でWPFを使う (3)

Tutorialフォルダの中にあるavalon.pyのことさえ知らずにIronPythonでWPFを使うための記事を書いていましたorz 正直.NET FrameworkやPythonの知識は実に乏しいのですが、 知らないなりに勉強したことや調べたことを書きつづっていきます。

さて、avalon.pyを読み始めた最初の頃、理解できなかった処理の記述がありました。 以下はavalon.pyからスクリプトをコピーして、interactive()関数の中にその処理を移したものです。

# 以下はwpf.pyに追加して使用
are = AutoResetEvent(False)
def on_startup(*args):
    global dispatcher
    dispatcher = Dispatcher.FromThread(t)
def start():
    try:
        global app
        app = Application()
        app.Startup += on_startup
        are.Set()
        app.Run()
    finally:
        ScriptDomainManager.CurrentManager.SetCommandDispatcher(None)
def dispatchConsoleCommand(consoleCommand):
    global dispatcher
    if consoleCommand:
        dispatcher.Invoke(DispatcherPriority.Normal, consoleCommand)
def interactive():
	global t, are
	t = Thread(ThreadStart(start))
	t.IsBackground = True
	t.ApartmentState = ApartmentState.STA
	t.Start()
	are.WaitOne()
	ScriptDomainManager.CurrentManager.SetCommandDispatcher(dispatchConsoleCommand)

結論から言うと、interactive()関数は、IronPythonのコンソールとは別のスレッドでWPFのウィンドウやコントロールを動かすための処理を実装しています。 次のようにコマンドを入力すると、コンソールが入力待ちになっているにも関わらず、WPFのウィンドウは自由に動かせます。

D:\temp>d:\IronPython-2.0A1\ipy.exe
IronPython console: IronPython 2.0A1 (2.0.10427.02) on .NET 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.
>>> import wpf
>>> wpf.interactive()
>>> from System.Windows import *
>>> win = Window(Width=500, Height=300)
>>> win.Show()
>>> 

まず、モジュールの中でThreadオブジェクトを作り、 ApartmentStateにSTAを設定してスレッドを開始している点が分かりませんでしたが、 これはWPFがSTAでないと動作できないからですね。 ThreadオブジェクトはデフォルトでMTAになっているため、明示的にSTAを指定してやらないといけません。 また、IsBackgroundプロパティをtrueにして、foregroundで動いているコンソールを終了したときにbackgroundのスレッドを停止するようにしています。

そして、ScriptDomainManager.CurrentManager.SetCommandDispatcherメソッドで スクリプトエンジンがコマンドを実行する際に呼びだす処理(ScriptEngine.ExecuteCommand)をdispatchConsoleCommand関数に置き換えていますが、 これはWPFが属しているスレッドの中でWPFオブジェクトにアクセスするコマンドを実行するためにdispatcher.Invoke経由で呼び出すようにしているからなんですね。

これらの処理のおかげで、IronPythonのコンソールからWPFの画面をオンデマンドで操ることが可能になります。 少しコマンドを入力して、WPFの画面を操作して、またコマンドを入力して... これはまるでTcl/Tkのwishを使っているような感じです。面白い!

Jun 11, 2007 - IronPython 2.0A1でWPFを使う (2)

WPFがSingle Thread Affinityであることを知らなかったので、記事を修正しました。([i]の部分)

EfficeonでHDDが20GBしかない古いモバイルノートPCでWPFは無理orz

と思っていたのですが、空き容量500MB強のC:ドライブからD:ドライブにページングファイルを移動して空き容量を増加してから .NET Framework 3.0 Runtimeをインストールして、どうにか↓のサンプルスクリプトを動かすことができました。 ただあまりにも起動時間が長すぎるので、ngen installコマンドはやっておきました。orz

さて、今回のサンプルもボタンを1つだけ表示しますが、タイマーを使ってぐるぐる回り続けます。 これもBOM付きUTF-8形式のテキストファイルとして保存してください。 また、(1)のときのwpf.pyも必要です。

# BOM付きUFT-8で保存
# sample2.py
# [i] このスクリプトはSTAで実行される
import wpf
from System import *
from System.Windows import *
from System.Windows.Controls import *
from System.Windows.Media import *
win = Window(Title=u'IronPythonとWPFの勉強', Width=400, Height=400)
layout = RotateTransform(CenterX=200, CenterY=100, Angle=0)
btn = Button(Content='ここをクリック!!', Width=240, Height=100, LayoutTransform=layout)
def receiver1(sender, e):
	global d
	print( '%d度でクリックされた!!' % (btn.LayoutTransform.Angle) )
	d = -d
btn.Click += receiver1
win.Content = btn
from System.Threading import *
from System.Windows.Threading import *
# [i] 一応、ここではSTAの中でbtnオブジェクトのプロパティにアクセスしている
dispatcher = btn.Dispatcher
def handler(o):
	btn.LayoutTransform.Angle += d
def callback(o):
	# [i] callback関数はMTAスレッドの中で実行される
	# [i] BeginInvokeからInvokeに変更
	dispatcher.Invoke( DispatcherPriority.Normal, TimerCallback(handler), o )
d = 5
tmr = Timer( TimerCallback(callback), None, 1000, 50 )
Application().Run(win)

今回はprintを使っているのでipy.exeで起動します。

D:\temp>d:\IronPython-2.0A1\ipy.exe sample2.py

8年ほど前Javaで仕事をしていたときにJPython(当時)を使って以来のPythonなので、あまり詳しく構文を分かっていないのですが、 クラスのインスタンスを作るとき(コンストラクタを呼び出すとき)に「属性名=値,属性名=値, ...」という書き方ができるんですね。便利です。

Timerのインスタンスは「callback」関数を呼び出しますが、ここから直接btn.LayoutTransform.Angleプロパティを変更しようとすると「ハンドルされていない例外: System.InvalidOperationException: このオブジェクトは別のスレッドに所有されているため、呼び出しスレッドはこのオブジェクトにアクセスできません。」となるため、btn.Dispatcher.Invokeを使ってボタンを作ったスレッドで「handler」関数を呼び出しています。

さて、このサンプルはEfficeonのモバイルノートPCでも動きました。 このPCにはWindows SDK for Vistaが入っていませんから、.NET Framework 3.0 RuntimeとIronPythonさえあれば、WPFを使ったプログラミングが可能になるはずです。

でも、WPF関連クラスのオフラインドキュメントはWindows SDK for Vistaに含まれていますから、 結局これも入れないとプログラムを作ることが出来ないわけですね。 もうHDDの空き容量がorz

やっぱりVAIO typeT(TZ)が欲しいな~

Jun 09, 2007 - IronPython 2.0A1でWPFを使う (1)

IronPythonでWindows Presentation Foundation (WPF)を使ってみようとGoogleで探してみたのですが、「これだ!」というサンプルコードや記事が見つかりません。

とりあえず、IronPython 2.0A1のコンソールから色々触っているうちに、どうにかWPFのウィンドウやコントロールを起動することに成功したので、参考までに記しておきます。 ほんのちょっとしか触っていないので、やり方は今後変わっていくかもしれません。

なお、MacBook Pro + BootCamp 1.2βの上で動作しているWindows Vistaで動作確認を行っています。 Visual Studio 2005やWindows SDKなど、色々な開発用リソースが入っている状態での動作確認です。

まず、これはWPF関連のAssemblyを読み込むためのモジュール「wpf.py」です。 BOM付きUTF-8形式のテキストファイルとして保存してください。

# wpf.py
import clr
# Assemblyの名前に拡張子「.dll」を付けるとエラーになる
clr.AddReferenceByPartialName( 'PresentationFramework' )
clr.AddReferenceByPartialName( 'PresentationCore' )
clr.AddReferenceByPartialName( 'windowsbase' )

clr.AddReferenceByPartialNameメソッドで必要なAssemblyへの参照を追加します。 現時点で私が作ったプログラムに必要なAssemblyは上記の3つだけですが、 用いるWPFクラスに応じてどんどん追加する必要があるでしょう。

次はWPFアプリのサンプルです。 元ネタはSoftBank Creativeから出版されている「XAMLプログラミング」の15ページ 例1.4.1になります。 これもBOM付きUTF-8形式のテキストファイルとして保存してください。

# sample1.py
import wpf
from System import *
from System.Windows import *
from System.Windows.Controls import *

class MyWPF(Window):
	def __init__(self):
		self.Title = 'This is WPF'
		self.Content = Button(Content='クリック!', Width=200.0, Height=25.0)

app = Application()
app.Run(MyWPF())

コマンドプロンプトからIronPython 2.0A1を起動してサンプルプログラムを実行すると、 ボタンが1つあるウィンドウが表示されるはずです。

D:\temp>d:\IronPython-2.0A1\ipy.exe sample1.py

ちなみに、この文章を書いているPCではこのプログラムは動きません。 EfficeonでHDDが20GBしかない古いモバイルノートPCでWPFは無理orz

VAIO typeT(TZ)が欲しいな~

Jun 09, 2007 - C++/CLIで/clrオプションを指定してunmanagedな関数を実装するには

以前C++/CLIのコンパイルオプションに/clrオプションを付けるとマネージコードが生成されることを知った後でunmanagedなコード(ネイティブコード)を出力するやり方も学んだのですが、 #pragmaの書き方を忘れてしまったので忘れないようにメモします。

#pragma unmanaged
void func() {
	// unmanaged code
}
#pragma managed
#pragma managed( push, off )
void func() {
	// unmanaged code
}
#pragma managed( pop )

Jun 02, 2007 - Google Gears

ローカルサーバー、ローカルデータベースとあるから、藤岡さんが提案しているデスクトップCGIのことかと思いましたが、 ブラウザの機能拡張として提供するとありますから、違うみたいですね。

記事の一覧

Copyright © 2007 Koichi Yamamoto