月別アーカイブ: 2009年5月

GtkTextView って内部は UNICODE なのかな

ちょっと gEdit のプラグインを弄くっていたのでありますが。

13.3. Text Buffers

を見てカーソル位置の GtkTextIter を得て後は多分コレでイケるだろうと作っていた。
上手くいったからイイのだろう、で、面白いことを見つけた。

def on_hoge_activate(self, action):
    # バッファを得る
    view = self._window.get_active_view()
    buf = view.get_buffer()
    try:
        # カーソル位置の GtkTextIter 取得
        pos = buf.get_property("cursor-position")
        it = buf.get_iter_at_offset(pos)
        # 一つ前の文字を得る
        it1 = buf.get_iter_at_offset(pos-1)
        s = it1.get_char()
        if s == "\n":
            return
        # 必要な変数を初期化してループ
        count = pos
        itnext = None
        itend = buf.get_end_iter()
        s1 = ""
        while 1:
            # 次の GtkTextIter を得る
            count += 1
            itnext = buf.get_iter_at_offset(count)
            # EOF
            if itnext.equal(itend):
                break
            # 文字を得て比較
            s1 = itnext.get_char()
            #
            # 表示してみる
            print s1
            #
            if s1 == "\n" or s1 == "。":
                break
            elif s == s1:
                # ここまでの範囲を print
                itnext = buf.get_iter_at_offset(count)
                text = it.get_text(itnext)
                print text
                break
    except Exception, s:
        self.messagebox("%s" % s)

とまあ説明しやすく?書き換えたけどカーソル位置から特定範囲の文字列を得る。
具体的にはカーソルの直前文字と同じ文字を見つけたら抜き出すコードです。

これでイイの?
だって Ubuntu って UTF-8 だよ、アルファベットは 1 バイト、日本語は 2?3 バイト。
というか Windows で文字列を弄くる時に絶対に問題になるんですよ文字コードは。
いやまて、get_char() で char を取り出したって結局はポインタなんだし。
でも漢字とか混ぜると破壊されるわけで、内部はどうなるんだろう?

ということで上のように途中で char を抜いた部分にて print してみることにした。
アルファベットとひらがなカタカナ漢字記号を混ぜた文を作って実験。

atom

あれ、char 配列になっていないじゃん。
_ismbslead みたいなことをしているのか内部で UNICODE 化しているか知らないけど.。
これは楽だ、 gEdit プラグインでは UTF-8 であることを意識しないでいいようです。

どうでもいいけど char って character の略なので「キャラ」と読みますお。

覚書

結局覚書ページの Python 関連は新しく PyGtk として作ることにした。

覚書のページ – L’Isola di Niente

GtkUIManager のほうをさっきまで作っていたけどパッキングの説明のほうが先だよなぁ普通。
ということで書き直ししている、解説する必要があるかどうかよく解らないけど。

こういうまとめを他に誰か、せめて大手が作らないのかなぁ。
今週の週刊アスキーも Ubuntu が載っていたがソフトウエアの紹介だけだし。
Windows と同じことしかしないのなら Linux は使いにくい Windows でしかないのに。
PyGtk もデフォルトで入っているんですよ。

Compiz と DWM は結果が同じ

Y901x 0.1.5 公開しました。

で、前回困っていた動画の背景を黒くする処理ですけど。
メソッドからは塗りつぶしができないので expose-event を処理して GDK で塗った。

def on_aframe_expose(self, widget, event):
    gc = widget.style.fg_gc[gtk.STATE_NORMAL]
    gc.set_foreground(gtk.gdk.Color(0,0,0))
    widget.window.draw_rectangle(gc, True, event.area.x, event.area.y,
                                    event.area.width, event.area.height)

なんか Windows でデバイス・コンテキストを使うのと同じ感覚。
ペンや色の情報はグラフィック・コンテキストが持っているってことですよね。
GTK+ から色の変更をする関数も実はコレをラッピングしているだけなんだろうきっと。
つまりは GDI+ のように、とか考えていたら気になった。

Linux ではこれはどうなるのだ?

ぱぇぽぃ2 ? Blog Archive ? Aero の TextOut

