Programming」カテゴリーアーカイブ

SeeMe for Windows 5.0.0

SeeMe for Windows 5.0.0 公開。
実は大半が二日前に終わっていたのですが、たった一箇所で二日掛かった。
Deleted プロパティ変更を SeeMe 本体でどうやって検知するかで。

INotifyPropertyChanged によって ListView にはバインディングされているんだけど…

<Window.Resources>
<DataTemplate x:Key="tpl_show">
    <CheckBox IsChecked="{Binding Path=delete}" IsTabStop="False" Click="CheckBox_Click" />
</DataTemplate>

と C# で作っていた時には DataTemplate で CheckBox.Click イベントのハンドラを指定していた。
こうしておけばこの DataTemplate を利用した CheckBox 全てに同一ハンドラがコネクトされる。
クリックで property が変更されましたというのを本体で知りたいだけなのでコレで十分。

IronPython では partial class にする方法が解らない(方法あるの?)というのと
SeeMe for Linux の GTK+ と限界までソックリにしたいということで ListView をパーツ化した。
結果イベントハンドラのコネクトはコードで行う以外に手が無い。

ListView サブクラスを作って partial にして XAML の x:Name 属性を…
散々試してみたんだがどうしても上手くいかない、やはり無理か?

えっと、ハンドラを含めたリソースをコードで書いて…
方法が解らない、つかそんなことできるのかいな?

res = self.custome_listview.FindResource("tpl_show")
print res
checkbox = res.LoadContent()
print checkbox
checkbox.Click += self.on_modification

例外にならないけど反映されない、困った。
海外を探しまくったけどやはり IronPython の細かい情報は少ない…

まてよ、INotifyPropertyChanged を利用しているんだ。
それならコイツを引っ張り出して CheckBox の変更を感知すればいいかも。
無理に Click イベントに拘る必要は無かったのではないか?

と気が付くまで二日も無駄にしたというわけです…

while 1:
    i += 1
    s = "Search Engine %i" % i
    if not ini.section_exists(s):
        break
    en = Engine()
    en.name   = ini.read_str(s, "Name", "")
    en.key    = ini.read_str(s, "Key", "")
    en.url    = ini.read_str(s, "URL", "")
    en.delete = not ini.read_bool(s, "Deleted", False)
    #...
    en.PropertyChanged += self.on_modification
    self.custome_liststore.Add(en)

という感じで PropertyChanged にハンドラをコードで追加メソッドにできる。
しかもコレなら全部のプロパティ変更を監視できるので自前検知処理を減らすことができる。
でもコレだけでは ObservableCollection への追加や削除は検知できないので

self.custome_liststore = ObservableCollection[Engine]()
#...
self.custome_liststore.CollectionChanged += self.on_modification

と CollectionChanged イベントにもハンドラを指定。
コレでやっと予定していたとおりの動作になった。
WPF が複雑すぎるのではなかった、自分が方法に気が付かなかっただけだ。
細かいことは SeeMe スクリプトの Python コードを見てください。

ということで予定を大幅に遅れて Windows 版公開。
メソッド名やアトリビュート名は Linux 版とだいたい合わせたので今後更新は早くなる。
と思う、つか Aspire 1410 ミニノートだけで作るのは結構辛い…

SeeMe V5 002

SeeMe for Windows v4 で一番気にしている部分。
エディット部の XAML が超テキトーであること…

Grid のレイアウトがよく解らなかったので StackPanelのみでレイアウト。
自分で見ても解り辛い、何故か下方から積み上げる最悪のコーディング。
今の私ならパネルを噛ませる技なんかを使うけど当事は手探りだったし。

IronPython でゼロから作り変えするんだ、そろそろなんとかしなければ。
ということで Grid を利用して Linux 版の GTK+ と同じになるようにする。
ちなみに Linux 版のエディット部は GtkTable を継承して

