Fedora」タグアーカイブ

Original Webkit Browser @ Fedora 15

HTML5 の勉強、その前に。
WebKit 自作ブラウザが不便すぎ、もう少しまともな形にしよう。

チョロミウム入れようかな、とも思うけど表示テスト以外で使わないだろうし。
テスト用なんだからとにかく最小限機能だけ付けて軽いほうがいいし。
しかし Fedora の Firefox は何故こんなに起動が遅いのやら…
だから Opera が手放せないんだよ、よく落ちるけど。

/usr/share/doc/pywebkitgtk-1.1.8/demos/browser.py
のコードはまだ GTK2, pywebkit のまま、でも参考にはなる。
本格的に作るならキャッシュ指定や Flash, JAVA の利用処理とかが必要になる。
今はワカンネエ。

WebKitGTK+ Reference Manual

仕様を決める、メニューはリロードだけあれば充分かな。
アドレスバーに打ち込んで Enter でも表示できるようにして。
HTML ファイルをドロップの受け入れは WebKit.WebView が勝手にやってくれる。
読み込んだらタイトルバーにタイトルを入れて、ロード中なんかも表示して。
後はリンク先をステータスバーに表示する、こんな仕様で充分だろう。

まてよ?
GtkApplication で動かす場合 Quit 処理はどうするんだろう?
Gtk.main() でのループではないので Gtk.main_quit() はおかしいよな。

GtkApplication

gtk_application_remove_window に自分のポインタを入れればいいってことかな。

def on_quit(self, widget, data=None):
    app = self.get_application()
    app.remove_window(self)

これで終了してくれた、やっぱり Delphi の TApplication もどきだね。
gtk_window_close(GtkWindow) 関数なんてものを用意すると勘違いする人が出そうだしこれでいいだろう。

とりあえず作ってみた。
タブを付けたいところだけどココでは最小限の IE6 スタイルで。

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

import sys
from gi.repository import WebKit, Gtk, Gio, Gdk

menu_xml = """<ui>
    <menubar name="MenuBar">
        <menu action="file">
            <menuitem action="reload"/>
            <separator/>
            <menuitem action="quit"/>
        </menu>
    </menubar>
</ui>"""

class WebKitWindow(Gtk.Window):
    """
        WebKit PyGi Version
        from Fedora 15 x86_64
    """
    def __init__(self):
        # Window
        Gtk.Window.__init__(self)
        self.resize(640, 480)
        self.set_title("WebKit")
        # GtkUIManager and GtkAccelGroup
        self.uimanager = Gtk.UIManager()
        accelgroup = self.uimanager.get_accel_group()
        self.add_accel_group(accelgroup)
        # GtkActionGroup
        self.actiongroup = Gtk.ActionGroup("seeme_menu")
        # GtkActionEntry
        self.ac = [ ("reload", Gtk.STOCK_REFRESH, "_Reload", "F5", "Reload", self.on_reload),
                    ("quit", Gtk.STOCK_QUIT, "_Quit", "<Control>Q", "Quit", self.on_quit),
                    ("file", None, "_File") ]
        self.actiongroup.add_actions(self.ac)
        self.uimanager.insert_action_group(self.actiongroup, 0)
        self.uimanager.add_ui_from_string(menu_xml)
        # menubar
        menubar = self.uimanager.get_widget("/MenuBar")
        # entry
        self.entry = Gtk.Entry()
        self.entry.connect("activate", self.on_entry_activate)
        # WebKit
        self.webview = WebKit.WebView()
        #self.webview.load_uri(HTML_URI)
        self.webview.connect("load-started", self.on_load_started)
        self.webview.connect("load-finished", self.on_load_finished)
        self.webview.connect("title-changed", self.on_title_changed)
        self.webview.connect("hovering-over-link", self.on_hovering_over_link)
        # ScrollWindow
        sw = Gtk.ScrolledWindow()
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.add(self.webview)
        # statusbar
        self.statusbar = Gtk.Statusbar()
        # box
        vbox = Gtk.VBox()
        vbox.pack_start(menubar, False, True, 0)
        vbox.pack_start(self.entry, False, True, 0)
        vbox.pack_start(sw, True, True, 0)
        vbox.pack_start(self.statusbar, False, True, 0)
        self.add(vbox)
        self.show_all()

    def on_load_started(self, webview, frame):
        self.statusbar.push(0, "Loading...")

    def on_load_finished(self, webview, frame):
        self.statusbar.push(0, "")

    def on_title_changed(self, webview, frame, title):
        self.set_title("{0} - WebKit".format(title))
        self.entry.set_text(webview.get_uri())

    def on_hovering_over_link(self, webview, title, uri):
        if uri:
            self.statusbar.push(0, uri)
        else:
            self.statusbar.push(0, "")

    def on_entry_activate(self, entry):
        self.webview.load_uri(entry.get_text())

    def on_reload(self, widget, data=None):
        self.webview.reload()

    def on_quit(self, widget, data=None):
        app = self.get_application()
        app.remove_window(self)

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="apps.test.webkit",
                flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_activate)
        
    def on_activate(self, data=None):
        l = self.get_windows()
        if l:
            return
        w = WebKitWindow()
        w.set_application(self)
    
