Logicool Mouse M505

ここ最近 Opera でマウス動作の調子が悪い。
クリックが勝手に文字列選択になったりマウスジェスチャ後に右メニューが出たりetc…
Chrome ではなんともない、やっぱりこんなところが Opera だなぁ…

と思っていたけど Chrome でも何か動作が変になってきた!
それどころか GEdit や Nautilus でもマウスで上手く選択ができなくなってきた!

試しにドスパラ BTO 付属の超握りにくいマウスに繋ぎ替えてみる。
Opera のバグだと思っていたマウス動作が全部解決…

Opera ちゃん疑ってごめんよ!

単純に Opera は設計上マウスカーソルに対して強烈に敏感だったということだろう。
Linux Opera の動作がおかしくなったら先にマウスかキーボードを先に疑ったほうがいいかも。
フォント表示がおかしいのは昔からというか毎度のことだけど…

ということで、ようするにマウスが壊れた。

しばらくは今繋いでいるドスパラ BTO 付属のマウスを使ってみよう。
Windows Vista の時に世間の反応と自分の使用感のあまりの違いを見て
「今までと違う考えは積極的に受け入れよう」
と自分で決めたのだから。

と思っていたけど超握りにくいわ軽過ぎるわ反応鈍いわでタマラン!
マウスは自動車のタイヤがクソじゃどうにもならんのと同じでストレスが凄すぎる。
素直に新しいのを買おう…

価格.com – マウス 売れ筋ランキング

Logicool 製ばっかだなぁ…
ちなみに私は今まで Logicool 製しか買ったことが無いのは前に書いたっけ。

しかし今でも Logicool 製か Microsoft 製かの選択肢しか無いんだね。
この二社以外は余程のことがないかぎり、その余程のことなんてありえるのか?

次もやはり Logicool かとも思うけど Windows 7 ノート用にコレを使っている。
Logicool の Bluetooth はコレしか見つからなかっただけなのだが。

反応とか握りやすさはまったく不満が無いてか最高だけどホイールクリックを間違えるのよね。
Bluetooth のせいかスリープ復帰が一瞬遅れる場合がある、2.4GHz タイプなら大丈夫なのかな?
高速ホイールはあんまり使わないしやはりホイールをクリックする方式なマウスががイイ。

有線にするか無線にするかも悩む、なんたって Linux で無線マウスは使えるのか?
検索しまくったら上記の一番人気である M505 ならば何も問題無いみたい。
なにより今更接続コードの存在に制約を受けるのも嫌だしやはり無線式を選びたい。
そうなると M505 を選ぶのが一番いいみたい。

