Python」タグアーカイブ

Python3 urllib

今日 Python3 で URI デコードをやろうとした。

Python 3 Porting Guide ? Porting to Python 3 v1 documentation
Python2 との違い一覧を作ってくれている人がいる、アリガトメルシー。
urllib.unquote は urllib.parse.unquote なのね。

#!/usr/bin/env python3

import urllib
print(dir(urllib))

''' output
['__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', '__loader__', '__name__', '__package__', '__path__']
'''

import urllib.parse
print(dir(urllib))

''' output
['__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', '__loader__', '__name__', '__package__', '__path__', 'parse']
'''

なんだこりゃ…

quote

当然そうなるよな、何故だ?
/usr/lib64/python3.3/urllib
を覗いてみると

urllib_zerobyte

__init__.py が 0 byte だとこういう動作になるってことなのか。
なるほど、多分どこかでこの知識が役に立つだろう。

#!/usr/bin/env python3

import urllib.parse
from gi.repository import GLib

uri = "file:///path/to/My%20Waifu.jpg"

path = urllib.parse.unquote(uri)
print(path) #=> file:///path/to/My Waifu.jpg

path, host = GLib.filename_from_uri(uri)
print(path) #=> /path/to/My Waifu.jpg

ただ unquote だと file:/// とかは普通に残るんだよな。
ホスト名があった場合(LAN 上ファイル等)も面倒だし。
Linux なら GLib を使ったほうがいいや。
こんな楽な手段を知っているので Python モジュールの知識が全然無いというのも考え物だな。

Python ** Asterisk

PyGI のオブジェクトは作成時の引数にて

#!/usr/bin/env python3

from gi.repository import Gtk

win = Gtk.Window(title="Test", default_width=500)
win.connect("delete-event", Gtk.main_quit)
win.show()
Gtk.main()

というように property を引数で指定できる。
今まであまり気にしていなかったけど、つまりこういうことだと気が付いた。

#!/usr/bin/env python3

from gi.repository import Gtk

class Win (Gtk.Window):
    def __init__(self, **args):
        #Gtk.Window.__init__(self, **args)
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # Own Property set
        for key, value in args.items():
            self.set_property(key, value)
        self.show()

Win(title="Test", default_width=500)
Gtk.main()

アスタリスク二つだと未定義キーワードの引数を受け取れるのは有名かと。
未定義キーワードは文字列になり辞書として扱える。
こういう本当に有用な形で実装されると「うわー便利!」と実感できますね。
今まで使い道が解らなかっただけだったりするけど。

こんなに便利なら IronPython でも同様にしてやろうと思ったけど…

import wpf
from System.Windows import *

win = Window(Title="Titlebar", Width=300, Height=100)
app = Application()
app.run(win)

既に実装されていた。
IronPython の開発者恐るべし。

g_idle_add

Gedit for Windows part3 | PaePoi
を少し改造しようと調べている時に g_idle_add という GLib の関数を知った。
筆者は何も知らないな、もっと勉強しなければいけないようだ。

何も処理を行っていないアイドル時にシグナルを送りつける。
ハンドラにて True を戻すとループ継続、False を戻すとループが止る。
g_timeout_add と同様みたい、ちとテスト。

#!/usr/bin/env python3

from gi.repository import Gtk, GLib

class IdleTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        self.label = Gtk.Label("0")
        button = Gtk.Button.new_with_label("Time consuming process")
        button.connect("clicked", self.on_clicked)
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(self.label, False, False, 0)
        vbox.pack_start(button, False, False, 0)
        self.add(vbox)
        self.show_all()
        # var
        self.count = 0
        # Idle
        GLib.idle_add(self.on_idle)

    def on_idle(self):
        if self.count == 1000000:
            return False
        self.count += 1
        self.label.set_text("{0}".format(self.count))
        return True

    def on_clicked(self, widget, data=None):
        s = ""
        for i in range(10000000):
            s += "homura"

IdleTest()
Gtk.main()

g_idle_add1

ボタンを押して何か処理を行っている最中は見事にシグナルは停止する。
なるほど、同一プロセスで何もしていない時だけシグナルが発生するのか。

#!/usr/bin/env python3

from gi.repository import Gtk, GLib

class IdleTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        self.label1 = Gtk.Label("0")
        self.label2 = Gtk.Label("0")
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(self.label1, False, False, 0)
        vbox.pack_start(self.label2, False, False, 0)
        self.add(vbox)
        self.show_all()
        # var
        self.count1 = 0
        self.count2 = 0
        # Idle
        GLib.idle_add(self.on_idle)

    def on_idle(self):
        if self.count1 == 100000:
            return False
        if self.count1 == 50000:
            GLib.idle_add(self.on_idle2)
        self.count1 += 1
        self.label1.set_text("{0}".format(self.count1))
        return True

    def on_idle2(self):
        if self.count2 == 100000:
            return False
        self.count2 += 1
        self.label2.set_text("{0}".format(self.count2))
        return True

