月別アーカイブ: 2011年11月

Gedit External Tools and Seed argv

Linux に移行してからテキスト編集のほぼすべてで Gedit を愛用している。
Windows から Linux に移行できたのも Gedit が存在したからなのは確実といえる。
Gedit をシンプルという人はただの無知、Windows 的思考を早く捨てろといいたい。
Windows ではほとんど意味が無くなった環境変数が Linux では重要なのです。

前置きはこれくらいにして。
私は Python コードの Debug に長いこと以下の外部ツールを使っていた。

#! /bin/sh
python $GEDIT_CURRENT_DOCUMENT_PATH

を F5 キーに割り付けて結果をボトムペインに表示させている。
しかし最近 Seed を少しかじってたりするが、つい F5 を押してしまう。
F6 等に割り付けしても ibus に取られるのか無視されるし何より間違える。

それなら F5 からソースによりで振り分けしてしまえばいいじゃないか!
環境変数 $GEDIT_CURRENT_DOCUMENT_TYPE で ContentType が得られるので

#!/bin/sh
PYTHON="text/x-python"
SEED="application/javascript"
HTML="text/html"
BASH="application/x-shellscript"
echo @Lunning $GEDIT_CURRENT_DOCUMENT_TYPE
if [ $GEDIT_CURRENT_DOCUMENT_TYPE = $PYTHON ]; then
    python $GEDIT_CURRENT_DOCUMENT_PATH
elif [ $GEDIT_CURRENT_DOCUMENT_TYPE = $SEED ]; then
    seed $GEDIT_CURRENT_DOCUMENT_PATH
elif [ $GEDIT_CURRENT_DOCUMENT_TYPE = $HTML ]; then
    google-chrome $GEDIT_CURRENT_DOCUMENT_PATH
elif [ $GEDIT_CURRENT_DOCUMENT_TYPE = $BASH ]; then
    sh $GEDIT_CURRENT_DOCUMENT_PATH
else
    echo Non Support File
fi

こうしておけば ContentType 次第でコマンドを変更できるじゃない。
私が現在利用するのはコレくらいだ、好みで追記とかすればいい。
ちなみに Perl の ContentType は application/x-perl である。
一部の人は「Opera じゃないのか?」とツッコまないでください。

これで Python でも Seed でもボトムペインに結果を出力できる。
そういえば Seed 名前空間にはどんなメソッドがあるのかな?
Seed で Python の dir() に相当するのは for in 文なので

#!/usr/bin/env seed

for (var s in Seed) {
    Seed.print(s);
}

と、こんな感じで利用できます。
なるほど、argv オプションはココから得るのかと解った。
for 文で取り出して、argv.length でコマンド数が得られるようだ。

#!/usr/bin/env seed

for (var i=0; i < Seed.argv.length; i++) {
    Seed.print(Seed.argv[i]);
}

Python とは違い引数の最初に seed が含まれてしまうようだ。
+x して ./ でも結果は同じ、注意しないと間違えそう。

後は for in 文を使って各名前空間のメソッドをボトムペインで調べてと。
かなり Seed プログラミングが楽になったぞと。
いや、間違えて js を Python で、というのが無くなるのが一番嬉しいか。

ここまで Gedit を利用してアプリ作ったりコードをバリバリ書いていても…
Linux 屋からみれば「コイツ vi 使えないの?ププッ!」と思われているかも…
使い方はマジで知らないですけど、サブのエディタは nano ですし。

Gio GFileEnumerator and load_contents

seed をやっている日本人がいた、あぁ日本語で読めるのは素晴らしい。
とはいえ海外でもあまり盛り上がっていないわけですが。
前ページを見て笑った、日本語で探すとそうなってしまうんだよ。

seedでGIOしてみる – ふとしの日記

なるほど、Gio でディレクトリ内容列挙したい場合はこうするのか。
引数は必ずしも JSON でなくてもいいのか?又解らないことが増えた。
while は for in 文でいいんじゃない?と思ったが…
そういえば JavaScript ではオブジェクトメソッドの列挙になるのよね。

ま、私は PyGI でヤルんですけど。
ということで $HOME のファイルを列挙するサンプルコード。
“standard::name” だと byte になるので “standard::display-name” にした。

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

import os
from gi.repository import Gio

# set Directory Name
d = Gio.file_new_for_path(os.path.expanduser("~"));
# Get GFileEnumerator
enum = d.enumerate_children(
        Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
        Gio.FileQueryInfoFlags.NONE,
        None )
for info in enum:
    # info: GFileInfo
    print info.get_display_name()