黒が人気だけどシルバーが汚れが目立たないしよさそう。
アマゾンで見つけたけど早速パソコンショップに行って(ぉい!
だってこういうのは実際に手で握って感触を確かめたいんだもの。

と思ったけどショップで握ってみてもイマイチ感覚が解らないや!
とっとと購入し家で試す、二千九百八十円だったからそんなに差は無いし。
どうでもいいけど先日電気屋で買った炊飯器の値段をアマゾンで調べてガックリした…

単三電池二本は付属していたのでそのまま入れる。
重さや重心は M555 とほぼ同じ、形状が全然違うはずなのに握った感触も似ている。
ホイールの位置や高さ、ボタンの傾斜角、親指と薬指が収まる位置関係、床面のすべり具合
といったところを並べて見比べると見事に一致、さすが同じメーカー。

気になっていた無線は Linux マシンなのにスイッチを入れただけで当然のように動いた。
あっけなさすぎ、あぁケーブルの無いマウスってすばらしい。

ということで使いごこちが悪いはずもなくとても快適。
スリープしているのか?というくらい反応も敏感、精度も有線時代と変わらない。
どうしても Bluetooth がいいというのでなければコレ最高です。

ただホイールクリックが少々固いのとホイール左右倒しがボタンとして機能している。
そのせいで Opera でホイールクリックしようとすると「戻る」になってしまったり…
そういうときは

の2つを上記に変更すれば横倒しは左右スクロールになるみたい。
Chrome や GEdit とかはデフォルトで横スクロールしてくれるのに…

SeeMe 5.0.1 and 1.0.5

GtkSharp TreeView Tutorial – Mono

まだ SeeMe for Linux を GTK# で作っていた頃の参考にしたトコ。
よく見ると自作クラスを型として GtkListStore を作成しているコードがあった。
Controlling how the model is used 以下のところね。

こういう型指定って PyGtk でもできるのかな?
可能であればもっと本体のコード量を減らすことができるんだが。
とりあえず Python で同じように SeeMe コードを書きかえてみた。

class Engine():
    def __init__(self, deleted=True, name="New Item", key="", url="", query="",
                post=False, endsp=False, encode="utf8", stype=0, pos=0,
                nameid=0, verb=0, icon="", unique="", usetld=0):
        self.delete = deleted
        self.name = name
        self.key = key
        self.url = url
        self.query = query
        self.post = post
        self.endsep = endsp
        self.encode = encode
        self.stype = stype
        self.pos = pos
        self.nameid = nameid
        self.verb = verb
        self.icon = icon
        self.unique = unique
        self.usetld = usetld

class SeeMe4(gtk.Window):
    def __init__(self, sset):
        gtk.Window.__init__(self)
        #...
        #self.default_liststore = gtk.ListStore(bool, str, str, str, str, bool, bool, str, int, int, int, int, str, str, int)
        self.custome_liststore = gtk.ListStore(Engine)

型指定の時点で駄目ジャン…
property にしてみたり小細工してみたりしたけど無駄な努力だった。
Python では変数宣言しただけでは型が決まっていないので当然なのかも。

gtk.ListStore

ま、公式の Constructor 解説には C 言語と同じ型を全部書く方法しか書かれていない。
できないことは素直に諦めて、せめて GtkListStore への append をもう少し簡単にやれないか?

てか、そうしておかないと後々のメンテで沢山書き換えを行わなければいけなくなる。
後で書き換えが必要だろう箇所が少なければ少ないほどミスが減るのよね。
というかソレがオブジェクト指向最大のメリットなのだし。

よく考えたら SeeMe で GtkListStore の型は一つしか無い。
だったら GtkListStore サブクラスを作ってコンストラクタでとっとと型を指定。
ついでに Engine クラスを受け取る add メソッドを作れば簡単になるかな?

class Engine():
    def __init__(self, deleted=True, name="New Item", key="", url="", query="",
                post=False, endsp=False, encode="utf8", stype=0, pos=0,
                nameid=0, verb=0, icon="", unique="", usetld=0):
        self.delete = deleted
        self.name = name
        self.key = key
        self.url = url
        self.query = query
        self.post = post
        self.endsep = endsp
        self.encode = encode
        self.stype = stype
        self.pos = pos
        self.nameid = nameid
        self.verb = verb
        self.icon = icon
        self.unique = unique
        self.usetld = usetld

class CreateListStore(gtk.ListStore):
    def __init__(self):
        gtk.ListStore.__init__(self, bool, str, str, str, str, bool, bool, str, int, int, int, int, str, str, int)

    def add(self, en):
        return self.append( [en.delete, en.name, en.key, en.url, en.query,
                en.post, en.endsep, en.encode, en.stype, en.pos,
                en.nameid, en.verb, en.icon, en.unique, en.usetld] )

class SeeMe4(gtk.Window):
    def __init__(self, sset):
        gtk.Window.__init__(self)
        #...
        self.custome_liststore = CreateListStore()
        #...

    def read_default_searchini(self):
        self.default_liststore.clear()
        inipath = self.sset.default_path
        lngpath = self.sset.lang_path
        if os.path.exists(inipath):
            ini = inifile8.Inifile(inipath)
            lng = inifile8.InifileReader(lngpath)
            try:
                self.iniver = ini.read_int("Version", "File Version", 0)
                i = 0
                while 1:
                    i += 1
                    s = "Search Engine %i" % i
                    if not ini.section_exists(s):
                        break
                    t = ini.read_str(s, "Key", "")
                    if t == "":
                        continue
                    en = Engine();
                    en.nameid = ini.read_int(s, "Nameid", 0);
                    if en.nameid != 0:
                        # Get View Name from *.lng
                        if en.nameid == 17171:
                            en.name = lng.read_str("Translation", "1632215285", "")
                        elif en.nameid == 17183:
                            en.name = lng.read_str("Translation", "-1971470391", "")
                        elif en.nameid == 71103:
                            en.name = lng.read_str("Translation", "-1453429782", "")
                        else:
                            en.name = lng.read_str("Translation", str(en.nameid), "")
                        if en.name == "":
                            n = -1752227277 - en.nameid;
                            en.name = lng.read_str("Translation", str(n), "")
                    else:
                        en.name = ini.read_str(s, "Name", "")
                    en.key = t
                    en.url = ini.read_str(s, "URL", "")
                    en.query = ini.read_str(s, "Query", "")
                    en.post = ini.read_bool(s, "Is post", False)
                    en.endsep = ini.read_bool(s, "Has endseparator", False)
                    en.encode = ini.read_str(s, "Encoding", "utf-8")
                    en.stype = ini.read_int(s, "Search Type", 0)
                    en.pos = ini.read_int(s, "Position", -1)
                    en.verb = ini.read_int(s, "Verbtext", 0)
                    en.delete = True #!
                    en.unique = ini.read_str(s, "UNIQUEID", "")
                    en.icon = ini.read_str(s, "ICON", "")
                    en.usetld = ini.read_int(s, "UseTLD", 0)
                    """self.default_liststore.append( [
                            en.delete, en.name, en.key, en.url, en.query,
                            en.post, en.endsep, en.encode, en.type, en.pos,
                            en.nameid, en.verb, en.icon, en.unique, en.usetld] )"""
                    self.default_liststore.add(en)

    def on_item_new(self, widget, event=None):
        uid = self.create_uuid()
        num = self.deditor.get_radio_num()
        if num == 2:
            num = -1988219522
        else:
            num = 0
        en = Engine(verb=num, unique= uid)
        #it = self.custome_liststore.append( [True, "", "", "", "", False, False, "utf-8", 0, -1, 0, num, "", uid, 0] )
        it = self.custome_liststore.add(en)

コードは全然短くならないけど Engine を直で渡して展開できるようになった。
デフォルト引数によってアトリビュートの型も大半が気にしないでいいという。
読み込みや新規アイテムで順番を気にする必要が無くなったのはデカい。
これなら後々で順番の入れ替えや追加の必要があっても型側で調節できる。

ただこのコードでは展開するために for ループにすると

for row in self.default_liststore:
    if row[0] == False:
        i += 1
        s = "Search Engine %i" % i
        ini.write_str(s, "UNIQUEID", row[13])

添字アクセスにするしか無いんだなぁこれが、意味ネェ。
イテレーターは整数による順番という概念を使わないことに意義があるのだが。
この部分については WPF は徹底的にやっている感じ、よく考えて作られているよ。
ここをアトリビュートで取り出すサブクラスをどうすれば作れるか考え中。

ということで重複キーチェック追加や不具合修正で Linux 版更新。
キーボードショートカットを増やしたり不具合修正で Windows 版も更新。

「こんなことができると楽」
な方法を考えている時がアプリケーション作りで一番楽しいです。

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

Microsoft.Win32.OpenFileDialog for .NET 4.0

今日 IronPython を少し試していて気が付いたのだが

# -*- coding: UTF-8 -*-

import clr

clr.AddReferenceByPartialName("PresentationFramework")

import Microsoft

def dlg_open():
    dlg = Microsoft.Win32.OpenFileDialog()
    dlg.ShowDialog()

dlg_open()

あれ?Windows 7 標準のダイアログになった。
この関数だと XP 時代の古いダイアログになってしまうはずなのに。
x64 の Windows 7 だからなのか IronPython が .NET 4.0 版だからなのか?

IronPython – Release: 2.6.1

試しに .NET 2.0 版の IronPython zip 版も落として実験。
同じコードを動かしてみる。

やはり 2.0 版だと Windows 7 でも古いまんまだ。
なるほど、.NET 4.0 は同じ関数でも地味に改良してくれているんだね。
WPF を使うのが少し楽になった、地味に調べてみると面白いかもしれない。