IdleTest()
Gtk.main()

2 つ作成しても別スレッドとして動作するみたいですね。
上記のような使い方は CPU 負荷が凄いのでヤメたほうがいいと一応。

これがどういう時に便利かというと簡易スレッド分離。
ランチャを作成し、起動したことを書き出す。
そして終了コードを調べて表示したい等とする。

#!/usr/bin/env python3

from gi.repository import Gtk
import subprocess

class IdleTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        view = Gtk.TextView()
        view.set_size_request(200, 100)
        self.buf = view.get_buffer()
        button = Gtk.Button.new_with_label("gnome-calculator")
        button.connect("clicked", self.on_clicked)
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(view, True, True, 0)
        vbox.pack_end(button, False, False, 0)
        self.add(vbox)
        self.show_all()

    def on_clicked(self, widget, data=None):
        self.buf.set_text("Do gnome-calculator\n\n")
        #
        it = self.buf.get_end_iter()
        retcode = subprocess.call(["gnome-calculator"])
        if retcode == 0:
            self.buf.insert(it, "Sucsess")
        else:
            self.buf.insert(it, "Error: {0}".format(retcode))

IdleTest()
Gtk.main()

動きそうな気がするけど実際は on_clicked を抜けるまで何も書き出されない。
なので on_clicked にて g_idle_add を入れてスレッドを別にする。

#!/usr/bin/env python3

from gi.repository import Gtk, GLib
import subprocess

class IdleTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        view = Gtk.TextView()
        view.set_size_request(200, 100)
        self.buf = view.get_buffer()
        button = Gtk.Button.new_with_label("gnome-calculator")
        button.connect("clicked", self.on_clicked)
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(view, True, True, 0)
        vbox.pack_end(button, False, False, 0)
        self.add(vbox)
        self.show_all()

    def on_clicked(self, widget, data=None):
        self.buf.set_text("Do gnome-calculator\n\n")
        GLib.idle_add(self.on_idle)

    def on_idle(self):
        it = self.buf.get_end_iter()
        retcode = subprocess.call(["gnome-calculator"])
        if retcode == 0:
            self.buf.insert(it, "Sucsess")
        else:
            self.buf.insert(it, "Error: {0}".format(retcode))
        return False

IdleTest()
Gtk.main()

g_idle_add2

これなら on_clicked は抜けランチャの終了待機も問題なく行われる。
つまりいつ終るか解らない処理を待つ必要がある場合に利用できる。
こんな感じで簡易なスレッドが必要な場合はもうコレで充分ですよね。

Wnck

Libwnck Reference Manual
を試していて面白いことをみつけた。

#!/usr/bin/env python3

from gi.repository import Wnck

screen = Wnck.Screen.get_default()
''' output
(process:5006): Gdk-CRITICAL **: ...
'''

となる、おいおいどういうことだよ。

Part?II.?Getting Started with libwnck
に書いてある Python コードをそのまんま書いたのに。
下のほうにある C のコードを見ると

gdk_init (&argc, &argv);

とあるので Gdk.init(sys.argv) が必要なのかな?
なんて思って試してみると

#!/usr/bin/env python3

from gi.repository import Wnck, Gdk

screen = Wnck.Screen.get_default()

# Screen Size
x = screen.get_width()
y = screen.get_height()
print("Screen @ {0}x{1} pixel".format(x, y))

''' output
Screen @ 1920x1080 pixel
'''

なんだよ Gdk の import だけで普通に動く、使っていないのに。
そういえば Gtk を使う場合も init を呼ばずに使えている。
PyGI では Gdk の import だけで初期化は完了するということなのね。
Gtk の import でも同様になるみたい。

更に

#!/usr/bin/env python3

from gi.repository import Wnck, Gdk

screen = Wnck.Screen.get_default()

win = screen.get_active_window()
print(win.get_name())

''' output
AttributeError: 'NoneType' object has no attribute 'get_name'
'''

None って、やはりこれも C のコード同様に

#!/usr/bin/env python3

from gi.repository import Wnck, Gdk

screen = Wnck.Screen.get_default()

# Important
screen.force_update()

win = screen.get_active_window()
print(win.get_name())

WnckScreen を作成しただけでは動的な状況を把握できないようです。
しかし下のサンプルコードではこんな関数を呼んでいない。
mainloop を回すなら update は不要ということみたい。

しかし面白いことに

#!/usr/bin/env python3

from gi.repository import Gdk

screen = Gdk.Screen.get_default()