Compiz は言うまでもなく Aero と同じようにタイトルバーが透けたりとかする。
ウインドウをマウスで掴んでグルグルとかでは 3D のほうが CPU 負荷が極端に少ない。
ということは 3D 状態では DWM 有効時と同じ結果になるのであろうか?
2D 描写と 3D 描写でどうなるか今回は円を描写して実験。

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

import gtk

class Wm_Paint(gtk.Window):
    """
        ボタンクリックで円を描く最低なコード
    """
    def __init__(self):
        gtk.Window.__init__(self)
        # DrawingArea
        self.drawing_area = gtk.DrawingArea()
        self.drawing_area.set_events(gtk.gdk.EXPOSURE_MASK | 
                                    gtk.gdk.STRUCTURE_MASK | 
                                    gtk.gdk.VISIBILITY_NOTIFY_MASK )
        self.drawing_area.connect("expose-event", self.on_paint)
        # GtkButton
        button = gtk.Button("クリック")
        button.connect("clicked", self.on_button_click)
        # pack
        vbox = gtk.VBox()
        vbox.pack_start(button, False)
        vbox.pack_start(self.drawing_area)
        self.add(vbox)
        # いつもの処理
        self.connect("delete-event", gtk.main_quit)
        self.resize(320, 150)
        self.show_all()

    def on_button_click(self, widget, event=None):
        # GetDC()
        gc = self.drawing_area.style.fg_gc[gtk.STATE_NORMAL]
        # window メンバが GdkWindow
        self.drawing_area.window.draw_arc(
            gc,    # グラフィックコンテキスト
            True,  # 塗りつぶすなら True
            0,     # x 座標
            0,     # y 座標
            self.drawing_area.allocation.width,  # width
            self.drawing_area.allocation.height, # height
            0,     # 開始位置、3時方向の位置から反時計回りに 1/64度刻みで指定
            64*360) # 64 の倍数が描写する円の角度
        #pass

    def on_paint(self, widget, event):
        # 本当はココに書く、てか widget パラメータが使える
        """gc = widget.style.fg_gc[gtk.STATE_NORMAL]
        widget.window.draw_arc(
            gc,
            True,
            0,
            0,
            widget.allocation.width,
            widget.allocation.height,
            0,
            64*360)"""
        pass

if __name__ == "__main__":
    w = Wm_Paint()
    gtk.main()

と、ボタンを押したら Window いっぱいに円を描写するだけの GUI アプリを作る。
コレを起動させ、上に何かウインドウを少し被せてみたら描写した円はどうなるか。
まず Compiz で 3D 有効状態は…やはり Vista の DWM 有効時と同じで何も起こらない。

次に 2D にして同じようにやってみる。

paint1

paint2

あーあ、結局 X も基本描写関連は Windows とそんなに変わらないということだわ。
Compiz の 3D 表示も Vista の DWM と同じ結果になると考えておけばよさそうだ。

