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

# -*- 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

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 は面倒なんだが…

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

# -*- 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

IntPtr(shinfo.hIcon)

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

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 でファイル名を渡してたから上手くいかなかったのね。

#-*- 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 にて

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

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

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