IronPython」タグアーカイブ

IronPython への関連付け

Windows 7 を Linux の GNOME のように便利に使いたい、その2。

Windows でスクリプトを使うのに Linux に比べ何が不便なのか。
書くまでも無い、実行パーミッションという概念が無いこと。
すべてが拡張子による関連付けで決まってしまうこと。

特に普通の Python はインストールで *.py に関連付けられるので悲惨。
ファイルをうっかり W クリックすると無意味に cmd.exe コンソールが出る。
ま、インストールする気は無いけど、Windows では何の約にも立たない。

Windows 版 Python はその辺りを考えてか Pythonw という exe が同根されている。
コンソール不要なら拡張子を *.pyw にすればいい、だけどそれだけだ。
何より Windows ではコンソールなんてほとんどの人が使わない。
IronPython にも同様に ipyw(64) という exe が同根されている。

ipyw64

しかしよく考えてみれば私の Y901x や SeeMe も実行パーミッションは付けていない。
同根のシェルスクリプトからメインファイルを Python に引渡しているだけ。
ならば ipyw64.exe を利用して同じようにすればいいんでないの?

ということで考えた。

*.py をテキストエディタに関連付け
*.pyw を ipyw64.exe に関連付け(私の Windows 7 が 64bit なので)

というくだらない方法だがこれで Linux で Python を使っている時と同様に使える。
実行はシェルスクリプトの変わりに ipyw64.exe にやってもらおうという考え。

main_window.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# -*- coding: UTF-8 -*-
 
import clr
 
clr.AddReferenceByPartialName("PresentationCore")
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName("WindowsBase")
 
from System import *
from System.Windows import *
from System.Windows.Controls import *
from System.Windows.Media.Imaging import *
 
class PicViewWindow(Window):
    def __init__(self):
        self.Title = "PicView"
        self.Drop += self.on_drop
        self.AllowDrop = True
        self.Width = 300
        self.Height = 300
        self.image = Image()
        self.Content = self.image
 
    def on_drop(self, sender, e):
        filename = e.Data.GetData(DataFormats.FileDrop)[0].ToString()
        self.image.Source = BitmapImage(Uri(filename))
 
def main():
    Application().Run(PicViewWindow())
 
if __name__ == "__main__":
    main()

picview.pyw

1
2
import main_window
main_window.main()

という2つのファイルを用意し上記関連付けを行っておく。
picview.pyw を W クリックしてみる。

ipyw_picview

ということができる。
main() という関数を用意するのがミソ。
debug 時は EmEditor 等テキストエディタの機能で以下のように実行できるから。

ぱぇぽぃ2 ? Blog Archive ? EmEditor を Gedit 風にカスタマイズ

これでコードを編集したい時は *.py 実行時は *.pyw を W クリックという環境完成。
アイコンが違うので解りやすいしこのほうがコード分割も行いやすい。
ただ OS デフォルト動作では無いので配布に関してはどうするかだ…

Get File Details and Icon Image for IronPython ctypes

C# で SHGetFileInfo を使ってアイコンと種類を取ってくる例

の IronPython 化コード。
CreateBitmapSourceFromHIcon の第一引数は IntPtr であった。

Imaging.CreateBitmapSourceFromHIcon メソッド (System.Windows.Interop)

HICON である ctypes.c_void_p をそのまま渡すと error になる。
例外くらい吐いてくれよ…
printf debug は面倒なんだが…

ということで以下のようなコードを書いてみた。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# -*- coding: UTF-8 -*-
 
"""
    get_file_detailst.py
    System.IO.FileInfo and SHGetFileInfoW Windows API use
    both the x86 and x64 editions
"""
 
import clr
 
clr.AddReferenceByPartialName("PresentationCore")
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName("WindowsBase")
 
from System import *
from System.IO import *
from System.Windows import *
from System.Windows.Controls import *
from System.Windows.Documents import *
from System.Windows.Media.Imaging import *
from ctypes import *
 
MAX_PATH = 260
HICON = c_void_p
 
# ShellFileInfoFlags
SHGFI_ICON = 0x000000100
SHGFI_DISPLAYNAME = 0x000000200
SHGFI_TYPENAME = 0x000000400
SHGFI_ATTRIBUTES = 0x000000800
SHGFI_ICONLOCATION = 0x000001000
SHGFI_EXETYPE = 0x000002000
SHGFI_SYSICONINDEX = 0x000004000
SHGFI_LINKOVERLAY = 0x000008000
SHGFI_SELECTED = 0x000010000
SHGFI_ATTR_SPECIFIED = 0x000020000
SHGFI_LARGEICON = 0x000000000
SHGFI_SMALLICON = 0x000000001
SHGFI_OPENICON = 0x000000002
SHGFI_SHELLICONSIZE = 0x000000004
SHGFI_PIDL = 0x000000008
SHGFI_USEFILEATTRIBUTES = 0x000000010
 