Python だと当然のように for in 文でイケるようだ。
seed は JavaScript の仕様そのままなので面倒かも。
逆に enumerate_children 等は引数を全部指定する必要がある。
seed は引数が曖昧でいいところもやっぱり JavaScript なのね。

ついでに Gio もう一つ、海外。
GIO tutorial: Stream IO ? Johannes Sasongko’s blog

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

import os
from gi.repository import Gio

# set File Name
fn = os.path.expanduser("~/.gtk-bookmarks")
# Create GLocalFile
f = Gio.File.new_for_path(fn)
# load_contents
result, contents, length = f.load_contents(None)
if result:
    print contents

で .gtk-bookmarks 内容が print される。
PyGtk を PyGI に書き換えしたのだが引数も戻り値も微妙に違った。
とにかくファイルの一騎読み込みはこうすればいいみたい。

でも stream のほうは PyGI では上手く行かない。
何か手段があるのだろうけど今はまだ Gio 勉強中。

# おまけ

fedora 16 アップデート通知に Gedit があった。

やっとドラッグアンドドロップ編集で落ちるのを修正してくれた!
ついやってしまっていったい何度書いたコードを無駄にしたことか。

Read in gjotsfile

昨日のコードを .gjotsfile を読み込むように改造してみた。
Gjots2 が保存するファイルは

“\NewEntry” でエントリーの追加
“\NewFolder” で入れ子の作成
“\EndFolder” で入れ子を一階層戻る
ツリータイトルはエントリーの一行め

だけという恐ろしく単純明解な構造である。
ルールもタイトルの一文字めには \ を使わないということだけ。
これなら初心者でも自力で読み書きするコードが書けそうです。

Python でファイルを一行毎に処理する方法は

path = os.path.expanduser("~/.gjotsfile")
f = open(path)
x = f.read()
f.close()
lines = x.split("\n")
for line in lines:
    hoge

という一番単純な方法と

path = os.path.expanduser("~/.gjotsfile")
f = open(path)
try:
    for line in f:
        hoge
finally:
    f.close()

の方法がある、下の方法のほうが圧倒的に早い。
一度全部読み込んで展開するのかストリームで読むかの差である。
但し行末に改行コードが含まれている、それと try 文にしないと何かあった場合に close できないというオチがあるので注意して使いましょう。

とりあえず上記フラグがあった場合に分岐して if 文を作成。
現在は空状態を示す方法として “\NonTitle” みたいなフラグを追加。
“\NewEntry” で GtkTreeStore にアペンドして読み込んだ部分を空にする。
イテレータを “\NewFolder” で入れ替え “\EndFolder” で書き戻し。

そんな感じにして上手くいったコード。

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

import os
from gi.repository import Gtk, Gio, GtkSource

