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

Change in Python3

Y901x を Python3 化してみた。
Python3 って pyc キャッシュは __pycache__ ディレクトリに入るのね。

言語としての一貫性を重視したPython 3の進化 ? @IT

Y901x は gir 以外は sys, os, time の標準モジュールしか使っていない。
多分文字列と割り算さえ気を付ければそれほど変更点は無いだろう。

Python3 はデフォルトが UTF-8 なので coding 指定が不要に。
これで海外の Python 製アプリの動作が変という場合は減ると思う。

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

to

#!/usr/bin/env python3

例外はカンマから as に変わり raise も関数(?)になった。

#except Exception, e:
except Exception as e:

#raise ValueError, "hoge"
raise ValueError("hoge")

忘れちゃいけない unicode 関数を使っていた部分は str にして。

#s = unicode(entry.get_text()) # Python2
s = entry.get_text()
i = s.rindex(".")

割り算で整数にならないと困る部分は / を // に書き換えて。
/= も //= にしないといけないのか。
逆に float にキャストしていた部分はキャストを外して。

Python 歴があるなら誰でも知っていることはこれくらいで。

他 Fedora 19 の gir を色々試してみると
GLib.filename_from_uri 関数の動作が Ubuntu 13.04 と同じになった。
Gio.ApplicationFlags.HANDLES_OPEN も使えるようになった。

しかし Gio.ActionEntry が作れず ApplicationMenu は使えないまま。
使えるなら多重起動防止の設定を追加しようと思ったけどまだムリっぽい。
pygobject のバージョンに今後も振り回されるんだろうな。

今頃気が付いたけど inifile8.py に古いものを使っていた。
Python3 互換の文字列フォーマッタ版を随分前に作ったのにアホだ。

思ったより簡単だと変更していたけど問題は文字列や割り算ではなかった。
自然順ソートを行う numsort.py だった。

Python3 には cmp 関数が無い、いやコレの自作だけなら簡単だけど。
sort(), sorted() の引数が違う、てか比較関数が使えない。

ソート HOW TO ? Python 3.3 documentation

自力でクイックソートを作るか cmp_to_key をコピペするかだね。
cmp_to_key 関数を使うとなるとこんな感じでいいようです。

#! /usr/bin/env python3

from gi.repository import GLib, Gio

def sort_func(str1, str2):
    """
        str1.encode("utf8") is not required.
        Not 'cmp' function in Python3
    """
    cmpstr1 = GLib.utf8_collate_key_for_filename(str1, -1)
    cmpstr2 = GLib.utf8_collate_key_for_filename(str2, -1)
    # cmp function
    if cmpstr1 < cmpstr2:
        return -1
    elif cmpstr1 > cmpstr2:
        return 1
    return 0

def cmp_to_key(mycmp):
    """
        http://docs.python.org/3.3/howto/sorting.html # en
        http://docs.python.jp/3.3/howto/sorting.html # Japanese
        Convert a cmp= function into a key= function
    """
    class K(object):
        def __init__(self, obj, *args):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        def __ne__(self, other):
            return mycmp(self.obj, other.obj) != 0
    return K

def get_file_numsort_list(dirname):
    """
        param dirname: Directory Full Path Name
        return: List in Numeric Order
    """
    files = []
    d = Gio.file_new_for_path(dirname)
    enum = d.enumerate_children(
            Gio.FILE_ATTRIBUTE_STANDARD_TYPE,
            Gio.FileQueryInfoFlags.NONE,
            None )
    for info in enum:
        if info.get_file_type() == Gio.FileType.REGULAR:
            s = info.get_name()
            if not s.startswith("."):
                if not s.endswith("~"):
                    files.append(s)
    # Python2
    #files.sort(lambda x, y : cmp(GLib.utf8_collate_key_for_filename(x, -1), 
    #            GLib.utf8_collate_key_for_filename(y, -1)))
    #
    # Python3
    files.sort(key=cmp_to_key(sort_func))
    return files

これじゃラムダ式にできない、別にいいけど。
随分コードが長くなってしまったけどアプリケーションは動けばいいのさ。
アーカイブサイズが減った理由がワカラナイ…

GLib.utf8_collate_key_for_filename は str のままイケた。
GLib で文字列として扱う場合は UTF-8 で得られるということみたい。
でも Python 文字列として扱う場合は UCS-4 であるのを意識する必用あり。
でいいのだろうか、いつか落とし穴にはまりそうな気もする。