if __name__ == "__main__":
    app = App()
    app.run(sys.argv)

リンク先をステータスバーに表示した後で消す方法に迷ったけど上記でイケた。
リンクをマウスカーソルが外れると uri に NULL がセットされて送られるようです。
GtkApplication 関連以外はほとんど PyGtk と同じでイケます。

どうでもいいけど。
GTK+ 関連のアクセスがドイツやスウェーデンといったヨーロッパからばかりなんだが…
アッチの google で検索してココがヒットするのも変な気分、どのくらい読解できるやら。
まあ私も GTK+ 関連は日本語では探さないんですけどね。

GLib.utf8_collate_key_for_filename

我がアプリの GTK+ 3 化を進めていく予定であったのだが…
Gst 関連がどうしても上手くいかなくて更新が止まっている…
clipoli も xpm を読み込む方法がイマイチ解らない…

とにかく解った部分から書き出していこう。
そういえばコレは PyGI からなら呼び出しできるのかな?

utf8_collate_key | PaePoi

GLib で普通に g_utf8_collate_key_for_filename が見つかった。
よし試してみよう。

from gi.repository import GLib

l = [
"40.txt",
"200.txt",
"001.txt",
"02.txt",
"03.txt",
"123456.txt"]

l.sort(lambda x, y : cmp(GLib.utf8_collate_key_for_filename(x, -1), GLib.utf8_collate_key_for_filename(y, -1)))

for s in l:
    print s

''' output
001.txt
02.txt
03.txt
40.txt
200.txt
123456.txt
'''

うん Nautilus 名前順ソートと同じ数値優先順になるね。
もう ctypes という危なっかしいものを使わなくて済みそう。

しかし今更ながら GLib って色々できるんだね。
たとえば日本語を含む UTF-8 文字列の長さを得る関数もある。
(バイト数では日本語は 2〜3 byte になる)
(そうそう、max バイト数未指定の場合は -1 を指定する)

Unicode Manipulation

from gi.repository import GLib
GLib.utf8_strlen("ちゆ12さい", -1) #=> 6L
len("ちゆ12さい") #=> 14

Windows API もこんな感じだったらもっと面白かったのにな。

Python with GTK+ 3 Gst

そろそろ Y901x の GTK+ 3 化の準備。
gi.repository に Gst があるからコチラを使えばいいのかな。

GStreamer Application Development Manual (0.10.35.1)

0.10 だから別に変わっていないんだが PyGtk と同じには書けないし。
前回 Drag & Drop をやったので DnD されたファイルを再生できるのみを。

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

import sys
import gi
try:
    gi.require_version("Gtk", "3.0")
except:
    print "This Program is GTK+ 3.0 or later."
    sys.exit()

from gi.repository import Gst, Gtk, Gio, Gdk