class TreeWin(Gtk.Window):
    """
        GtkTreeView Sample
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # CellRenderer
        cell = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Tree Title", cell)
        column.add_attribute(cell, "text", 0)
        # TreeView and TreeStore
        store = Gtk.TreeStore.new([str, str])
        tree = Gtk.TreeView.new_with_model(store)
        tree.append_column(column)
        # Hide Header
        tree.props.headers_visible = False
        # Signal
        selection = tree.get_selection()
        selection.connect("changed", self.selection_changeed)
        #
        # Data
        self.path = os.path.expanduser("~/.gjotsfile")
        t = ".gjotsfile"
        s = ""
        it = None
        itold = None
        f = open(self.path)
        try:
            for line in f:
                if line == "\\NewEntry\n":
                    if t == ".gjotsfile":
                        it = store.append(it, [t, s])
                    elif t != "\\NoneTitle":
                        store.append(it, [t, s])
                    s = ""
                    t = "\\NoneTitle"
                elif line == "\\NewFolder\n":
                    itold = it.copy()
                    it = store.append(it, [t, s])
                    s = ""
                    t = "\\NoneTitle"
                elif line == "\\EndFolder\n":
                    store.append(it, [t, s])
                    it = itold
                    s = ""
                    t = "\\NoneTitle"
                else:
                    if t == "\\NoneTitle":
                        t = line[:-1]
                    s += "{0}".format(line)
        finally:
            f.close()
        #
        # GtkSourceView
        self.view = GtkSource.View()
        self.view.set_show_line_numbers(True)
        # Paned and ScrolledWindow
        swa = Gtk.ScrolledWindow()
        swa.add(tree)
        swb = Gtk.ScrolledWindow()
        swb.add(self.view)
        hp = Gtk.HPaned()
        hp.add1(swa)
        hp.add2(swb)
        # self
        self.set_title("TreeView Test")
        self.add(hp)
        self.show_all()

    def selection_changeed(self, widget, data=None):
        """
            Params @ widget: GtkTreeSelection
        """
        model, it = widget.get_selected()
        if it:
            buf = self.view.get_buffer()
            buf.set_text(model.get_value(it, 1))

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="apps.test.treeview",
                flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_activate)
        
    def on_activate(self, data=None):
        w = TreeWin()
        w.set_application(self)
    
if __name__ == "__main__":
    app = App()
    app.run(None)

もう少しスマートにできそうなんだけど…
そういえば Ubuntu でも動くかな、仮想マシンの 11.10 で実験。

よし動く、後は書き込みとツリーの追加削除移動なんかだ。
とりあえず今日はココまで、ノンビリすぎだと思うけど。

Python with GTK3 @ GtkTreeView

gjots2 が ORBit2 を必要とするので GNOME3 に入れたくない。
というわけで代わりになるものを自作することにした。
まてよ、私は GtkTreeView を ListView としてしか使ったことがない。
GtkListStore を GtkTreeStore にすればいい、だと思うけど実験だ。

ところで PyGI のウイジェットには new というメソッドがあるわけだが。

store = Gtk.TreeStore.new([str, str])
# or Compatibility PyGtk
store = Gtk.TreeStore(str, str)

GtkTreeStore を作るには new でシーケンスを渡すか PyGtk と同様に作成。
というか TreeStore(str, str) のほうは多分 PyGtk 互換用だろう。
C では数と内容をズラズラだから PyGI 的にはシーケンスで書くほうが正しいかと思う。

けど new_with_model とかのパラメータ指定差を吸収する互換も便利だから捨てがたい。
パラメータが void しか無いウイジェットは互換のままでいいと思うけど。

そういえば、最近気がついたのだが Property も props メソッドから辿れる。
どちらでも結果は同じなのでお好みで、しかしなんというか Gtk# 臭くなったものだ。
ハイフンをアンダースコアに変換するのを忘れないでね。

# Convert '-' to '_'
tree.props.headers_visible = False
# or Compatibility PyGtk
tree.set_property("headers-visible", False)

それはともかく、GtkTreeView の利用方法。
CellRenderer, TreeViewColumn, TreeStore(ListStore) が必要。

ツリーに表示するレンダラを決める、今回は文字列なので GtkCellRendererText を。
そのレンダラを表示するカラムが必要、複数作れば ListView になると解るね。
シーケンスの何番目データをツリー表示するか、今回は最初のデータなので 0 を指定。

型を指定してシーケンスにして TreeStore を作成。
ソレを TreeModel として TreeView 作成のパラメータにする。
先ほど作成したカラムをアペンド、コレでツリー部分作成は終わり。

ツリー選択を変更したシグナルを捕まえるコードを忘れずに。
ハンドラの widget は GtkTreeSelection になるようだ。

後は GtkTreeIter を介してデータを入れていく。
GtkTreeIter に None を指定すればツリーの先頭に入る。

人により微妙に言い回しが違う専門用語だらけになったけどなんとなく解って。
GTK+ には簡単な ListBox が無いので嫌でも使うハメになる時があるから。

ということでサンプルコード。

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

from gi.repository import Gtk, Gio, GtkSource

class TreeWin(Gtk.Window):
    """
        GtkTreeView Sample
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # CellRenderer
        cell = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Tree Title", cell)
        column.add_attribute(cell, "text", 0)
        # TreeView and TreeStore
        store = Gtk.TreeStore.new([str, str])
        tree = Gtk.TreeView.new_with_model(store)
        tree.append_column(column)
        # Hide Header
        tree.props.headers_visible = False
        # Signal
        selection = tree.get_selection()
        selection.connect("changed", self.selection_changeed)
        #
        # Data
        it0 = store.append(None, ["1.0", "Kaname Madoka"])
        store.append(it0, ["1.1", "Akemi Homura"])
        store.append(it0, ["1.2", "Tomoe Mami"])
        store.append(None, ["2.0", "Miki Sayaka"])
        it1 = store.append(None, ["3.0", "Sakura Kyoko"])
        store.append(it1, ["3.1", "QB"])
        #
        # GtkSourceView
        self.view = GtkSource.View()
        self.view.set_show_line_numbers(True)
        # Paned and ScrolledWindow
        swa = Gtk.ScrolledWindow()
        swa.add(tree)
        swb = Gtk.ScrolledWindow()
        swb.add(self.view)
        hp = Gtk.HPaned()
        hp.add1(swa)
        hp.add2(swb)
        # self
        self.set_title("TreeView Test")
        self.add(hp)
        self.show_all()

    def selection_changeed(self, widget, data=None):
        """
            Params @ widget: GtkTreeSelection
        """
        model, it = widget.get_selected()
        if it:
            buf = self.view.get_buffer()
            buf.set_text(model.get_value(it, 1))

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="apps.test.treeview",
                flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_activate)
        
    def on_activate(self, data=None):
        w = TreeWin()
        w.set_application(self)
    
