Python」タグアーカイブ

Eye of GNOME 3 plugin Create

Rename Dialog Eye of GNOME 3.* plugin 公開。

EyeOfGnome/Plugins – GNOME Live!

あれ、一番下に
~/.config/eog/plugins/ に入れろって書いてある。
3.0 の時からそう書いていたかな?記憶では v2 と同じ位置に置けと…
つか confug ディレクトリなのか、開発者によって色々思う所あるんだろうなと…

普通にサンプルコードが動いてしまったのだけど。
ちなみに v2 時は ~/.gnome2/eog/plugins に入れろであった。

Eye of GNOME プラグインは本当に面倒 | PaePoi

v2 の時には Gedit Plugin と同様に activate で window ポインタをアトリビュートにして保持すると終了できないという「ワケガワカラナイヨ」な動作をしやがったたので強引な奇策を使う必要があった。

だけど v3 はソレで全然問題ないみたい。

というか。
class 直下にポインタ保持変数を書くのは初期化に限られていると思う、うっかり self を忘れがちなどころか継承を行うと場合によっては知らぬ間に書き換えらている可能性があるので正直勧められない。

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

class Cls(object):
    def __init__(self):
        self.first = "3rd"

class Test(Cls):
    first = "1st"
    def __init__(self):
        Cls.__init__(self)
        print "2nd"
        print self.first

if __name__ == "__main__":
    Test()

""" output
2nd
3rd
"""

継承元を熟知していないと Python の場合こういうことが起こるのよ。
__init__ の下に書きたくない場合は絶対に継承元が利用していないと断言できる変数名にするしかない。
とはいえ公式が公開しているサンプルコードなので同様に書くのが一番初心者が迷わないよな。
まあソレは余談で。

*.plugin の IAge が 2 指定のままなのが気になるけど。
IAge=3 に書き換えてもどっちでも動くや、意味ネェ。
多分このキーを完全無視しているだけなのだろう。

とにかく Gedit とほぼ同一に書いて問題なくなったぞと。
Gedit Plugin の解説が知らぬ間にスゲェ細かくなてるね。
書き方が解らない場合は Gedit のを参考にすればいいかなと。

Gedit/PythonPluginHowTo – GNOME Live!

次は Unicode へ変換して日本語を含む文字数を算出する方法に困った。
ダイアログ表示時に拡張子を除いた部分のみを選択するのに必要な処理。
gtk_entry_get_text() 戻り値から 普通に unicode(str) でイケなかったのよ。

v2 時はイケた、というか PyGtk がそうバインドされていた。
どうやら PyGI ではあの親切極まりない全自動変換は諦めるしか無いようだ。
ならば GLib を使ってドット位置の long 値を見つければいいと考えた。

Unicode Manipulation

上記で strstr から for 文のような処理が使える方法を探すも上手くいかない。
GLib.utf8_substring(s, 0, 3) とかで日本語も一文字扱いなのは解ったけどドット位置を日本語も一文字扱いで探す方法が見つからない。
というか UCS4 への変換すらできない、Python で扱えないということか。
やはり Python の強力な文字列変換機能に頼りたい。

Character Set Conversion

g_filename_display_name () というのを見つけて実験。

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

from gi.repository import Gtk, GLib