class IniEditor(gtk.Table):
    """
        カスタムタブのエディタ部
    """
    def __init__(self):
        gtk.Table.__init__(self, 6, 7)
        label = []
        for i in range(len(editor_labels)):
            l = gtk.Label(editor_labels[i])
            label.append(l)
        self.edit_name = gtk.Entry()
        self.attach(self.edit_name, 1, 2, 0, 1)
        self.edit_key = gtk.Entry()
        self.attach(self.edit_key, 3, 5, 0, 1, 0)
        self.edit_url = gtk.Entry()
        self.attach(self.edit_url, 1, 6, 1, 2)
        self.edit_query = gtk.Entry()
        self.attach(self.edit_query, 1, 6, 2, 3)
        self.edit_uniqueid = gtk.Entry()
        self.attach(self.edit_uniqueid, 1, 5, 3, 4)
        self.check_post = gtk.CheckButton(editor_checks[0])
        self.attach(self.check_post, 5, 6, 3, 4, gtk.FILL)
        self.edit_icon = gtk.Entry()
        self.attach(self.edit_icon, 1, 5, 4, 5)
        self.check_separator = gtk.CheckButton(editor_checks[1])
        self.attach(self.check_separator, 5, 6, 4, 5, gtk.FILL)
        self.edit_encode = gtk.Entry()
        self.attach(self.edit_encode, 1, 4, 5, 6)
        self.edit_type = gtk.Entry()
        self.attach(self.edit_type, 5, 6, 5, 6, 0)
        # Label
        self.attach(label[0], 0, 1, 0, 1, gtk.FILL)
        self.attach(label[1], 2, 3, 0, 1, 0, xpadding=10)
        self.attach(label[2], 0, 1, 1, 2, 0)
        self.attach(label[3], 0, 1, 2, 3, 0)
        self.attach(label[4], 0, 1, 3, 4, 0)
        self.attach(label[5], 0, 1, 4, 5, 0)
        self.attach(label[6], 0, 1, 5, 6, 0)
        self.attach(label[7], 4, 5, 5, 6, 0)
        self.etc_label = gtk.Label("<span color=\"darkgreen\">Verfbtext =  Position =  NameID =  UseTLD =  </span>")
        self.etc_label.set_use_markup(True)
        #self.etc_label.set_justify(gtk.JUSTIFY_LEFT)
        self.etc_label.set_alignment(0.1, 1.0)
        self.attach(self.etc_label, 0, 6, 6, 7)
        # Button
        self.button = gtk.Button("リストに反映")
        self.attach(self.button, 5, 6, 0, 1, 0)

こうはやりたくないなぁ…
やはりメニュー等と同様に XAML で綺麗に仕上げたい。

とりあえず基本的にコントロールサイズに Column を合わせるには Auto を指定。
サイズ変更した時に表示名エディット部は連動して広がるように。
マージンが必要な所は Margin プロパティを指定。

editor_str = """<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <Label Grid.Row='0' Grid.Column='0'>表示名</Label>
    <TextBox Grid.Row='0' Grid.Column='1' Grid.ColumnSpan='1' Name="edit_name" />
    <Label Grid.Row='0' Grid.Column='2'>キー</Label>
    <TextBox Grid.Row='0' Grid.Column='3' Grid.ColumnSpan='2' Name="edit_key" />
    <Button Grid.Row='0' Grid.Column='6' Grid.ColumnSpan='1' Margin="20,0,20,0" Name="button">リストに反映</Button>

    <Label Grid.Row='1' Grid.Column='0'>URL</Label>
    <TextBox Grid.Row='1' Grid.Column='1' Grid.ColumnSpan='5' Name="edit_url" />

    <Label Grid.Row='2' Grid.Column='0'>クエリ</Label>
    <TextBox Grid.Row='2' Grid.Column='1' Grid.ColumnSpan='5' Name="edit_query" />

    <Label Grid.Row='3' Grid.Column='0'>UNIQUEID</Label>
    <TextBox Grid.Row='3' Grid.Column='1' Grid.ColumnSpan='4' Name="edit_uniqueid" />
    <CheckBox Grid.Row='3' Grid.Column='5' Grid.ColumnSpan='2' Name="check_post">POSTで送信</CheckBox>

    <Label Grid.Row='4' Grid.Column='0'>ICON</Label>
    <TextBox Grid.Row='4' Grid.Column='1' Grid.ColumnSpan='4' Name="edit_icon" />
    <CheckBox Grid.Row='4' Grid.Column='5' Grid.ColumnSpan='2' Name="check_separator">下にセパレータ</CheckBox>

    <Label Grid.Row='5' Grid.Column='0'>エンコード</Label>
    <TextBox Grid.Row='5' Grid.Column='1' Grid.ColumnSpan='3' Name="edit_encode" />
    <Label Grid.Row='5' Grid.Column='4'>サーチタイプ</Label>
    <TextBox Grid.Row='5' Grid.Column='5' Grid.ColumnSpan='2' Name="edit_type" />

    <Label Grid.Row='6' Grid.Column='0' Grid.ColumnSpan='6' Name="label_etc" Foreground="DarkGreen">Verbtext Position NameID UseTLD</Label>
</Grid>"""

なんとか同じようになった、しかし

GTK+ と違って WPF は縮めるとこうなってしまう…
MinWidth とか数値プロパティでではなくコントロールサイズを最小サイズにしたい。
知らないだけで方法があるのだろうか?

んで、コントロール名を元にアトリビュートを作成。
ということで下記のようなコードを書いてみたのだが。