ついでに PyGI で今更気が付いたこと。

class CToolBox(Gtk.Box):
    def __init__(self):
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)

GtkBox は GtkOrientable をインプリメントしているから orientation property がある。
なのでこの指定で GtkBox のサブクラスを作れば GtkVBox と同様になる。
いやプロパティは全部 __init__ の引数にできるのを知ったのは最近だし。

y901x101

とりあえず Python3 化した Y901x 1.0.1 公開。
memopoli の更新をやるべきなんだろうけど、実はもう使っていないのだがどうしよう。

1.0.1 Download (34.0kb)

blog にもリンクを今回から貼ることにする。
需要は無いだろうけど何かの参考になればいいや。

Fedora 19 Install (HDD) part3

とうとう Fedora にもデフォルトで Python3 が入るようになった。
そりゃ当然 Python 作者達はいいかげんに移行してほしいだろうけど。

python3_japanese

Python2 の byte 列が単なる str のエイリアスだったのが厳密になっただけだが。
こんな感じで ascii より扱いづらくなるのよね。

それと Gtk+ を使って文字列のやりとりは UTF-8 と UCS-4 を変換しているのかな?
とにかく内部文字列が UCS-4 の言語だと色々とデメリットしか無い気がするが…
Windows だと内部も Python3 も、ついでに C# も UTF-16 だから逆に歓迎だが。
Vala なんて C# 風な言語なのに文字列は ascii なんだよね。

Seed, Gjs は今回もあるけど全然普及しないな…

Gedit で自作の Python 製プラグインがロードできない。
Could not find loader ‘python’ for plugin ‘html_escape’
Python が見つからないってどういうことだよ…
まさかと思って *.plugin のローダーを python3 に変更したら動いた、マジかよ。
公開プラグインは面倒だしトップページからお知らせでお茶を濁すことにする。
スニペットと外部ツールは以前のを ~/.config/gedit からコピペで使えた。

他の我が Python 製アプリはまだ問題なく動くみたい。
PyGst はやはり無くなったか、Y901x は早めに切り替えしてよかった。
どっちにせよ Python3 製に変更しなきゃいけなくなるだろう。

とにかく Fedora で Python を少しでも使っている人は注意してね。

**********

あまりに自然すぎて気が付かなかった。
dconf-editor の説明が日本語表示になっている。

dconf_editor_jp

メニューのカテゴリを変更したいな。
org.gnome.shell app-folder-categories
で指定するみたいだけど
[‘Utilities’, ‘Sundry’, ‘Office’, ‘Network’]
とやっても Office フォルダはできるけどメインメニュー側にも残るしブラウザフォルダは作成すらされない。
‘Network’, ‘Internet’ と 2 つ指定しないとインターネットカテゴリがまとまらない。

Enabling Categories in GNOME 3.8 Shell Application Menu

[‘Utilities’, ‘Games’, ‘Sundry’, ‘Office’, ‘Network’, ‘Internet’, ‘Graphics’, ‘Multimedia’, ‘System’, ‘Development’, ‘Accessories’, ‘System Settings’, ‘Other’]

