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

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
にアイコンを入れないとインストールで認識しないのねん。
忘れていた、うーんどうしよう。

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 復活という手もあるな、誰も覚えていないだろうけど。