# FileAttributesFlags
FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
FILE_ATTRIBUTE_HIDDEN = 0x00000002,
FILE_ATTRIBUTE_NORMAL = 0x00000080,
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
FILE_ATTRIBUTE_OFFLINE = 0x00001000,
FILE_ATTRIBUTE_READONLY = 0x00000001,
FILE_ATTRIBUTE_SYSTEM = 0x00000004,
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
 
class SHFILEINFOW(Structure):
    """
        typedef struct
    """
    _fields_ = [("hIcon", HICON),
                ("iIcon", c_int),
                ("dwAttributes", c_uint),
                ("szDisplayName", c_wchar * MAX_PATH),
                ("szTypeName", c_wchar * 80)]
 
class GetFileInfoWindow(Window):
    """
        Get FileInfo as Droped File
    """
    def __init__(self):
        """
            Window.__init__(self) is No
            Constructor has been
        """
        self.Title = "Get FileInfo"
        self.Drop += self.on_drop
        self.AllowDrop = True
        self.SizeToContent = SizeToContent.WidthAndHeight
        self.ResizeMode = ResizeMode.NoResize
        # Controls
        panel = StackPanel()
        self.Content = panel
        b = TextBlock()
        b.Inlines.Add(Bold(Run("Get FileInfo as Droped File")))
        panel.Children.Add(b)
        self.image = Image()
        self.image.Height = 32
        self.image.Width = 32
        panel.Children.Add(self.image)
        # Create TextBlock List
        self.texts = []
        for i in range(4):
            t = TextBlock()
            panel.Children.Add(t)
            self.texts.append(t)
 
    def on_drop(self, sender, e):
        """
            Get Dropped File Details
        """
        filename = e.Data.GetData(DataFormats.FileDrop)[0].ToString()
        shinfo = SHFILEINFOW()
        r = windll.shell32.SHGetFileInfoW(
                filename,
                FILE_ATTRIBUTE_NORMAL,
                byref(shinfo),
                sizeof(shinfo),
                SHGFI_ICON |
                SHGFI_LARGEICON |
                SHGFI_TYPENAME |
                SHGFI_DISPLAYNAME
            )
        if r == 0:
            self.texts[0].Text = "error"
        else:
            # Get System.IO.FileInfo
            ioinfo = IO.FileInfo(filename)
            # Icon
            bms = Interop.Imaging.CreateBitmapSourceFromHIcon(
                    IntPtr(shinfo.hIcon),
                    Int32Rect(0, 0, 32, 32),
                    BitmapSizeOptions.FromEmptyOptions()
                )
            self.image.Source = bms
            # File Name
            self.texts[0].Text = shinfo.szDisplayName
            # Updated
            self.texts[1].Text = ioinfo.LastWriteTime.ToString("yyyy/MM/dd HH:mm")
            # Type Name
            self.texts[2].Text = shinfo.szTypeName
            # File Size
            self.texts[3].Text = Math.Ceiling(ioinfo.Length / 1024.0).ToString("#,##0 KB")
 
if __name__ == "__main__":
    Application().Run(GetFileInfoWindow())

get_fileinfo_x64

1
IntPtr(shinfo.hIcon)

と cast すれば Icon Image も普通に取得できるようである。
HICON, HWND のような Handle は Pointer と同じと考えていいのね。

1
str(e.Data.GetData(DataFormats.FileDrop)[0])

だと日本語ファイル名が上手くいかない、str と ToString は少し処理が違うみたい。

とにかくこれでアイコンや詳細を取得できるどころか x64 対応コードが簡単に書ける。
C# の P/Invoke なんてもうばかばかしい、これからは IronPython の時代だ。

しかし上記リンク先コードは色々間違えていると今頃気が付いた。
SMALLICON 指定だし ANSI 関数だし SHFILEINFO 第二引数は int にしないといけないし…
32bit Windows は int と Pointer はサイズが同じだからこういう間違いって多いんだろうな。

IronPython SHGetFileInfoW is ctypes

Re: SHGetFileInfo :: ASPN Mail Archive :: python-list

の IronPython 化が解った。
HICON は ctypes.c_voidp or ctype.c_void_pにすれば x86,x64 両方に対応できる。
追記: c_voidp は後方互換性で残っているだけのようで…
x64 のみでいいなら ctypes.int64 でもいいみたいだけど…
違いは何なのだろう?もう少しソースコードを見てみよう。

