27 件 見つかりました。
WindowsプログラミングにはIronPythonが便利そうだなと思っていたが、同じ開発元からIronPython StudioなるIDEまでリリースされていたことを知り、導入に踏み切った。その際のメモ。
pre-requistiesを見てみるとVisual Studio 2008 Shell Isolated Mode Redistributable packageがあったのでダウンロードしインストール。場所としてC:\VS 2008 Shell Redist以下を指定し、しばらく待ったら終わった。だがその後に C:\VS 2008 Shell Redist\Isolated Mode\vs_shell_isolated.enu.exe を実行せねばインストールが真に終わらない(*)とのことで、実行した。.NET Framework 3.5のインストールの後にVS2008Shellなんちゃらのインストールが始まった。
(*) pre-requistiesのページに以下の記述があった。
After you run the Install for the MS VS 2008 Shell Isolated Mode Redistributable, you must then go to the folder (in my case: C:\VS 2008 Shell Redist\Isolated Mode) and click on: "vsshellisolated_enu.exe" to actually install the redistributable runtime.
のちに IronPythonStudioIsolated.zip をダウンロードしインストールした。スタートメニューにIronPython Studioが登場したので実行したが、ちゃんと起動できた。
Windows Applicationの新規プロジェクトを立ち上げ、Formに適当に貼り付けて実行する(この操作はVS 2003と同様)と、ちゃんと実行できた。すごい。コントロールをダブルクリックするとちゃんとpythonソースのイベントハンドラの所に飛ぶ。これはもっとすごい。
作成したプロジェクトのbinディレクトリを見ると実行ファイルができていた。同じくIronMath.dll、IronPython.dllもあり、これら3つを別のPCに持っていって、プログラムを実行できるのを確認した。うまくいきすぎて拍子抜けした。プロジェクトのプロパティで"Target Platform"が選べて、ここの値の.NETが入っているPCなら動くのだろうと理解。
できれば実行ファイルでなくスクリプトのままで動かしたいところだが、ひと手間かかるようだ。Visual Studio 2005 for IronPythonで作ったコードを ipy.exe 単体で動かせるようにするには? - ふにゃるん
起動はとても遅い。FormにボタンとNumericUpDownを配置し、メッセージボックスを出すだけのReleaseビルド実行ファイルの起動にかかる時間は、Athlon 64 X2 3800+ 2GB RAMマシンで2秒ほど、Celeron 2.5GHz 2.5GB RAMマシンに至っては15秒前後かかる。起動後の動作に特に不自然な印象は無い。
だけどまあ素早い開発ができそうなのは良い。PyWin32も結構便利な環境だが、IDEがあるあたりが良い。個人的には、NumericUpDownが簡単に使えるのが動機の大きい割合を占める。
参考:IronPythonプログラミングの始め方 − @IT
[2008-08-16]の続き。ちょっと実際に試してわかったことをメモする。
結論:ResEditを使う際、win32rcparser.pyを変更した方が良い。変更箇所はこのメモ後半に示す。
ResEditで配置する場合、いくつか「地雷」なコントロールがある。
(1) チェックボックス、ラジオボタン
チェックボックスやラジオボタンを配置したリソーススクリプトを使うと、pythonスクリプト実行時ダイアログ自体も表示されなかった。
ResEditでチェックボックス(ラジオボタンも同様)を配置すると以下のようなリソーススクリプトとなる。win32rcparser.pyはこれをちゃんと解釈してくれないようだ。
AUTOCHECKBOX "Check1", IDC_CHECKBOX1, 10, 43, 40, 10C:\Python25\Lib\site-packages\win32\test\win32rcparser\test.rc を見ると以下の記述は正しく解釈されているようだった。
CONTROL "Check1",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,52,68,12両者はリソーススクリプト的には等価だが、前者の記述はwin32rcparser.pyは想定してなかった、といったところか。
CONTROL "", IDC_SPIN1, UPDOWN_CLASS, UDS_ARROWKEYS, 10, 119, 9, 14
CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_ARROWKEYS,7,71,14,22というわけで、ResEditのウィンドウクラスの表現(UPDOWN_CLASS)がwin32rcparserに解釈できなかった、という感じ。ピクチャコントロールも同様に躓いた模様。
--- C:/Python25/Lib/site-packages/win32/lib/win32rcparser_orig.py Thu Jul 29 00:25:22 2004 +++ C:/temp/e/f/win32rcparser.py Sun Aug 17 14:25:20 2008 @@ -199,10 +199,12 @@ def parseH(self, file): lex = shlex.shlex(file) lex.commenters = "//" token = " " + lex.whitespace += '()' + lex.wordchars += '-' while token is not None: token = lex.get_token() if token == "" or token is None: token = None else: @@ -456,10 +458,17 @@ self.getCommaToken() if control.controlType == "CONTROL": self.getToken() control.subType = self.token[1:-1] + + # ad hoc handling for ResEdit's common-controls by H.Itoh + # ex. 'UPDOWN_CLASS' => 'UPDOWN_CLASSA' => "msctls_updown32" (see commctrl l.731) etc. + try: control.subType = getattr(commctrl, self.token+'A') + except AttributeError: pass + if self.token=='WC_STATIC': control.subType = 'Static' + thisDefaultStyle = defaultControlStyle | \ _addDefaults.get(control.subType, 0) # Styles self.getCommaToken() self.getToken() @@ -483,10 +492,21 @@ self.getToken() control.style, control.styles = self.styles([], thisDefaultStyle) if self.token==",": self.getToken() control.styleEx, control.stylesEx = self.styles([], defaultControlStyleEx) + + # ad hoc handling for ResEdit by H.Itoh + f = lambda s: control.controlType.endswith(s) + if f('CHECKBOX') | f('RADIOBUTTON'): + for s in ['BS_'+control.controlType, 'WS_TABSTOP']: + control.styles.append(s) + control.style |= getattr(win32con, s) + control.controlType = 'CONTROL' + control.subType = 'Button' + control.style |= defaultControlStyle + #print control.toString() dlg.controls.append(control) def ParseStreams(rc_file, h_file): rcp = RCParser()
from pywin.mfc import dialog import win32rcparser class Mydialog(dialog.Dialog): def __init__(self, ddef): dialog.Dialog.__init__(self, ddef) def OnInitDialog(self): return dialog.Dialog.OnInitDialog(self) if __name__=='__main__': rc = win32rcparser.Parse('my.rc') ddef = rc.dialogs['IDD_DIALOG1'] w = Mydialog(ddef) w.DoModal()
[2008-06-09-2]で、pythonによるwindowsプログラミングにおいてリソース(スクリプト/エディタ)が使えそうである旨メモした。
その時はリソーススクリプトをパースする在野のpythonスクリプトを見つけ出したと思っていたが、実は手元のpywin32(-211)に同様のものが含まれていた。
本体は C:\Python25\Lib\site-packages\win32\lib\win32rcparser.py にあり、C:\Python25\Lib\site-packages\win32\Demos\win32rcparser_demo.py でその動作っぷりが確かめられる。これで、個人的には、pythonによるwindowsプログラミングの敷居がぐっと下がったのではと感じる。
ただこれをResEditで利用する場合、そのままでは動かない。以下を参照。
- ANSI版のResEditを使うべき。Unicode版はUTF16のリソーススクリプトを吐くが、win32rcparser.py はこれをうまく読んでくれなかった。本来はwin32rcparser.pyに修正を施すべきだが、これは面倒。作成後にリソーススクリプトをANSIに変換しても良いけれど、そんなことするくらいなら最初からANSI版を使うのが良い。
- ResEditの吐くヘッダ中の
#define IDC_STATIC (-1)が、そのままでは通らない。これは、カッコと負号がちゃんとパースされていないため。この行を編集(消すなり、カッコ無しの正数にしたり)するのがひとつの対策。即ちアプリケーション本体の実行時に動的にこの行をフィルタしてwin32rcparserに渡す、など。もうひとつの対策は、win32rcparser.pyに修正を施すこと。場当たり的だが以下のような簡単に修正できるので、こちらの方が良さそう。
@@ -199,10 +199,12 @@ def parseH(self, file): lex = shlex.shlex(file) lex.commenters = "//" token = " " + lex.whitespace += '()' + lex.wordchars += '-' while token is not None: token = lex.get_token() if token == "" or token is None: token = None else:
SendInputでクリックイベントを送るのが確実だけど、位置合わせが面倒。
ボタンのウィンドウハンドルを取得すればどうにでもなると思ったが、あまり単純な話ではないようだ。
http://m--takahashi.com/bbs/pastlog/06400/06309.html
なんで、押すこと自体は諦め、代わりにSendInputからキーイベントを送ってメニューから実行したと見做させるのが良いように思う。
下記は、windowsのAPIのSendInputをpythonから呼ぶ例。
Adobe Readerに右矢印入力を10発送りこむ(開いてるPDFを10ページ進める)。エラー処理は省略。
この手のことはAutoHotKeyが専門だけど、pythonでできるにこしたことはない。
AppendKeyInput で win32con.KEYEVENTF_KEYUP を入れないと、同じキーの連続入力が出来ない。
http://mail.python.org/pipermail/python-win32/2005-April/003131.html を参照。
from ctypes import * import win32ui, win32con PUL = POINTER(c_ulong) class KeyBdInput(Structure): _fields_ = [("wVk", c_ushort), ("wScan", c_ushort), ("dwFlags", c_ulong), ("time", c_ulong), ("dwExtraInfo", PUL)] class HardwareInput(Structure): _fields_ = [("uMsg", c_ulong), ("wParamL", c_short), ("wParamH", c_ushort)] class MouseInput(Structure): _fields_ = [("dx", c_long), ("dy", c_long), ("mouseData", c_ulong), ("dwFlags", c_ulong), ("time",c_ulong), ("dwExtraInfo", PUL)] class Input_I(Union): _fields_ = [("ki", KeyBdInput), ("mi", MouseInput), ("hi", HardwareInput)] class Input(Structure): _fields_ = [("type", c_ulong), ("ii", Input_I)] def AppendKeyInput(vk): ret = [] extra = c_ulong(0) ii_ = Input_I() scan = windll.user32.MapVirtualKeyA(vk, 0) ii_.ki = KeyBdInput(vk, scan, 0, 0, pointer(extra) ) ret.append( ( win32con.INPUT_KEYBOARD, ii_ ) ) ii2_ = Input_I() scan = windll.user32.MapVirtualKeyA(vk, 0) ii2_.ki = KeyBdInput(vk, scan, win32con.KEYEVENTF_KEYUP, 0, pointer(extra) ) ret.append( ( win32con.INPUT_KEYBOARD, ii2_ ) ) return ret def ExecSendInput(inputs): num = len(inputs) FInputs = Input * num x = FInputs(*inputs) windll.user32.SendInput(num, pointer(x), sizeof(x[0])) if __name__ == '__main__': inputs = [] for i in range(10): inputs += AppendKeyInput(win32con.VK_RIGHT) acrobat = win32ui.FindWindowEx(None, None, 'AdobeAcrobat', None) acrobat.SetForegroundWindow() ExecSendInput(inputs)
[2008-06-07]でPyWinのMFC(GUI)プログラミングが有望っぽい事をメモした。
さてGUIプログラミングではコントロールの配置がキモである。ソース内にベタ書きするのは、ファイルが1つで済むので好きだけど、ある程度の規模に膨れるとそうも言ってられなくなる。Visual Studioによるwin32/MFC開発ではリソースエディタでもってコントロールの配置を行い、それをリソーススクリプトに保存するが、これが中々具合が良い。これをPyWin32にも活用できないものだろうか?
ResEditなるものがあるようだ。ちょっと導入してみたけど中々良い。リソースの作成はこれで大丈夫そう。(Visual Studioが使えない時という意味で)
では如何にそれをpythonに喰わせるか?という話になる。[Spambayes-checkins] spambayes/Outlook2000/dialogs/resources rcparser.py, NONE, 1.1.2.1 にそれっぽいコードがある。今度試してみよう。
pywin32のmfcラッパを試している。pythonで結構簡単にWindowsのダイアログが作れて、良さそう。
とりあえず手元で試して動いたコードをメモ。
とにかくダイアログを出す、という例が以下。拡張子をpywにして保存しダブルクリックすればOK。
from pywin.mfc import dialog import win32con class Mydialog(dialog.Dialog): dlgstyle = (win32con.WS_DLGFRAME | win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU ) btnstyle = (win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP | win32con.WS_CHILD | win32con.WS_VISIBLE) dlgButton = 0x0080 tmpl = [ ["my dialog", (0, 0, 200, 100), dlgstyle], # dialog [dlgButton, "my OK", win32con.IDOK, (5, 55, 190, 40), btnstyle], # button ] def __init__(self): dialog.Dialog.__init__(self, self.tmpl) d = Mydialog() d.DoModal()
from pywin.mfc import dialog import win32con class Mydialog(dialog.Dialog): dlgstyle = (win32con.WS_MINIMIZEBOX | win32con.WS_DLGFRAME | win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT ) btnstyle = (win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP | win32con.WS_CHILD | win32con.WS_VISIBLE) IDC_MYBUTTON = 1025 dlgButton = 0x0080 tmpl = [ ["my dialog", (0, 0, 200, 100), dlgstyle], # dialog [dlgButton, "my OK", win32con.IDOK, (5, 5, 190, 40), btnstyle], # button [dlgButton, "my button", IDC_MYBUTTON, (5, 55, 190, 40), btnstyle], # button ] def __init__(self): dialog.Dialog.__init__(self, self.tmpl) def OnInitDialog(self): rc = dialog.Dialog.OnInitDialog(self) self.HookCommand(self.OnMyButton, self.IDC_MYBUTTON) self.btn = self.GetDlgItem(self.IDC_MYBUTTON) return rc def OnMyButton(self, id, cmd): if cmd==win32con.BN_CLICKED: ret = self.MessageBox('hello, my button', 'my caption', win32con.MB_YESNO) if ret==win32con.IDYES: cap = 'yes' else: cap = 'no' self.btn.SetWindowText(cap) def OnOK(self): rc = dialog.Dialog.OnOK(self) self.MessageBox('OnOK!', 'farewell') return rc def OnCancel(self): rc = dialog.Dialog.OnCancel(self) self.MessageBox('OnCancel!', 'farewell') return rc d = Mydialog() d.DoModal()
self.btn = win32ui.CreateButton() self.btn.CreateWindow('my button', self.btnstyle, (10, 75, 340, 140), self, self.IDC_MYBUTTON)
from pywin.mfc import dialog import win32con class Mydialog(dialog.Dialog): dlgstyle = (win32con.WS_MINIMIZEBOX | win32con.WS_DLGFRAME | win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT ) btnstyle = (win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP | win32con.WS_CHILD | win32con.WS_VISIBLE) staticstyle = (win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.SS_LEFT) IDC_MYBUTTON = 1025 IDC_MYSTATIC = 1026 dlgButton = 0x0080 dlgStatic = 0x0082 tmpl = [ ["my dialog", (0, 0, 200, 130), dlgstyle, None, (14,'Arial')], # dialog [dlgButton, "my OK", win32con.IDOK, (5, 5, 190, 40), btnstyle], # button [dlgButton, "my button", IDC_MYBUTTON, (5, 55, 190, 40), btnstyle], # button [dlgStatic, "my static", IDC_MYSTATIC, (5,105, 190, 20), staticstyle], # static ] timerid = 1 def __init__(self): dialog.Dialog.__init__(self, self.tmpl) def OnInitDialog(self): rc = dialog.Dialog.OnInitDialog(self) self.HookCommand(self.OnMyButton, self.IDC_MYBUTTON) self.static = self.GetDlgItem(self.IDC_MYSTATIC) self.timerflag = False self.timercount = 0 return rc def OnMyButton(self, id, cmd): if cmd==win32con.BN_CLICKED: if self.timerflag: self.KillTimer(self.timerid) else: self.SetTimer(self.timerid, 500) # 0.5s self.timerflag = not self.timerflag def OnTimer(self, nIDEvent): self.timercount += 1 self.static.SetWindowText('timer! %d' % self.timercount) def OnOK(self): rc = dialog.Dialog.OnOK(self) self.MessageBox('OnOK!', 'farewell') return rc def OnCancel(self): rc = dialog.Dialog.OnCancel(self) self.MessageBox('OnCancel!', 'farewell') return rc d = Mydialog() d.DoModal()
pythonでCOMを使うためPython for Windows extensions (see also: Mark Hammond's Free Stuff)を導入した。他の機能にも触れてみようとしている。mfcまわりもラップされているのでGUIアプリケーションも書けるようだ。windowsに特化するなら、MFCへの慣れを勘案すれば、wxPythonよりこっちを使う方がラクかな?
とりあえず簡易な入力ダイアログが非常に簡単に作成できるようなのでメモ。後々重宝しそうな気がする。
import pywin.mfc.dialog print pywin.mfc.dialog.GetSimpleInput("my textbox title", 'my default value', 'my window title')
Windowsプログラミングにおいて、UTF-8文字列を用意したい場合にはどうすればよいか?
次の作業を行えばよい:MultiByteToWideCharでUnicodeに変換 => WideCharToMultiByteでUTF-8に変換
UTF-8エンコードについて
Yu TANAKA's Works:S-JISからUTF-8への変換
画像は情報量が多く情報伝達媒体として極めて優秀だが、ちと検索が難しいので後々の整理が悩ましい。画像のキーワードが既知であれば、それを元に文字からの検索が可能となるので、後々の整理で威力を発揮するだろう。要は画像ファイルにコメントとしてキーワードが埋め込まれていれば便利そうだという話。PNGについてこのへんの事情を調べた。[2006-12-05]も参照。
【前提知識】
もともとPNGファイルにはtEXtチャンクなるコメント用の領域が用意されている。もちろん、libpngからの読み書きが可能である。
・PNG利用術
・PNGファイル(2) PNG操作の手順
しかしながら、ここには日本語のデータを入れてはいけない模様。とても残念な仕様である。
・PNG Specification: Chunk Specifications 中に次の記述が:"The text string can contain any Latin-1 character."
・お絵描き研究室:PNG(原理編)
・PNG チャンク編集ツール < 10 < November < 2004 < nulog, NULL::something : out of the headphone
その不満を受けてのことだろうが、iTXtチャンクなる、UTF-8を入れてもいいコメント領域が追加されたようだ。「PNGの常識」と捉えるにはまだ早いのかな?
・PNG Specification: Chunk Specifications
・煤 - Note 0312
【コード】
tEXtチャンクの読み書きはPNG利用術を見るとよくわかる。
だけどiTXtのサンプルコードはあんまり見ない。Re: g_warnings in png loaderくらい?ちょっとやってみて、独断でうまくいったっぽいと感じたのでメモする。
libpngは libpng-1.2.22-no-config.tar.gz を使用。このバージョンでは、iTXtの機能はデフォルトでは無効になっている(pngconf.h の474行目 "iTXt support was turned off by default")。これを有効にするために、503行目あたり(iTXt関連の#defineが終わった直後)に無理矢理以下をねじこみ、その上でコンパイルしてライブラリを作成した。
#define PNG_iTXt_SUPPORTED #define PNG_READ_iTXt_SUPPORTED #define PNG_READ_iTXt #define PNG_WRITE_iTXt_SUPPORTED #define PNG_WRITE_iTXt
png_text text_ptr[3]; const WCHAR* wstr[3] = {L"これが1番目です", L"これは2番目かも", L"そして3番目だ"}; char str[3][1024]; char key[3][32]; for(int i=0; i<3; i++){ sprintf(key[i], "My Key %d", i); WideCharToMultiByte(CP_UTF8, 0, wstr[i], -1, str[i], 1024, NULL, NULL); text_ptr[i].key = key[i]; text_ptr[i].text = str[i]; text_ptr[i].compression = PNG_ITXT_COMPRESSION_NONE; text_ptr[i].text_length = 0; text_ptr[i].itxt_length = strlen(text_ptr[i].text); text_ptr[i].lang = NULL; text_ptr[i].lang_key = NULL; } png_set_text(png_ptr, info_ptr, text_ptr, 3);こうして作ったPNGは次の通り:
cl /MD main.cpp libpng.lib zlib.lib
#include <stdio.h> #include <stdlib.h> #include "png.h" void check_if_png(char *file_name, FILE **fp); void read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr); #define PNG_BYTES_TO_CHECK (4) int main() { FILE *fp; png_structp png_ptr; png_infop info_ptr; png_uint_32 i; check_if_png("test.png", &fp); read_png_info(fp, &png_ptr, &info_ptr); { // 以下8行はtEXtチャンクを取得し表示します png_textp text_ptr; int num_text; if (png_get_text(png_ptr, info_ptr, &text_ptr, &num_text)) for (i = 0; i < num_text; i++) printf("%s", text_ptr[i].text); } png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); fclose(fp); return 0; } void check_if_png(char *file_name, FILE **fp) { char sig[PNG_BYTES_TO_CHECK]; if ((*fp = fopen(file_name, "rb")) == NULL) exit(EXIT_FAILURE); if (fread(sig, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) { fclose(*fp); exit(EXIT_FAILURE); } } void read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr) { *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (*png_ptr == NULL) { fclose(fp); exit(EXIT_FAILURE); } *info_ptr = png_create_info_struct(*png_ptr); if (*info_ptr == NULL) { png_destroy_read_struct(png_ptr, (png_infopp)NULL, (png_infopp)NULL); fclose(fp); exit(EXIT_FAILURE); } if (setjmp((*png_ptr)->jmpbuf)) { png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL); fclose(fp); exit(EXIT_FAILURE); } png_init_io(*png_ptr, fp); png_set_sig_bytes(*png_ptr, PNG_BYTES_TO_CHECK); png_read_info(*png_ptr, *info_ptr); }