if __name__ == "__main__":
    app = App()
    app.run(None)

ツリー構築だけならそんなに難しくはないね。
後はファイルを読み込んでどうメモリ展開するかだ。

せっかくなので不満だった部分
・GtkSourceView を利用した色分け表示
・タイトルは一行目ではなく独自データに
なんかも実装しつつ Gjots2 完全互換も可能にできればいいな。

# おまけ

窓の杜 – 【NEWS】「AzPainter」「AzDrawing」の作者がWindows向けの全ソフトの開発終了を宣言

書いていることが Linux を初めたばかり丸解りなんだが…
AU○RA とかみたく寒いことにならなきゃいいけど。

Linux では Windows なんて比較にならないほど有名どころ以外は見向きもされないのだから外国人向けに英語でサンプルコードでも書いて自分が使う範囲だけ作ってたほうがマシだって。

Celeron su2300 mini Note @ Fedora Dual Boot

Acer Aspire 1410 ミニノートの電源を三ヶ月ぶりくらいで起動。

当然のごとく二時間半程度ひたすらアップデート、更に何度か再起動…
電源すら入れていないのにセキュリティエッセンシャルがスキャン開始…
ゲンナリして使う気をなくしそのまま電源オフ…
をやったのが三ヶ月前、そして今回も…

このままでは永久ループだ!
必要になった時に即使えないモバイルなんてまったくの無意味。

Windows に文句を言ってもしかたがない。
とにかくアップデート時間の短縮と無意味スキャンをなくしたい。
ならば Linux にするしか無い、しかし Windows7 を消すのも躊躇する。

ということで Fedora 16 とのデュアルブートにすることにした。
ミニノートに Linux で無線 LAN とかは大丈夫だろうか、実験を兼ねて。

とりあえず Windows7 を起動して Linux を入れる空き領域の確保。
「スタートボタン」→「コンピューター」右クリして「管理」を選択。
「ディスクの管理」から C ドライブを選択、右クリして「ボリュームの縮小」
表示された最大値で圧縮、簡単に空き領域確保。

画像は Fedora を入れた後だから埋まっているけど右二つ分ほど領域が開いた。
後は Fedora Live CD を立ち上げ以前と同じようにインストールする。
Fedora 16 64bit Install (HDD) | PaePoi
Live 時に無線 LAN の指定をしておいたらそのまま引き継がれたよ。
デュアルブートにするにはインストール時に「空き領域に…」を選択。
だけで見事 GRUB でのブート選択ができる環境の完成。

Fedora 16 の起動時間は約 3 分…
Windows7 も安定するまではそのくらい掛かるけど、やっぱり遅すぎ。
起動後にバックでガリガリとかはまったく無いのは嬉しいけど。

当然のように最初から 1366×768 画面で起動。
GMA4500M のショボイグラフィックでも問題なく 3D デスクトップ。
ノートなのを認識して画面や電源の設定で節電項目が増える。
Windows の NTFS 領域にもマウントしてアクセスできる。
無線 LAN は選択して暗号化キーを打ち込むだけで接続完了。
Bluetooth マウスもアッサリ認識。
タッチパネルの指二本スクロールもマウス設定から指定可能。
天板を閉めると普通にサスペンド。

今の Linux って凄い、何の苦労もなくほぼ全自動だった。

そうそう音も普通に鳴る、昔は凄い苦労をした記憶があるが。
Intel グラフィックでも問題無い、以前は相性最悪だったのだが。
Compiz の Ubuntu だと解らないけど、GNOME 3 は Clutter なので。

動画再生支援は無いけど 1080p もギリギリ再生できる。
支援機能の利用はライセンス料がいるからしかたがないよ。

これでもうアップデートとスキャンでゲンナリすることは無い。
デスクトップと同じ OS だと 1.2GHz CPU の遅さを体感できるw

それにしても Linux は快適だ。
非アクティブのアプリでもホイールスクロールできるのが嬉しい。
たまに Windows7 を使うと「あれ?」ってなる、特に Explorer で。

ついでに Windows7 からもいらないアプリを削除。
Live 全削除、Adobe は Flash だけ残して削除。
RealPlayer Mini は配布が無くなっているって何よ、これも削除。
PDF は evince、メールは Opera、動画はノートで見ない。
よしスッキリ、でも次に Windows を使うのは又三ヶ月後くらいだろう。