class Win(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # DnD
        dnd_list = Gtk.TargetEntry.new("text/uri-list", 0, 0)
        self.drag_dest_set(
                Gtk.DestDefaults.MOTION |
                Gtk.DestDefaults.HIGHLIGHT |
                Gtk.DestDefaults.DROP,
                [dnd_list],
                Gdk.DragAction.MOVE )
        self.drag_dest_add_uri_targets()
        self.connect("drag-data-received", self.on_drag_data_received)
        # Gst
        Gst.init(sys.argv)
        self.player = Gst.ElementFactory.make("playbin2", "player")
        # self
        self.set_title("movie player")
        self.show_all()

    def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
        uris = data.get_uris()
        self.player.set_property("uri", uris[0])
        self.player.set_state(Gst.State.PLAYING)

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="apps.test.gst",
                flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_activate)
        
    def on_activate(self, application, user_data=None):
        Gdk.threads_init()
        w = Win()
        w.set_application(self)

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

これで動画なら別ウインドウが出て再生できるだけのアプリ完成。
ほとんど変わっていなかった、けどやはり Gst.init() で初期化が必要になる。
PyGtk が親切すぎただけなんですけど。

しかし肝心なシグナルのハンドラで困った。

Bus

bus.add_watch なんてアトリビュートは無いんだが…
しかたがないから一番下の書き方をしてもエラーだし…

#bus.connect("message::state-changed", self.gst_on_message) # Error
bus.connect("message", self.gst_on_message)

#####

def gst_on_message(self, bus, message):
    print message # all messages are None

以前の手段では全部 None になってしまう…

python – GStreamer bus sends None message – Stack Overflow

わーい、同じことで詰まっている人を見つけた。
ってどうすりゃいいんだ…
ヤケクソだ、PyGI じゃない以前の gst を使ってやる。

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

import sys
import gi
try:
    gi.require_version("Gtk", "3.0")
except:
    print "This Program is GTK+ 3.0 or later."
    sys.exit()

from gi.repository import Gtk, Gio, Gdk
import gst

class Win(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # DnD
        dnd_list = Gtk.TargetEntry.new("text/uri-list", 0, 0)
        self.drag_dest_set(
                Gtk.DestDefaults.MOTION |
                Gtk.DestDefaults.HIGHLIGHT |
                Gtk.DestDefaults.DROP,
                [dnd_list],
                Gdk.DragAction.MOVE )
        self.drag_dest_add_uri_targets()
        self.connect("drag-data-received", self.on_drag_data_received)
        # old Gst
        self.player = gst.element_factory_make("playbin2", "player")
        bus = self.player.get_bus()
        bus.add_signal_watch()
        bus.enable_sync_message_emission()
        bus.connect("message", self.gst_on_message)
        # self
        self.set_title("movie player")
        self.show_all()

    def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
        uris = data.get_uris()
        self.player.set_property("uri", uris[0])
        self.player.set_state(gst.STATE_PLAYING)

    def gst_on_message(self, bus, message, user_data=None):
        print message # OK

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="apps.test.gst",
                flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_activate)
        
    def on_activate(self, application, user_data=None):
        Gdk.threads_init()
        w = Win()
        w.set_application(self)

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

よし、コレなら問題ない。
しかしこの GStreamer バインディングを今後も残してくれるのだろうか?
やはり PyGI を使ったほうがいいよな…
もう少し勉強します。

Python with GTK 3 Drag and Drop

今回は Python with GTK+ 3 で Drag and Drop をやってみる。
といってもファイルドロップで URI を得るというよく利用するものだけだが。

Drag and Drop

何か色々と細かくなったような気がするけどあまり変わっていなかった。
drag_dest_set() で text/uri-list を指定するだけでドロップ受け入れ処理は終わり。

ただし GtkTargetEntry は new で作成し list にして渡す必要があった。
PyGtk の場合はタプルのリストで良かったので少しだけ面倒くさくなった。
やはり C で書くのとも PyGtk で書くのとも微妙に違っていて迷う。

ところで drag-data-received シグナルのハンドラなんだが