self.editor = XamlReader.Parse(editor_str)
self.editor.edit_name = self.editor.FindName("edit_name")
self.editor.edit_name.Text = "abcde"
DockPanel.SetDock(self.editor, Dock.Bottom)

なんでだよ、Python なのにアトリビュートにできないじゃん。
しかたがないからダミーのクラスを作ってみる。

class Editor():
    pass

class SeeMe(Window):
    def __init__(self, sset):
        # ...
        self.editor = Editor()
        edit = XamlReader.Parse(editor_str)
        self.editor.edit_name = edit.FindName("edit_name")
        self.editor.edit_name.Text = "abcde"
        DockPanel.SetDock(edit, Dock.Bottom)

これなら上手くいく。
XAML から作成すると Python オブジェクトとして扱ってくれないようだ。
IronPython は Python クローンだと思って使うと色々落とし穴がありますね。

seeme5_002.zip

どうでもいいけど readme.txt と ChangeLog.txt を BOM 付き UTF-8 に。
Python コードは BOM 無し UTF-8 に、色々と事情があってコレに統一。
それと改行を CRLF にしとかないと notepad.exe で改行してくれない。
Windows って面倒くさいなぁ…

見た目はなんとかなったので後は編集コードの作成だ。
連休だけど月曜は仕事だ、明日には仕上がるだろうか…

追記 @ 2010.07.19
進んでいないけど盗難が怖いのでバックアップ、畜生、夏のやろう!

seeme5_003.zip

追記 @ 2010.07.20
エディタ部への設定書き出しを実装、思っていたより手間が掛かる。
そういえば Linux 版は競合キーチェックを入れ忘れしていた…
ついでに Has endseparator をマイナスにしていない、10.60 では無意味な部分だが。

seeme5_004.zip

追記 @ 2010.07.21
__init__.pyw というファイル追加、コレを ipyw(64).exe に関連付けかドロップ。
としておけば Python ワカンネな人でも理解しやすいかも?と思ったので…
ini 保存は色々あって UTF-8 に BOM を付けるようにした。
デフォルト uuid 指定タブもなんとか完成、もう変更できるようにしておいた。
Item の編集と削除は実装したけど新規追加や並べ替え処理はまだ実装していない。
Linux 版とアトリビュート名やメソッド名合わせを考えなければもっと早く作れるけど…
次の日の仕事に影響が出ない範囲で作っているのもあって我ながら遅い…

seeme5_005.zip

SeeMe V5 001

SeeMe for Windows の Opera 10.60 対応版は IronPython で作ります。
一年前に途中まで作ってみたけど色々あってボツにした下記をベースで。

SeeMe is to IronPython

Linux しか使わなくなった作者が Windows のアプリを作るのは苦痛でしかない。
というより作者自身が使わないアプリを作ってもメンテナンスに疑問が残る。
こんな需要が少ないアプリを他の人がやってくれるとも思えない。
それより今の C# という言語が自分の趣味に合わない、Ruby が嫌いな理由と同じ。
ついでに VisualStudio を使いたくない、コンパイルもメンドクサイ。

一週間悩んだ結果、好き勝手にやることにした。
使いたい人は IronPython を導入してくれ、というトンデモネェ暴挙をマジでやる。

もちろんスクリプトのままで配る。
Windows しか使えない人の大半は EXE でないと拒否反応をするのは知っている。
それがキッカケで IronPython に興味を持ってくれる人が増えれば嬉しい。

戯言終わり、ということで。

xml:lang

現行の IronPyton では XAML に xml:lang=”ja-JP” を追記せずとも文字化けしない。
結局アレは IronPython のバグだったのだろうか?

WPF ListView DataBinding for IronPython

この pyevent.py をコピーさせなければいけないのもなんとかしたい。
ということでよく考えてみたらコレでいいのでは?と。

from System import *
from System.ComponentModel import *

# Search pyevent.py
import sys
tutorial_path = IO.Path.GetDirectoryName(sys.executable) + "\\Tutorial"
sys.path.insert(0, tutorial_path)
from pyevent import *

zip 版でも exe パス直下に Tutorial ディレクトリがあるのでイケるはず。
なんにせよ INotifyPropertyChanged は使わなければいけないわけで。
ただ zip 版を考えると os.path.join() は使えない…
追記 System.IO.Path.Combine() ってのがあった。

.NET Framework required BOM

この件もあった…
inifile8.py は他に os.path.exists を System.IO.File.Exists に書き換えて…
sys は組み込まれているけど他は Python 標準モジュールを使わないように…

SeeMe for Linux と共用できるコードがほとんど無いんだなコレが。

あとは x64 を見分けて Opera デフォルトインストール場所振り分けとか。
それから何があったかなぁ?もう少し更新が遅れるのはご了承。
とりあえず現在までのバックアップ。

seeme5_001.zip

