Python」タグアーカイブ

Python3 convert

Python3 に移行して随分たったが今頃気がつく。
コレ (cp932 to utf-8) ができない!
g_convert | PaePoi

GLib.convert() 関数の第一引数は gchar* なので cp932 の文字列を渡す必要がある。
Python3 では当然 bytes(b’…’) である。
そのまま指定するとバイト配列なくて文字列にしなさいというエラーになる。

#!/usr/bin/env python3

"""
    ConvertError: Must be string, not bytes
"""

import sys
from gi.repository import GLib

try:
    result, contents = GLib.file_get_contents(sys.argv[1])
    if result:
        try:
            text, bytes_read, bytes_written = GLib.convert(contents, len(contents), "UTF-8", "CP932")
            print(text)
        except Exception as e:
            print("ConvertError: {0}".format(e))
except Exception as e:
    print("FileError: {0}".format(e))

python3_g_convert

Python2 だと bytes と str って実は同じものだから問題ないのだけど。
Python3 では文字列に変換っっって何だそれ、cp932 だぞおい。

Python3 の文字列が UCS-4 になった弊害がこんなところに…
いや pygobject が今後対応してくれるかもしれないけど。

あれこれ試してみたけど現状 GLib や Gio では対処できないようだ。
しかたがないので Python 組み込みの decode() を使ってみる。

#!/usr/bin/env python3

import sys
from gi.repository import GLib

"""
    f = open(sys.argv[1], encoding="cp932")
    print(f.read())
    f.close()
"""

try:
    result, contents = GLib.file_get_contents(sys.argv[1])
    if result:
        try:
            str932 = contents.decode("cp932")
            print(str932)
        except Exception as e:
            print("ConvertError: {0}".format(e))
except Exception as e:
    print("FileError: {0}".format(e))

g_convert を使うより圧倒的に簡単なコードになってしまった。
というか組み込みの open() なら更に簡単だったり。

Vala からも参考にできるよう今までやってきたつもりだけど、もう無理。
しかし Vala の string が gchar* のままなのは本当に正解だよね。

GtkCellRendererAccel

GtkCellRendererAccel がいったい何なのか解らなかった。
海外をいくら検索してもサンプルコードが無いどころか devhelp 丸写しのみ。
そんなこんなで詰まって覚書ページ作成が止まっていたり…

ココにすらないし、避けている?
12. CellRenderers ? Python GTK+ 3 Tutorial 3.4 documentation

ウイジェット名からコラムにフォーカスを当てるキーボードアクセラレータかなと。
コラムは列なので行を選択する GtkTreeView ではまったくの無意味だし。
いったいこのレンダラの正体は何かと考えてはや半年。

何故か今日テキトーに試していたらやっと理解。
覚書ページのほうに書いたのでサンプルコードはソチラで。
レンダラ – L’Isola di Niente

つまり

gtkcellrendereraccel

この部分ってソレ専用のレンダラだった。
キーボードアクセラレータの設定及び確認以外の使い道が何もないwww

とはいえコモンダイアログなんかも皆同じものを使っているわけで。
操作性に統一感を出そうとする GNOME なら当然の選択だったのかも。
と思ったけどよく見ると GTK+ 2.1 の頃にはすでにあったのか。
何故サンプルコードがどこにも無いのだ?

やっとココを突破できたので少しは追記が進みそうです。
バイク乗りにしかわかんないネタになってきたけど、まどかネタ飽きた。

Empty the Clipboard. and Set Clipboard Text

Gedit の GtkToolbar を見ていて気が付いた。

Clipboard が空の時に Gedit を起動。
ウエブブラウザ等の別アプリで Ctrl+C を押し何か文字列をコピー。
すると Gedit の貼り付けアイコンが自動的に有効になる。
当然 Ctrl+V で貼り付け可能。

リアルタイムで監視していたんだね。
クリップボードに何か入っているかのインジケータとして使える、便利だ。

それはともかく、ウエブブラウザを終了してもクリップボードに残っている。
あれ?以前はコピー元を終了すると使えなくなっていたはずだが。
GNOME2 時代の古い記憶だから今は変わっているのかもしれない。

よし実験、ちなみに今後このブログは Python3 コードで書きます。

#!/usr/bin/env python3

from gi.repository import Gtk, Gdk

"""
    Empty the Clipboard.
"""

display = Gdk.Display.get_default()
clipboard = Gtk.Clipboard.get_for_display(display, Gdk.SELECTION_CLIPBOARD)
clipboard.set_text("Failed", -1)

だめジャン!
と思ったけど Gedit のツールバーを見ると貼り付けアイコンが無効になっている。
つまりこうするとクリップボードを空にできるってことみたい。

不本意だがクリップボードを空にする方法を発見してしまった。
使い道はともかく。

何故だろう、我が clipoli と同じコードを書いているのに。
もしかして mainloop が必用なのだろうか。

clipoli から mainloop を通る最小限コードを抜き出して実験。

#!/usr/bin/env python3

from gi.repository import Gtk, Gdk

"""
    Success additional main loop
"""

class ClipboardTest(Gtk.Menu):
    def __init__(self):
        Gtk.Menu.__init__(self)
        menuitem = Gtk.MenuItem.new_with_mnemonic("_Set Clipboard Text")
        menuitem.connect("activate", self.on_menuitem)
        self.append(menuitem)
        self.show_all()
        self.popup( None, None, None, None, 0, 0)

    def on_menuitem(self, widget, data=None):
        display = Gdk.Display.get_default()
        clipboard = Gtk.Clipboard.get_for_display(display, Gdk.SELECTION_CLIPBOARD)
        clipboard.set_text("Sucsess", -1)
        Gtk.main_quit()

ClipboardTest()
Gtk.main()

これなら上手くいく、やはり一旦メインループを回す必用があるみたい。
つまりループ無しだと set_text に NULL ポインタが入ってしまうのかな。
とにかく挙動は解ったので内部の細かいことはイイや(ぉい!

しかし gtk_main_quit を呼ぶ必用があるのでコードが冗長だ。
メニューを出さなくても呼べる何か上手い手段は、、、

そうだ GtkApplication があるじゃないか。

#!/usr/bin/env python3

from gi.repository import Gtk, Gdk

"""
    Put the text to clipboard.
"""

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)
        self.connect("activate", self.on_activate)
        
    def on_activate(self, data=None):
        display = Gdk.Display.get_default()
        clipboard = Gtk.Clipboard.get_for_display(display, Gdk.SELECTION_CLIPBOARD)
        clipboard.set_text("Success", -1)

app = App()
app.run(None)

GtkApplication ならこんなにスッキリなコードに。
クリップボードにスパッと特定文字列を入れるコマンド程度なら簡単に作れるね。

**********

そういうことなら minipoli の Linux バージョンもイケそう。
なんて思ったけど。

ctrl_c

Nautilus で Ctrl+C して Editor で Ctrl+V すると実はフルパス貼り付けができる。
gnome-terminal に DnD する、そこから Ctrl+Shift+C なんて手段もあるし。
GNOME って便利すぎ、デフォルトでコレだもん。
たまに Windows を使うと何も出来なくてイラッとするのは私だけなのか?

何より GetAsyncKeyState に相当する API が Gtk には無いようだ。
minipoli の移植は作るだけムダだしムリっぽいかな。
でも Palepoli 復活という手もあるな、誰も覚えていないだろうけど。

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
指定のカテゴリが複数ある場合は個別だとメインに残るということなのね。
これじゃ全部かデフォルト以外は選べない、あまり意味がない。