class Win(Gtk.Window):
    """
        Unicode Test
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_title("Gtk3")
        entry = Gtk.Entry()
        button = Gtk.Button("Button")
        button.connect("clicked", self.on_clicked, entry)
        vbox = Gtk.VBox()
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(entry, False, False, 0)
        self.add(vbox)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        self.set_focus(entry)

    def on_clicked(self, widget, entry):
        s = entry.get_text()
        print s
        #unicode(s) #=> UnicodeDecodeError
        displayname = GLib.filename_display_name(s)
        #print displayname #=> UnicodeDecodeError
        print len(unicode(displayname))

if __name__ == "__main__":
    Win()
    Gtk.main()

display_name 変換後であれば Unicode 変換が可能になる。
しかし display_name を標準出力するとエラー、ややこしいよ。
GTK3 は ucs4 を utf-8 として扱っているみたいだからえっと…
つまりメモリ内での扱いが少々違っていることだけ解ればいい、と思う。

Activate 時の Action とかを本家が全然解説していないけど Gedit とまったく同じ。
下記から PyGI コードに自力変換する、そんなに難しくはない。

Eye of GNOME Reference Manual

他色々と v2 時とは違っていて。
画像表示中に EogListStore から削除すると EogThumbView が死ぬとかリネーム前のファイルを取り除くのは最後にしないとサムネイル更新が上手くいかないとかets…
キリがないので後はコードを見てで済ませる。

とにかくこんなコードでやっと成功した。
多分外国人のほうが興味があると思うのでヘタクソな英語にしました。
日本人って自分で作らず探しているだけの奴ばっかだもんなぁ…

renamedlg.plugin

[Plugin]
Loader=python
Module=renamedlg
IAge=2
Name=rename
Name[ja]=リネーム
Description=view of file rename
Description[ja]=観覧中のファイルをリネーム
Authors=sasakima-nao <m6579ne998z@gmail.com>
Copyright=Copyright © 2012 sasakima-nao <m6579ne998z@gmail.com>
Website=http://palepoli.skr.jp/

renamedlg.py

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

#    Eye of GNOME renamedlg plugin version 3.0.0
#    Copyright © 2012 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 Plugins
#    http://live.gnome.org/EyeOfGnome/Plugins
#    Eye of GNOME Reference Manual
#    http://developer.gnome.org/eog/stable/index.html
#
#   2012.08.28 3.0.0

from gi.repository import GObject, Gtk, Eog, Gio, GLib
import os

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

class RenameDlgPlugin(GObject.Object, Eog.WindowActivatable):
    """
        Rename Dialog Plugin for eog 3.*
        Actibate from Menu select or F2 key
    """
    window = GObject.property(type=Eog.Window)
    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        uimanager = self.window.get_ui_manager()
        self._action_group = Gtk.ActionGroup("RenameActions")
        actions = [("rename", None, "Rename", "F2", "Rename", self.on_rename)]
        self._action_group.add_actions(actions)
        uimanager.insert_action_group(self._action_group, 0)
        self._ui_id = uimanager.add_ui_from_string(ui_str)

    def do_deactivate(self):
        uimanager = self.window.get_ui_manager()
        uimanager.remove_ui(self._ui_id)
        uimanager.remove_action_group(self._action_group)
        uimanager.ensure_update()

    def on_rename(self, action, data=None):
        img = self.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( "Rename",
                        self.window,
                        Gtk.DialogFlags.MODAL,
                        (Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
                        Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT) )
        vbox = d.get_content_area()
        try:
            vbox.pack_start(label, False, False, 0)
            vbox.pack_start(entry, False, False, 0)
            d.show_all()
            def dlg_ok(self):
                d.response(Gtk.ResponseType.ACCEPT)
            def on_focus_in(self, widget):
                try:
                    # Calculate the number of characters in Unicode
                    # (e.g. Japanese Language)
                    # Converted to a Display Name
                    s = entry.get_text()
                    displayname = GLib.filename_display_name(s)
                    u = unicode(displayname)
                    i = u.rindex(".")
                    entry.select_region(0, i)
                except ValueError:
                    # Full select
                    pass
            def on_focus_out(self, widget):
                entry.select_region(0, 0)
            entry.connect("activate", dlg_ok)
            entry.connect("focus-in-event", on_focus_in)
            entry.connect("focus-out-event", on_focus_out)
            # Loop until success or Cancel
            while 1:
                if d.run() == Gtk.ResponseType.ACCEPT:
                    text = entry.get_text()
                    if text == "":
                        self.messagebox("File name is empty")
                        entry.set_text(name)
                    elif text == name:
                        self.messagebox("Have not changed")
                    elif  text in os.listdir(path):
                        self.messagebox("Found the same file name")
                    else:
                        # Get the EogListStore
                        store = self.window.get_store()
                        # Rename
                        newname = os.path.join(path, text)
                        os.rename(fullname, newname)
                        # Turn the queue
                        while Gtk.events_pending():
                            Gtk.main_iteration()
                        # Create EogImage
                        f = Gio.file_new_for_path(newname)
                        newimg = Eog.Image.new_file(f)
                        # Insert EogListStore
                        store.append_image(newimg)
                        # EogThumbView
                        tv = self.window.get_thumb_view()
                        tv.set_current_image(newimg, True)
                        # Turn the queue
                        while Gtk.events_pending():
                            Gtk.main_iteration()
                        # Remove Image from EogListStore
                        store.remove_image(img)
                        break
                else:
                    # Cancel Button
                    break
        finally:
            d.destroy()

    def messagebox(self, text):
        dlg = Gtk.MessageDialog(
                self.window,
                Gtk.DialogFlags.MODAL,
                Gtk.MessageType.WARNING,
                Gtk.ButtonsType.OK,
                text)
        dlg.set_title("Eye of GNOME")  
        r = dlg.run()  
        dlg.destroy()
        return r

Now, move all of these to ~/.config/eog/plugins/

Pless F2 Key

Enjoy!

WebM binary

意表を突いて Y901x を更新しました。
PyGtk のままだよ、当然 GTK2 だよ、もう古いよ過去の異物だよ。
とはいえ Google Chrome なんかも GTK2 だったりするのだが。

とりあえず再生速度変更の方法を何故か見つけたのでやってみようと。
Basic tutorial 13: Playback speed – GStreamer SDK documentation – GStreamer SDK documentation

一番目引数に 1.0 をデフォルト値に上下した float 値を入れた seek_event を作る、それを playbin2 に投げただけでアッサリ。
ただ再生停止したりファイルを切り替えするとリセットされて困った。
GST_MESSAGE_STATE_CHANGED で都度投げる方法でなんとかした。
こんなんで大丈夫かな…

ボタンを付けたかったけどスペースが無いのよね。
VLC とかみたく小さくしたくてもできないウインドウでは嫌だもの。

それと気になっていたけど放置していたところをまとめて。
Linux アプリ作りは本当にイイなぁ。
どうせ自分しか使っていないだろうから好き勝手にできるし。

VLC と違って音程は過去の遺物である Y901 同様変化する。
つか私は変化したほうが自然だと思うんだけど。
ま、これで YouTube から拾ったけど早すぎてどうやっているのかが全然解らなかったバイクのスゴテク動画をスローでじっくり検証できるようになったぞと。
倍速側なんてオマケだ、どんな場合の時に使うかワカラン。

しかーし

YouTube は最近 Flash, MPEG-4 AVC から HTML5, WebM になったのね。
これはキャッシュからコピーするスクリプトを作り替えしなければ。
というわけで今まで使っていた Nautilus スクリプトを改造した。

のだが…

YouTube の動画はいつのまにかキャッシュに残らなくなっている。
検索してゲゲッ、最近拾ってなかったので気がつかなかった…
FLASH のままな Opera キャッシュでも拾えない、お手上げだ!
あきらめて拡張を使おう、個人製作のバイク動画くらい拾わせてくれよ。

使えないけどせっかくやったので Python 覚書。

ちなみに Fedora 17 でファイルの拡張子が無い場合は
/usr/share/magic (/usr/share/misc/magic へのリンク)
の指定で見分けている、一応拡張子優先であるが。

とにかく /usr/share/magic を Gedit で開いてみる。
「エンコードが不明!」とか出るけど無視して「強制的に編集する」ボタンをば。
Ctrl+F で webm と打ち込み検索する、強制編集状態でも探せるよ。

WebM って Matroska のサブセットなんだ、今頃知った。
てか日本ではネクラでキモチワルイ奴しか今まで縁がなかったでしょコレ。
ftypwebm なんて指定で MP4 じゃダメだったのかな?

Linux の場合リポジトリからまとめてコーダーとデコーダー(あえてコーデックとは書かない)を入れられるからあんまり気にしないのよね。
MOV, RM, WebM なんかも普通に Nautilus でサムネイルできるし。
私の知る限り GStreamer で再生できないのは WMV3 だけだ。

それはどうでもよくて。

0x4282 が含まれている否かで通常 Matroska と見分けているみたい。
ま、実質先頭バイナリ4つだけ見れば動画であることは解るわな。

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

TEST_FILE = "test.webm"
WEBM = "\x1a\x45\xdf\xa3"

for a in WEBM:
    print ord(a),

print "\n-----"

o = open(TEST_FILE, "rb")
bin_array = o.read(4)
for a in bin_array:
    print ord(a),
o.close()

print "\n-----"

print bin_array == WEBM

この方法で拡張子が無くても先頭バイナリで判別が可能になる。
使い道は無かったけど、きっといつかは勉強して良かったになるはず!

実は Gio を使ったほうが簡単なんだけどね。
拡張子を取っ払ってこんなコードを試せば解るよ。

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

from gi.repository import Gio

# Create GLocalFile
# Remove File Extenshon
f = Gio.file_new_for_path("test")
# Create GFileInfo
info = f.query_info(
    Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
    Gio.FileQueryInfoFlags.NONE,
    None )

print info.get_content_type() == "video/webm"

実は Python コードを書くのも久々だったり。
とにかく半年以上間が開いたけどアプリの更新。
年金を貰える歳まで絶対に生き残ってやる!

GtkSourceView Insert spaces instead of tabs

タブ幅とスペース切り替えで上手く行かなかった理由が解った。
indent-width を弄くっただけではなんか変な動作になるのね。

indent-width Property が -1 なら tab-width Property に従う。
デフォルトが -1 なので最初から tab-width のみで指定する、つまり
gtk_source_view_set_indent_width ではなく
gtk_source_view_set_tab_width で指定する。

ただ tab-width は困ったことに guint (unsignde int のマクロ) である。
ちなみに WindowsSDK でのマクロは UINT、なら最初から uint でいいのに…
C 言語を作った人達は何故こんな長い型名にしてしまったのやら。

Python ってつまり guint が無い。
型として記憶するようだし、旧文字列フォーマッタにも %u が何故かあるが。
しかし試してみると PyGI は guint を long として扱うようだ。
gint, guint の違いを long と扱って吸収するようだ。

int で型チェックを行っているモジュールに渡す場合は当然弾かれるのだが。
その場合は abs(int_value), int(uint_value) キャストでイケるみたい。
つまり我が自作の inifile8.py とかを使う場合、ココでしばし悩んだ。

gtk_source_view_set_insert_spaces_instead_of_tabs
の真偽値でタブかスペースかを切り替え。

gtk_source_view_set_indent_on_tab
の真偽値では Shift+Tab での動作。
False にすると Shift+Tab でもインデントしたり BackSpace でインデントが先頭まで削除されたりでワケが解らないよ。
思えばそんな動作をするエディタもある、私的に Shift+Tab は逆インデントが直感的だと思うのだが逆にそうなっているエディタのほうが少ないという現実。

更に GtkSpinButton を使うと値が float だったりする。
しかし面白いことに guint のまま値を突っ込むことができる。
そうなるようにバインドされているだけだが Python って簡単だね。
C 言語で同じことやっちゃダメだよ。

実際にアプリを作っている人でないと気がつきもしないだろうな。
こんなに簡単だとメモリ内でどうなっているかとかが勉強できないよ。

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

from gi.repository import Gtk, Gio, GtkSource, Pango

class TabTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # GtkSourceView
        self.view = GtkSource.View()
        desc = Pango.font_description_from_string("Monospace 11")
        self.view.override_font(desc)
        self.view.set_show_line_numbers(True)
        self.view.set_wrap_mode(Gtk.WrapMode.CHAR)
        self.view.set_draw_spaces(GtkSource.DrawSpacesFlags.ALL)
        # Tab Width
        spin = Gtk.SpinButton.new_with_range(0.0, 100.0, 1.0)
        spin.set_value(self.view.get_tab_width()) # uint to float ?
        spin.connect("value-changed", self.on_spin_changed)
        # Indent of Tab or Space
        check = Gtk.CheckButton.new_with_mnemonic("Insert _spaces instead of tabs")
        check.set_active(self.view.get_insert_spaces_instead_of_tabs())
        check.connect("toggled", self.on_toggled)
        # Test Button
        button = Gtk.Button.new_with_label("Type is Integer ?")
        button.connect("clicked", self.on_clicked)
        # pack
        vbox = Gtk.VBox()
        vbox.pack_start(self.view, True, True, 0)
        vbox.pack_start(spin, False, True, 0)
        vbox.pack_start(check, False, True, 0)
        vbox.pack_start(button, False, False, 0)
        # self
        self.add(vbox)
        self.resize(300, 300)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        self.set_focus(self.view)

    def on_spin_changed(self, widget, data=None):
        """
            There is no need to UINT
            self.view.set_tab_width( abs(widget.get_value_as_int()) )
        """
        self.view.set_tab_width( widget.get_value_as_int() )

    def on_toggled(self, widget, data=None):
        self.view.set_insert_spaces_instead_of_tabs(widget.get_active())

    def on_clicked(self, widget, data=None):
        """
            tab-width Type Check
        """
        value = self.view.get_tab_width()
        widget.set_label("Type is {0}".format(type(value)))

if __name__ == "__main__":
    TabTest()
    Gtk.main()

てなわけで、なんとかなった。
memopoli 0.1.1 をめでたく公開、多分自分しか使わないと思うけど。

GtkSourceDrawSpacesFlags

あけましておめでとうございます。
ということで GtkSourceView でアプリを作っているのだが。

GtkSourceView

GtkSourceDrawSpacesFlags というのがある。
gtk_source_view_set_draw_spaces で指定するのね。
以前書いたコードに追記して試しに使ってみる。

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

from gi.repository import Gtk, Gio, GtkSource, Pango

class TextEditor(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # GtkUIManager and GtkAccelGroup
        self.uimanager = Gtk.UIManager()
        accelgroup = self.uimanager.get_accel_group()
        self.add_accel_group(accelgroup)
        # GtkActionGroup
        self.actiongroup = Gtk.ActionGroup("editor_menu")
        # GtkActionEntry
        ae = [  ("open", Gtk.STOCK_OPEN, "_Open", "O", "open", self.on_open),
                ("save", Gtk.STOCK_SAVE, "_Save", "S", "save", self.on_save),
                ("save_as", Gtk.STOCK_SAVE_AS, "Save as...", "S", "save as", self.on_save_as),
                ("quit", Gtk.STOCK_QUIT, "Quit", "Q", "quit", self.on_quit),
                ("file", None, "_File") ]
        self.actiongroup.add_actions(ae)
        self.uimanager.insert_action_group(self.actiongroup, 0)
        self.uimanager.add_ui_from_string(menu_xml)
        # MenuBar
        menubar = self.uimanager.get_widget('/MenuBar')
        # Toolbar and Style
        toolbar = self.uimanager.get_widget('/ToolBar')
        toolbar.set_style(Gtk.ToolbarStyle.ICONS)
        style = toolbar.get_style_context()
        Gtk.StyleContext.add_class(style, Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
        # StatusBar
        statusbar = Gtk.Statusbar()
        # GtkSourceView
        self.view = GtkSource.View()
        desc = Pango.font_description_from_string("Monospace 11")
        self.view.override_font(desc)
        self.view.set_show_line_numbers(True)
        self.view.set_wrap_mode(Gtk.WrapMode.CHAR)
        self.view.set_draw_spaces(GtkSource.DrawSpacesFlags.ALL)
        # Add
        sw = Gtk.ScrolledWindow()
        sw.add(self.view)
        vbox = Gtk.VBox()
        vbox.pack_start(menubar, False, True, 0)
        vbox.pack_start(toolbar, False, True, 0)
        vbox.pack_start(sw, True, True, 0)
        vbox.pack_start(statusbar, False, True, 0)
        self.add(vbox)
        # self
        self.open_filename = ""
        self.resize(300, 300)
        self.set_title("Text Editor")
        self.show_all()
        self.set_focus(self.view)

    def on_open(self, widget, data=None):
        dlg = Gtk.FileChooserDialog(
                "Open File",
                self,
                Gtk.FileChooserAction.OPEN,
                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 
                Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT))
        r = dlg.run()
        if r == Gtk.ResponseType.ACCEPT:
            self.open_filename = dlg.get_filename()
            f = open(self.open_filename, "r")
            t = f.read()
            f.close()
            buf = self.view.get_buffer()
            buf.set_text(t)
        dlg.destroy()

    def on_save(self, widget, data=None):
        if self.open_filename == "":
            self.on_save_as(widget)
        else:
            buf = self.view.get_buffer()
            t = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False)
            f = open(self.open_filename, "w")
            f.write(t)
            f.close()

    def on_save_as(self, widget, data=None):
        dlg = Gtk.FileChooserDialog(
                "Save File",
                self,
                Gtk.FileChooserAction.SAVE,
                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 
                Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT))
        r = dlg.run()
        if r == Gtk.ResponseType.ACCEPT:
            buf = self.view.get_buffer()
            t = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False)
            self.open_filename = dlg.get_filename()
            f = open(self.open_filename, "w")
            f.write(t)
            f.close()
        dlg.destroy()

    def on_quit(self, widget, data=None):
        self.emit("destroy")

menu_xml = """<ui>
    <menubar name="MenuBar">
        <menu action="file">
            <menuitem action="open"/>
            <menuitem action="save"/>
            <menuitem action="save_as"/>
            <separator/>
            <menuitem action="quit"/>
        </menu>
    </menubar>
    <toolbar name="ToolBar">
        <toolitem action="open"/>
        <toolitem action="save"/>
    </toolbar>