ついでに UNICODE Function 化、x64 で ANSI なんて悲しすぎる。
つか IronPython は内部が UNICODE や…
ANSI 関数に UNICODE でファイル名を渡してたから上手くいかなかったのね。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#-*- coding:utf-8 -*-
 
from ctypes import *
 
MAX_PATH = 260
HICON = c_void_p
 
# SHFILEINFOW
class SHFILEINFOW(Structure):
    _fields_ = [("hIcon", HICON),
                ("iIcon", c_int),
                ("dwAttributes", c_uint),
                ("szDisplayName", c_wchar * MAX_PATH),
                ("szTypeName", c_wchar * 80)]
 
SHGFI_ICON              = 0x000000100
SHGFI_DISPLAYNAME       = 0x000000200
SHGFI_TYPENAME          = 0x000000400
SHGFI_ATTRIBUTES        = 0x000000800
SHGFI_ICONLOCATION      = 0x000001000
SHGFI_EXETYPE           = 0x000002000
SHGFI_SYSICONINDEX      = 0x000004000
SHGFI_LINKOVERLAY       = 0x000008000
SHGFI_SELECTED          = 0x000010000
SHGFI_ATTR_SPECIFIED    = 0x000020000
SHGFI_LARGEICON         = 0x000000000
SHGFI_SMALLICON         = 0x000000001
SHGFI_OPENICON          = 0x000000002
SHGFI_SHELLICONSIZE     = 0x000000004
SHGFI_PIDL              = 0x000000008
SHGFI_USEFILEATTRIBUTES = 0x000000010
 
shfileinfo = SHFILEINFOW()
 
import sys
 
# SHGetFileInfoW
flags = SHGFI_DISPLAYNAME | SHGFI_TYPENAME | SHGFI_ATTRIBUTES
print windll.shell32.SHGetFileInfoW(sys.executable,
                                   0,
                                   byref(shfileinfo),
                                   sizeof(shfileinfo),
                                   flags)
 
# print no repr
print hex(shfileinfo.dwAttributes)
print shfileinfo.szDisplayName
print shfileinfo.szTypeName

python_list

よし動いた。
後下記をコレで書いてみようと思ったけど何故か Image が取得できない。

C# で SHGetFileInfo を使ってアイコンと種類を取ってくる例

c_voidp で HICON は取得できないのかな?
もう少し調べる。

IronPython が RC3 になっていた

IronPython のサイトを久々に覗いてみた
なんか RC3 になっていた、まだ正式版にするつもりは無いのだろうか?

IronPython

とりあえずダウンロード、おぉ ctypes モジュールが付属になったのか。
てゆーかいつのまにかメチャメチャ付属モジュールが増えている!

ctypes_and_etc

ちなみに zip 版にはコレらは入っていないようである。
早速動作テストだ。

ぱぇぽぃ2 ? Blog Archive ? ctypes で WindowsAPI

も当然のように IronPython のみインストールでそのまま可能になった。
普通の Python はもう本当に必要無いね、そうしたかったんだろうけど。

Re: SHGetFileInfo :: ASPN Mail Archive :: python-list

でもコレはできない…
64bit で動かないから 32bit の Vista でも試したけど駄目だった。

32bit_test

バグっぽいけどもう少し弄くってみます。

ついでに Mandriva Linux でも少し試してみた。
3つもダウンロードした私は余程人柱になりたいのだろうか。

ipy26test

clr を import できない…
これじゃ何もできん、2.0.3 なら問題無いんだが。

Python property Decorator

INotifyPropertyChanged and databinding in IronPython WPF

Wonderful!
私が書いた下記を「理想的じゃネェ」と断言しているだけある。

ぱぇぽぃ2 ? Blog Archive ? WPF ListView DataBinding for IronPython

OnPropertyChanged を先に呼ぶことになるのが気になるけど…
問題なく動くから多分 OnPropertyChanged は Queue に入るのだろう。
組み込み以外で Decorator の実用的な使い方を初めて見た。

PyGtk 製 Y901x にて

1
2
3
@dbus.service.method("org.y901x.Interface1")
def set_uri(self, uri):
    ###

と使っているが何故これで D-Bus 経由で別インスタンスから関数が呼べるのか実は知らない…
自身で関数を呼んでいるのではないし、もっと調べると面白い使い方ができそうだ。

とにかく良いものを見つけた、こんな Blog をやっているといいことあるよ。
てかこの Blog へのリンク元を見ると海外からばかり、co.uk とかもあるし。
日本の Python 屋って非実用的コードばかり書いている人が多いですからね。