win = screen.get_active_window()
w = win.get_width()
h = win.get_height()
print("Active Window @ {0}x{1} pixel".format(w, h))

こっちだと active_window が得られる、よくわかんねぇ!
WnckScreen と GdkScreen は用途がまったく違うから実際の混乱は無いだろうけど。
Wnck はスクリーンの監視等が主な役目。
ということでサンプルコードの下側を GUIに改造してみた。

#!/usr/bin/env python3

# https://developer.gnome.org/libwnck/stable/getting-started.html
# to PyGI Window

from gi.repository import Wnck, Gtk

class ScreenMonitoring(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        # Wnck
        screen = Wnck.Screen.get_default()
        screen.connect("window-opened", self.on_window_opened)
        screen.connect("active-window-changed", self.on_active_window_changed)
        # View
        view = Gtk.TextView()
        self.buf = view.get_buffer()
        sw = Gtk.ScrolledWindow()
        sw.add(view)
        # Label
        self.label = Gtk.Label("active:")
        # Pack
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(sw, True, True, 0)
        vbox.pack_start(self.label, False, False, 0)
        self.add(vbox)
        self.resize(500, 200)
        self.show_all()

    def on_window_opened(self, screen, window, data=None):
        appname = window.get_application().get_name()
        title   = window.get_name()
        it = self.buf.get_end_iter()
        self.buf.insert(it, "{0}: {1}\n".format(appname, title))

    def on_active_window_changed(self, screen, previously_active_window, data=None):
        active_win = screen.get_active_window()
        if (active_win):
            self.label.set_text("active: {0}".format(active_win.get_name()))
        else:
            self.label.set_text("no active window")

ScreenMonitoring()
Gtk.main()

wncktest

機動後にアプリを立ち上げたりアクティブ状態を替えたりすると即座に反応する。
実はこのとき通知領域に clipoli を置いていたのだが認識しなかった。
Google Chrome は最小化していたけど認識、なるほど。

デスクトップはデスクトップというウインドウとして認識するのね
あと画像を見れば判るだろうけど筆者はデスクトップは desktop に変名している。
でも[デスクトップ]になる、Nautilus のサイドバーも変わらないしなんだろう?
Nautilus は[ファイル]にならないのに Eye of GNOME は[画像ビューアー]って何だよ。
Python アプリは get_application で python とはならないのか。

wnck_lubuntu

Lubuntu でも動くけど何故か Wnck が Warnimg を出しまくる。
desktop はキチンと変名、いやこのとき PCManFM で desktop を開いていた。
pcmanfm: pcmanfm がデスクトップのようだ、なんだこれ…
しかも何故か lxpanel を認識、GNOME と全然違うじゃないの。

なんかディストリビューションどころか環境ごとにバラバラになりそう。
上手く利用しないと Wnck はドハマリしそうな予感。

Lubuntu 13.10 GStreamer and cairo

Lubuntu で PyGI から GStreamer を使うには

Ubuntu ? パッケージのファイル一覧: gir1.2-gstreamer-1.0/quantal/amd64
Ubuntu ? パッケージのファイル一覧: gir1.2-gst-plugins-base-1.0/raring/amd64

を Synaptic から導入すればいい。

lubuntu_gst_and_gstaudio

と思ったけど Python3 から利用すると cairo 関連パッケージが足りない。
コードで利用していなくても PyGI が利用するのでエラーになる。
というか Python2 でも足りないので cairo での描写ができないんですけど…

もう Canonical が Python3 に移行しろと言っているので Python2 は無視して。

python3-cairo
python3-gi-cairo
libpython3.3

も必要、この 3 つがないと cairo で何も描写できない。
線や図形の描写 – L’Isola di Niente

lubuntu_cairo

んで肝心の GStreamer デコーダー(プラグイン)はインストール時に
「サードパーティーうんたら」のチェックで全部入るはずなんだが
libav, bad, base, ugly があるのに何故か good が無い。

gstreamer1.0-plugins-good (gstreamer1.0-x が依存で入る)

も必要、一番肝心なプラグインが無いってどうよ。
コレが無くても PCManFM や MPlayer では問題ないんですけどね。
Nautilus や Totem が GStreamer を使う素の Ubuntu はキチンと入るのか?

これで Lubuntu 13.10 にて Python から cairo, GStreamer を使う環境完成。
皆軽さで LXDE を選んでいるはずだから最小限にしたいですよね。
アプリの依存関係で既に入っている場合もあるよと一応。

urusai

何故かソケットエラーが出るけど再生自体は問題なく可能。
2D 描写しかできない Lubuntu でもなんとか開発はできそう。

#############################################

それと LXDE 等の GTK2 ベースデスクトップだと
/usr/share/pixmaps
にアイコンを入れないとインストールで認識しないのねん。
忘れていた、うーんどうしよう。