3D 描写がもう少し普及したら勘違いしてしまう人が続出しないだろうか?
この手の情報が圧倒的に少ない Linux から開発に入ると理解に苦しむだろうなと思う。
いや、Windows も既にそうなって(略

ついでにコレを試したおかげで widget に allocation メンバがあることを知った。
サイズを得るのに今まで get_allocation() していたけどこのメンバから簡単に得られた。
実践は最大の練習だと本当に思う。

GtkAspectFrame スゲェ

renamedlg プラグインを更新しました、たった一日かよ!
いや、リネーム時に拡張子を選択から省く処理がなんか上手くいったんで。
UNICODE で計算しなきゃいけないのね、てか今まで試していた手段が悪かった。

つーことで。

そろそろ Y901x にアスペクト比変更機能を追加せねばなるまい。
そう思って色々調べてみたので覚書しておきます。

変更させるには2通り手段がある。
この比率で表示してね!と GStreamer に伝えてやってもらう、方法があるかは今は解らない。
もう一つは自分で縦横位置とサイズを計算して配置する、Y901 や Cinema はこの方法。
古い Windows は絶対位置配置なので簡単だったけど Gtk+ のパッキング方法ではどうやるの?

とりあえずデフォルトのソースサイズを無視するだけなら on_sync_message で

imagesink.set_property("force-aspect-ratio", False)

とこの部分を False にすればよいだけだ。

とにかく自力で計算しても配置方法が思いつかないので GStreamer から行う方法を探す。
上の例もあるし XvImageSink から設定できないかな?

xvimagesink

mediactrl.cpp – wxWidgets/src/unix – Code Search

wxWidget には便利そうなラッパークラスがあるんだね。
んー GstPad からサイズ取得はやったし… pixel-aspect-ratio なんてのも抜けるのか。
試しにやってみたら「そんなのネェ」と例外、うーよく解らん。
こんな感じで他も探すも見当たらず,何か関係ありそうな pixel-aspect-ratio で探す。

viewer.py – pitivi-0.10.0/pitivi/ui – Code Search

あ、、、、、、、、、、こんな方法が。

てか GtkAspectFrame なんつークソ便利なものがあったんだ。
早速 force-aspect-ratio を False にして実験じゃ!

aspect

あっさりアスペクト比維持は完成、GtkAspectFrame スゲェ。
しかし背景がボタン色でダサイ、こうしたら背景を黒くする方法はどうするの?
modify_bg は GtkAspectFrame や GtkVBox には効かないみたいだし。
Eye of GNOME みたい、Totem のコードでも漁るか。

てか変更するラジオメニューも作らなきゃ、こりゃ今日中に終わりそうもない。
つーことでプラグインのみ更新、さて続きをやります。

Eye of GNOME プラグインはやっぱり面倒

Rename Dialog プラグインはなんとか完成した。
本サイトに eog-plugin 付きのを置いているけどコードも貼っておきます。
日本語が読めないのに海外からコードを探しに来る人もいるって解ったし。
コメントは日本語でいくけど。

#-*- coding:utf-8 -*-

#    Eye of GNOME renamedlg plugin version 1.0.0
#    Copyright © 2009 sasakima-nao <m6579ne998z@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.

# Eye of GNOME Documentation
# http://library.gnome.org/devel/eog/stable/index.html

import eog
import gtk
import os
import gio

ui_str = """<ui>
    <menubar name="MainMenu">
        <menu action="Edit">
            <separator/>
            <menuitem action="rename"/>
        </menu>
    </menubar>
</ui>"""

class RenameDlgPlugin(eog.Plugin):
    """
        ダイアログを出しリネームを行うプラグイン
    """
    def __init__(self):
        eog.Plugin.__init__(self)

    def activate(self, window):
        """
            window ポインタをどこかに保存するとプロセスが終了しなくなる
            GtkActionGroup.add_actions でシグナルの user_data でも駄目
            ということで F2 キーとメニューのシグナル処理は別々に作る
        """
        uimanager = window.get_ui_manager()
        # F2 キーでの処理
        accelgroup = uimanager.get_accel_group()
        accelgroup.connect_group(gtk.keysyms.F2, 0, gtk.ACCEL_VISIBLE, self.on_acc)
        # メニューを作るだけの処理
        action_group = gtk.ActionGroup("RenameActions")
        actions = [("rename", None, "リネーム(_R)", None, "リネーム", None)]
        action_group.add_actions(actions)
        uimanager.insert_action_group(action_group, 0)
        self._ui_id = uimanager.add_ui_from_string(ui_str)
        # 普通にコネクトなら window を user_dataにしても大丈夫
        m = uimanager.get_widget("/MainMenu/Edit/rename")
        m.connect("activate", self.on_rename, window)

    def update_ui(self, window):
        pass

    def deactivate(self, window):
        uimanager = window.get_ui_manager()
        uimanager.remove_ui(self._ui_id)
        accelgroup = uimanager.get_accel_group()
        accelgroup.disconnect_key(gtk.keysyms.F2, 0)
        uimanager.ensure_update()

    def on_acc(self, accelGroup, window, keyval, modifier):
        self.on_rename(None, window)

    def on_rename(self, widget, window):
        #print dir(window)
        if window == None:
            return
        img = window.get_image()
        if img == None:
            return
        fullname = img.get_uri_for_display()[7:]
        path, name = os.path.split(fullname)
        label = gtk.Label(name)
        entry = gtk.Entry()
        entry.set_text(name)
        d = gtk.Dialog( "リネーム",
                        window,
                        gtk.DIALOG_MODAL,
                        (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
                        gtk.STOCK_OK, gtk.RESPONSE_ACCEPT) )
        try:
            d.vbox.pack_start(label, False)
            d.vbox.pack_start(entry, False)
            d.show_all()
            def dlg_ok(self):
                d.response(gtk.RESPONSE_ACCEPT)
            entry.connect("activate", dlg_ok)
            # 成功かキャンセルまでループ
            while 1:
                if d.run() == gtk.RESPONSE_ACCEPT:
                    text = entry.get_text()
                    if text == name:
                        self.messagebox("変更されていません", window)
                    else:
                        if  text in os.listdir(path):
                            self.messagebox("同一ファイル名が見つかりました", window)
                        else:
                            # EogListStore から削除
                            store = window.get_store()
                            store.remove_image(img)
                            # リネームさせる
                            newname = os.path.join(path, text)
                            os.rename(fullname, newname)
                            # 一応キューを回しておく
                            while gtk.events_pending():
                                gtk.main_iteration()
                            # EogImage として登録
                            f = gio.File(newname)
                            newimg = eog.eog_image_new_file(f)
                            # EogListStore に突っ込む
                            store.append_image(newimg)
                            # EogThumbView に選択させれば EogScrollView に反映
                            tv = window.get_thumb_view()
                            tv.set_current_image(newimg, True)
                            break
                else:
                    break
        finally:
            d.destroy()

    def messagebox(self, text, window):
        dlg = gtk.MessageDialog(window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, 
                                gtk.BUTTONS_OK, text)
        dlg.set_title("Eye of GNOME")  
        r = dlg.run()  
        dlg.destroy()

こうやって貼り付けると「なんだ、簡単なんだ」思われそう。
せっかくなのでコードの整理をする前の一部分を貼り付けておきます。

store = window.get_store()
store.remove_image(img)
newname = os.path.join(path, text)
#
os.rename(fullname, newname)
while gtk.events_pending():
    gtk.main_iteration()
f = gio.File(newname)
newimg = eog.eog_image_new_file(f)
# EogListStore に突っ込む
store.append_image(newimg)
i = store.get_pos_by_image(newimg)
tv = window.get_thumb_view()
tv.set_current_image(newimg, True)
#
#view = window.get_view()
#view.set_image(store.get_image_by_pos(i))
#
# GLocalFile を得てコピーを作る
"""oldimg = img.get_file()
f = gio.File(newname)
print dir(f)"""
#newimg = eog.eog_image_new_file(f)
"""oldimg.copy(f)
newimg = eog.eog_image_new_file(f)
# EogListStore に突っ込む
store = window.get_store()
#i = store.get_pos_by_image(newimg)
store.append_image(newimg)
while gtk.events_pending():
    gtk.main_iteration()
view = window.get_view()
view.set_image(newimg)
store.remove_image(img)
oldimg.remove()"""
#print old
#print dir(old)
"""os.rename(fullname, newname)
while gtk.events_pending():
    gtk.main_iteration()
f = gio.File(newname)
newimg = eog.eog_image_new_file(f)"""
#img.load(newimg, 0)#, (eog.IMAGE_DATA_IMAGE, eog.IMAGE_DATA_DIMENSION))#, None, None)
#view = window.get_view()
#view.set_image(newimg)
"""store = window.get_store()
store.append_image(newimg)
v = window.get_thumb_view()
v.set_current_image(i, True)"""

とにかく情報らしい情報が無いも同然だから自力で漁ったのよこの方法。
eog のソースに eog_window_display_image なんて関数があるが外部提供されていないし。
これ以上書くと愚痴になるのでおしまい。

しかしこれで Eye of GNOME への不満は私的には解消だ、他を探す必要もない。
プラグインを作るって面白いよ、アプリは探すものではなく作ったり改造したりするものだ。