</ui>"""

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="apps.test.editor",
                flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_activate)
        
    def on_activate(self, data=None):
        w = TextEditor()
        w.set_application(self)
    
if __name__ == "__main__":
    app = App()
    app.run(None)

これは、まるで秀丸ではないか。
なんだよ、GtkSourceView には改行やタブ文字表示機能があるんだ。
dconf-editor で Gedit の設定を漁ったけど無かった…

GTK_SOURCE_DRAW_SPACES_NBSP とかは試してもよく解らないのだけど。
日本語圏内では解らないことなのだろうか、うーん。

ついでに、上記コードのようにフォント指定を作っている。
いや、デフォルトに戻したくなる場合もあるわけで。

# Get Default Font
settings = Gtk.Settings.get_default()
defaultfont = settings.props.gtk_font_name
desc = Pango.font_description_from_string(defaultfont)

これでデフォルトの PangoFontDescription が得られるわけだ。

どうでもいいけど Fedora の Monospace では半角全角で幅が違う。
VL ゴシックにすればいいわけだがコレは人によるからな。

ぶっちゃけ設定は Gedit の劣化パクリである。
括弧の強調表示方法がイマイチ解らない、GtkSourceBuffer 側なのよね。

現在インデントが全部半角スペースになる現象に悩まされている…
明日には出したいな、休みは明日までだし。

PangoFontDescription

memopoli の読み書きとツリー移動がなんとかなったので本サイトで公開。
まだ実装していないメニュー項目があるし設定も何もないけど「まあいいや」で。

どうしても 2011 年中に間に合わせたかっただけだ!
こんな最小限の実装までにとうとう大晦日まで掛かってしまった…

memopoli ヘルプ – L’Isola di Niente

私的にたいしたことはやっていないと思うので blog に書くことが無い。
後は自分で利用(バグ出し)しながら細かい機能を実装していくつもり。

次にフォント設定をやろうと考えていきなり困った。
GTK+ ではフォント情報をどうやって保存するのだろう?

Fonts

Pango にpango_font_description_to_string という関数があるね。
そういえば以前フォント変更する方法だけはやっていた。

GtkFontChooserDialog | PaePoi

今頃気がついたが gtk_widget_modify_font は古いようだ。
gtk_widget_override_font にすればいいのね。
よし、この前回コードをフォント情報が保存できるように書き換えてみよう。

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

from gi.repository import Gtk, Gio, Pango

class Win(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_title("font test")
        entry = Gtk.Entry()
        entry.set_text("homurachan")
        button = Gtk.Button("Select Font")
        button.connect("clicked", self.on_clicked, entry)
        vbox = Gtk.VBox()
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(entry, False, False, 0)
        self.add(vbox)
        self.connect("delete-event", self.on_delete)
        # Config File Read
        try:
            f = open("font.conf", "r")
            s = f.read()
            f.close()
        except:
            s = "Monospace 16"
        # PangoFontDescription
        self.desc = Pango.font_description_from_string(s)
        entry.override_font(self.desc)
        self.show_all()

    def on_delete(self, widget, event):
        c = self.desc.to_string()
        f = open("font.conf", "w")
        f.write(c)
        f.close()

    def on_clicked(self, widget, entry):
        dlg = Gtk.FontChooserDialog("Select Font")
        if dlg.run() == Gtk.ResponseType.OK:
            self.desc = dlg.get_font_desc()
            #entry.modify_font(self.desc) # old Func
            entry.override_font(self.desc)
        dlg.destroy()

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
                self,
                application_id="apps.test.font",
                flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.connect("activate", self.on_activate)

    def on_activate(self, data=None):
        w = Win()
        w.set_application(self)

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

なるほど、単純に「フォント名 サイズ」文字列だけでいいのか。
これなら簡単だ、早速 memopoli に実装することにしよう。
メニューに乗せるかダイアログに入れるか、うーん…

ということで。

2011 年イッパイになったのでめでたく Y901, Palepoli 等の公開を終了。
早く消したかったけど我慢して一年も間をおいたので多分大丈夫だろう。
それでは良いお年を。