def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
    """
    PyGtk
    if "\r\n" in data.data:
        uris = data.data.split("\r\n")
    else:
        uris = data.data.split("\n")
    """
    # Python with GTK+ 3
    uris = data.get_uris()

と get_uris() だけで list が得られるようになっている。
これで送られてくる uri-list の改行コードを気にしなくて済む。

それと前やった時には何故か気がつかなかったのだけど
GtkApplication にセットする方法なら delete-event 処理は不要なんだね。
Delphi の TApplication のように全ての Window を閉じた時点で終了するようだ。

ついでに以前 PyGtk でやった content-type を得る方法も入れてと。
というか、ソレらをやらないと Y901x の GTK3 化ができない。

後は GTK+ 3 に合わせて書き換えていく。

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

import sys
import gi
try:
    gi.require_version("Gtk", "3.0")
except:
    print "This Program is GTK+ 3.0 or later."
    sys.exit()

from gi.repository import Gtk, Gdk, Gio

class Win(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # DnD
        dnd_list = Gtk.TargetEntry.new("text/uri-list", 0, 0)
        self.drag_dest_set(
                Gtk.DestDefaults.MOTION |
                Gtk.DestDefaults.HIGHLIGHT |
                Gtk.DestDefaults.DROP,
                [dnd_list],
                Gdk.DragAction.MOVE )
        # GtkLabel
        self.label = Gtk.Label("Please drop your files")
        self.add(self.label)
        # Gtk.Window
        self.drag_dest_add_uri_targets()
        self.connect("drag-data-received", self.on_drag_data_received)
        self.set_title("dnd_type")
        self.show_all()

    def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
        uris = data.get_uris()
        s = ""
        for uri in uris:
            f = Gio.file_new_for_uri(uri)
            info = f.query_info(
                    Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
                    Gio.FileQueryInfoFlags.NONE,
                    None)
            content_type = info.get_content_type()
            s += "URI={0}\nContent Type={1}\n".format(uri, content_type)
        self.label.set_text(s)

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="apps.test.trash",
                flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_activate)
        
    def on_activate(self, application, user_data=None):
        w = Win()
        # Gtk.main_quit is Do not Need
        w.set_application(self)
    
if __name__ == "__main__":
    app = App()
    app.run(sys.argv)

情報が少ないなりにマシになったと思う。
日本語で検索してもまったく情報が見当たらないんだが需要はそんなに無いのかな…

**********

CNN.co.jp:ユーザーのIQが高いブラウザーは? ブラウザー別IQテスト

比較的どころかまったく人気が無い Linux 版 Opera な私は IQ が極めて高い。
わけが無い、工業高校機械化卒な現場のオッサンだもの。

PyGI GTK+ Version

PyGI – GNOME Live!

で Gtk+ 等のバージョン振り分け方法を今頃知った。

Get GTK+ theme colors with python and GObject introspection – Stack Overflow

とにかく gi.repository は(我が fedora は 64bit)
/usr/lib64/girepository-1.0
というディレクトリにある *.typelib というファイル群である。

Ubuntu 11.04 デフォルトには Gtk-3.0.typelib は無い。
Gtk+3.0 自体が無いのだから当然だが、最新なのに古臭さを感じるよ。

pygobject – Python bindings GLib and GObject

この demos にあるサンプルは GtkDrawingArea の draw シグナルを使っているが…
Gtk+ 2 になる Ubuntu とかじゃ例外になるはずなんだがいいのかな。

import gi
gi.require_version('Gtk', '2.0')
from gi.repository import Gtk

とやれば Fedora 15 でも Gtk2 になる。
そんなことする人いないだろうけど Ubuntu 11.04 対応を Fedora 15 から確認なんかに使えるな。
Gtk2 を使うなら枯れた PyGtk のほうが楽だもの、サンプルコードはゴロゴロあるし。

import sys
import gi
try:
    gi.require_version("Gtk", "3.0")
except:
    print "This Program is GTK+ 3.0 or later."
    sys.exit()

Gtk+ 3 専用の場合はこうするのが賢明かな。