のように全部カテゴリを指定すると全部まとまる。
/usr/share/applications/*.desktop
指定のカテゴリが複数ある場合は個別だとメインに残るということなのね。
これじゃ全部かデフォルト以外は選べない、あまり意味がない。

festival command

Linux で Mac OS の say コマンドのようなことをやりたい。
筆者は Mac を持っていないのだが検索すると面白そう。

厳密には GTK+ アプリ等で選択した文字列なんかを喋るようにしたい。

この英語スペルはどう発音するか等の簡易チェックに使いたい。
頻繁に使うわけではないので組み込みではなく単独アプリのほうがいい。
chrome 等から DnD で文字列を受け取ると読み上げるみたいな感じで。

GNOME3 には orca というアプリがある。
フォーカスのあるウインドウ文字列を読み上げる。
だけなのか。
つまり全部読み上げるので超鬱陶しい、求めているのはコレじゃない!
PyGI がエラーを吐いているし、日本語部はチャイニーズうんたらって何よ。

orca

というか、検索すると皆日本語を読ませようと四苦八苦しているみたい。
業務用途なら当然そうなるけど個人利用なら必要性をまったく感じないんだが。
英語の発音チェックに使いたいと考える筆者みたいな人はあまりいないのね。

ということで。

探してみると festival というコマンドがあるようだ。
Festival

私の Fedora 18 には最初から入っていた。

# stdout
echo "Read and Write" | festival --tts
# Read file
festival --tts read.txt

こんな感じで使える、これは使えそうだ。
英語で一定速しか読み上げできないけど私的には充分だ。

文字列のドロップで読み上げる PyGI ウインドウを作ってみる。
ついでにファイルドロップでも中身を読み上げできるように。

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

from gi.repository import Gtk, Gdk, GLib
import subprocess

class SayWin(Gtk.Window):
    def __init__(self):
        """
            Speech at the Drop string
        """
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        self.set_title("Say")
        # DnD
        dnd_list = [Gtk.TargetEntry.new("text/plane", 0, 0),
                    Gtk.TargetEntry.new("text/uri-list", 0, 1) ]
        self.drag_dest_set(
                Gtk.DestDefaults.MOTION |
                Gtk.DestDefaults.HIGHLIGHT |
                Gtk.DestDefaults.DROP,
                dnd_list,
                Gdk.DragAction.COPY )
        self.drag_dest_add_uri_targets()
        self.drag_dest_add_text_targets()
        self.connect("drag-data-received", self.on_drag_data_received)
        # GtkLabel
        self.label = Gtk.Label("Please drop your Selection Text or File")
        self.add(self.label)
        self.show_all()

    def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
        if info == 0:
            # text/plane
            p1 = subprocess.Popen(['echo', data.get_data()], stdout=subprocess.PIPE)
            p2 = subprocess.Popen(['festival', '--tts'], stdin=p1.stdout)
            p1.stdout.close()
            p2.communicate()[0]
        elif info == 1:
            # text/uri-list
            uris = data.get_uris()
            filename = GLib.filename_from_uri(uris[0], None)
            subprocess.call(['festival', '--tts', filename])

SayWin()
Gtk.main()

say_app

GLib.spawn_command_line_async でパイプを使う方法が解らなかった。
subprocess モジュールでマニュアルどおりにしたけど何か冗長な感じ。
もう少しスマートにできそうだけど。

イマイチな感じもするけど目的どおりにはなった。
orca のほうが発音が解りやすいのでソッチが利用できればなと思うけど。

Empty the trash from PyGI

我が公開 Linux アプリでまだ PyGI 化していないものがあった。
trash だ、しょーもないアプリだと思って作ったが意外に本人は結構使う。

PyGI 化だけだと二分で終わるし何かないかな。
そうだ、ゴミ箱を空にするオプションを付けちゃえ!
実際にそんな機能を使うかどうかは別の話。

Nautilus のソースを参考にすれば多分手段は解るだろう。
ということでソレっぽい名前で grep しまくる。

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

"""
    Empty the trash.
"""

from gi.repository import Gio

# Get Trash URI
trash_uri = Gio.file_new_for_uri("trash:")
# Get Child
enum = trash_uri.enumerate_children(
        "{0},{1}".format(
            Gio.FILE_ATTRIBUTE_STANDARD_NAME,
            Gio.FILE_ATTRIBUTE_STANDARD_TYPE ),
        Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
        None )
# Delete
for info in enum:
    f = trash_uri.get_child(info.get_name())
    f.delete(None)

こんな感じでやっていた。
当然元の C 言語はもっと複雑だったけど Python は楽だ。
普通に g_file_delete しているだけなんだな。

Nautilus から Ctrl+L でロケーションバーを表示させる。
んで trash: と打ち込んで Enter するとゴミ箱が開くんだね。
Explorer の sendto みたいなことができたのか、知らなかった。

んでファイルリスト作成と同様に enum を得る。
g_file_get_child でゴミ箱内の GFile を個別で得る。
既に GFile なのでそのまま g_file_delete できる。

パス名に変換して rm コマンドに渡すとか最初は考えていたけど不要だった。
Nautilus のゴミ箱アイコンは監視しているようでしっかり連動する。
何かメッセージを送らないといけないかと思ったけどコレも不要だった。

より手段は解った。
あとはコマンドラインオプションをどう実装するか。

15.4. optparse ? より強力なコマンドラインオプション解析器 ? Python 2.6ja2 documentation

こんな便利なモンが標準モジュールにあったのか!
使わないと損だ、ということで適用。

ただバージョン情報を -V, –version にしようと思ったけど手段が無い。
書くまでもなく Python と同じにしたかっただけですけど。
自前で –version を定義するとコンフリクトしていると怒られる。
素直にあきらめよう。

ということで trash-1.0.0 公開。
全アプリの PyGI 化完了、去年からの目標だったけど長かった。

Xubuntu 13.04 and GtkApplication

Ubuntu 13.04 は少し軽くなったとはいえ相変わらず 3D しか使えない。
テスト用途にしか使わない仮想とはいえ重すぎる。
しかも操作性最悪、何故カスタマイズしてまで Unity を使う人がいるのか?

なので今回も LXDE…
では芸が無いので Xubuntu にしてみた、実は初めてです。

sudo apt-get install xubuntu-desktop

ってなんだこれ、ちっとも軽くないというより重すぎ!

XFCE は GTK2 ベースだけど 3D 表示しているんだな。
[ウインドウマネージャ(詳細)]で合成処理の有無を切り替えできるようだ。
ということは有効状態なら下記で背景が透けるはず。
Transparent GtkWindow | PaePoi

xubuntu_visual

off にすると Lubuntu ほどではない気がするけど充分軽くなった。
コレなら継続利用できそうだ、ということで気になる部分をチェック。

デフォルトプレイヤーの parole で動画が再生できないぞ!
しかしコイツもやはり GStreamer なのか。

xubuntu_video

gnome-session-properties に何も無い。
あれ?と思い 12.04 LTS のを見ても何もない、独自処理だったのか。
キーリングとかはどうしているんだろう、便利なのに。

ub_session

つまりディレクトリ名監視なんかしていない。
普通に不要なデフォルトディレクトリを削除したり F2 キーで変名できる。
何故かデスクトップだけは変名できなかったけど。

GTK+ のバージョンは 3.6.4 って今の Fedora と同じかい。
でも pygobject が変わっているのだからもしかして。

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

from gi.repository import Gtk, Gio
import sys

class Win(Gtk.Window):
    """
        Test "HANDLES_OPEN"
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        swin = Gtk.ScrolledWindow()
        self.view = Gtk.TextView()
        swin.add(self.view)
        self.add(swin)
        self.resize(320, 240)
        self.show_all()

    def add_uri(self, uris):
        buf = self.view.get_buffer()
        buf.set_text(uris)

class AppClass(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="org.gnome.test_id",
                flags=Gio.ApplicationFlags.HANDLES_OPEN )
        self.connect("startup", self.on_startup)
        self.connect("activate", self.on_activate)
        self.connect("open", self.on_open)

    def on_startup(self, data=None):
        self.window = Win()
        self.add_window(self.window)

    def on_activate(self, data=None):
        self.window.present()

    def on_open(self, application, files, n_file, hint, data=None):
        """
            Ubuntu 13.04 @ SUCSESS!
            Fedora 18    @ FAILED...
        """
        s = ""
        for f in files:
            s += "{0}\n".format(f.get_uri())
        if s == "":
            s = "FAILED..."
        self.window.add_uri(s)
        self.window.present()

if __name__ == "__main__":
    app = AppClass()
    app.run(sys.argv)

gtkapplication

ついに HANDLES_OPEN に対応してくれたようだ。
コレで多重起動防止アプリを Python で作るのが簡単になったね。
Fedora 19 が待ち遠しいぜ!

clipoli も動く、よしコイツはこのままでいいな。
memopoli も動く、いや初期状態に問題があることは解っているんだが。
とりあえず我がアプリは Y901x 以外は大丈夫であるようだ。

肝心の使い勝手は Lubuntu とたいして変わらない。
しかし標準テキストエディタはショボすぎだろ、GtkSourceView なのに。

Xubuntu は合成オフ可能なことに気がつくまではキレそうだったけど悪くない。
別に GTK2 ベースのデスクトップでも GTK3 開発は可能なわけだし。
ただ 3.6 なので Fedora 18 から乗り換える理由は何も無いけど。

FTP やスマホ接続で Nautilus が絶対にファイルマネージャである必要があるのでホスト OS は今後も GNOME にしますが。