少し楽しくなってきた、こんなことをやりながら作り上げていくのが楽しいんだよね。
「プログラミングを楽しむ」と「アプリを作って楽しむ」とでは実は少し違うのよ。

g_utf8_collate_key_for_filename of ctypes

g_utf8_collate_key_for_filename
がいったいどういう変換をしているかもう少し。

ところで端末に長々と打ち込むのが面倒なので GEdit 外部ツールに以下を指定。
GTK+ を使うには pkg-config 指定が必要なのよね、キーは F7 にした。

/*
gedit tool script

#!/bin/sh
gcc $GEDIT_CURRENT_DOCUMENT_PATH `pkg-config --cflags --libs gtk+-2.0`
*/

#include <stdio.h>
#include <gtk/gtk.h>
#include <glib.h>

int main() {
    gchar c[4];
    int i;
    for (i=0; i<105; i++) {
        sprintf(c, "%d", i);
        gchar *s = g_utf8_collate_key_for_filename (c, -1);
        printf("%s\n", s);
        g_free(s);
    }
    return 0;
}

端末だと解り難いにでリダイレクトしてみる。

十進で桁が増える毎にコロンが一つ付加されていくようだ。
なんだかよく解らない変換だけどこれで自然順ソート比較は上手くいくようだ。
色々やってみたけど結局 Python から ctypes を使う。

14.14.1 ctypesチュートリアル

ココに全部書いているんだが、Linux はちびっと面倒なのね。

glibc = ctypes.cdll.LoadLibrary('libglib-2.0.so.0')
cmpstr = glibc.g_utf8_collate_key_for_filename("a")

とやっても int が戻ってくる、実際にはポインタだが Python にはポインタ型が無いので。
つまり restype をキッチリ指定しないと全部 int になるってことですね。
ということで。

#! /usr/bin/env python
# -*- coding: UTF-8 -*-

import ctypes

glibc = ctypes.cdll.LoadLibrary('libglib-2.0.so.0')
cmpstr = glibc.g_utf8_collate_key_for_filename
cmpstr.restype = ctypes.c_char_p
cmpstr.argtypes = [ctypes.c_char_p, ctypes.c_int]

def sort_nicely(l):
    l.sort(lambda x, y : cmp(cmpstr(x, -1), cmpstr(y, -1)))

たまには lambda を使ってみようと思ったので。
ガベージコレクションなのだからコレでいいはずだけど…
とにかくこれでどうだ?

よし Nautilus とはドットファイルを除けば一致するようになった。
隠しファイルはリストアップに含めないようにする予定なのでどうでもいいけど。

でも Mandriva KDE 上で動かしたら何故か数値ソートしてくれなかった。
って ./ を付け忘れで以前のバージョンを起動しただけだった、GNOME と同様になる。
つか Dolphin のソートって以前の関数とまったく同じ結果じゃん!

これじゃ設定でどちらかに振り分けしてもらうしか両対応の方法が無さそう。

utf8_collate_key

いいかげんに Y901x が落ちまくる件とソート問題を解決させねば。
なんとかさせないと追加機能もやれないよ。
落ちる件は色々試しているんだがまだ原因が解らない、困った…

ソートに関しては自力を諦め Nautilus のコードをひたすら追う。
libnautilus-private/nautilus-file.c
に compare_by_display_name というソレっぽい関数をやっと見つける。

display_name_collation_key を strcmp で比較しているだけなのか。
g_utf8_collate_key_for_filename という glib の関数で代入している。

Unicode Manipulation

あれ、もしかして数値もドットもこの関数一つで解決してまうの?

The Whole PyGTK FAQ

Python には実装されていないようで。
最近のバージョンではあるかもと dir() で探しまくるも見つからず。

とにかくこの関数でどう変換されるのか気になるので C でやってみる。
Ubuntu 10.04 デフォルトでは gcc はあるけど gtk や glib のヘッダは無い。
Glade を入れれば依存関係で Devhelp を含めまとめて入るので Glade を入れる。

/*
gcc  b.c `pkg-config --cflags --libs gtk+-2.0`
*/

#include <stdio.h>
#include <gtk/gtk.h>
#include <glib.h>

int main() {
    gchar *c;
    c = "a1.mp4";
    gchar *u;
    u = g_utf8_collate_key_for_filename (c, -1);
    printf("%s to %s\n", c, u);
    /*g_free(c); is Segmentation fault*/
    g_free(u);
    return 0;
}

久々の C なんだがこんな感じでよかったかなぁ…
strcpy で警告になったのが Visual Studio と同じだったが代替が解らない。
とにかく結果。

なんだかよく解らないのに変換されとる。
後は比較関数を作って実験して上手くいったら…
Python で使うんだが、ctypes しか手が無いかな?