月別アーカイブ: 2011年12月

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 等の公開を終了。
早く消したかったけど我慢して一年も間をおいたので多分大丈夫だろう。
それでは良いお年を。

Dark Theme

GNOME 3.2 から導入された Dark Theme って簡単なんですね。
gtk_settings_get_default ()
で得られる Settings のプロパティを弄くるだけ。

ついでだから他にどんなプロパティがあるかを書き出す処理も。

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

from gi.repository import Gtk

# Sharing settings Default Value Check
settings = Gtk.Settings.get_default()
for s in dir(settings.props):
    if not s.startswith("_"):
        print "{0}: {1}".format(s, settings.get_property(s))

# Set Dark Theme
settings.props.gtk_application_prefer_dark_theme = True
# or
#settings.set_property('gtk-application-prefer-dark-theme', True)

w = Gtk.Window()
w.connect("delete-event", Gtk.main_quit)
w.show()
Gtk.main()

こんなに簡単な処理で Totem 等と同じ見た目になる。
他色々プロパティがあるけど利用するかどうかは微妙かな。

ついでに memopolix の現状。
memopoli に名前変更する、メモポリエックスじゃ言いにくい。
クリポリエックスも次で x を取る、Windows 版と名前を分ける必要はなかった。
ワイキュウマルイチエックスだけは x を付けないと混乱する人が出る。

ガシガシ例外を吐いてくれる、もう少しまともになったら何か書く。

destroy and delete-event Signal

現在作っている PyGI アプリで Window size を保存したい。
GtkApplication から起動なら delete-event で Gtk.main_quit せずに閉じる。
ちなみに delete-event は標準の「閉じる」ボタンか吐くシグナルです。

なので destroy Signal で処理しようとしたのだけど。

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

from gi.repository import Gtk, Gio

class DestroyTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        cx, cy = self.get_size()
        print "initialize size {0},{1}".format(cx, cy)
        self.connect("delete-event", self.on_delete_event)
        self.connect("destroy", self.on_destroy)
        # Resize
        self.resize(320, 240)
        self.show_all()

    def on_destroy(self, widget):
        cx, cy = self.get_size()
        print "destroy size {0},{1}".format(cx, cy)

    def on_delete_event(self, widget, event):
        cx, cy = self.get_size()
        print "delete-event size {0},{1}".format(cx, cy)

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

""" output
initialize size 200,200
delete-event size 320,240
destroy size 200,200
"""

こうなっってしまった。
破棄された後に値を得ようとしているのだからそりゃそうだ。
.NET とかなら例外、って GTK+ は C 言語だったね。

それなら delete-event で設定保持を行えばいい。
んでもって emit も destroy から変更して。
で、又困ってしまった。

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

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

class DestroyTest(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        button = Gtk.Button("emit delete-event")
        button.connect("clicked", self.on_button_clicked)
        self.add(button)
        self.connect("delete-event", self.on_delete_event, button)
        self.show_all()

    def on_button_clicked(self, button, data=None):
        """
            emit is No Close
        """
        event = Gdk.Event(Gdk.EventType.DELETE)
        self.emit("delete-event", event)
        # Enable a Close
        #self.emit("destroy")

    def on_delete_event(self, widget, event, button):
        button.set_label("catch delete-event")
        return False

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

delete-event の emit だけじゃ終了してくれない。
メニューやボタンから終了するなら destroy も投げる必要あり?

GtkApplication はどうやって閉じているのだろう。
やっぱり普通に Gtk.main() を使ったほうが楽かも。

memopolix 0.0.0

gjots2 の代わりを現在作っている。
Read in gjotsfile | PaePoi

躓きまくっているので Blog の更新が止まりぎみですが…
勉強だけやっていても面白くないし進歩もない、とにかく何か作りたい。
実際にアプリケーションを作ってみないと解らないことが結構多いですから。

アプリ名と仕様を考えるのに一番時間を使ったのだが…
名前ごときに時間を割いてもしかたがない、memopolix でいいや。
***poli で x を末尾につける、このパターンはつまり(以下略

ファイル仕様は gjots2 完全互換、但しコピーして元ファイルは汚さないように。
本体ツールバーはメインツールバーに保存と移動矢印のみとする。
それと一行めだったタイトルを GtkEntry に分離、ファイルはそのまま。
拡張するのは v2 からでいいだろう、もし拡張するならだが。
とにかく徹底的にシンプル、よし仕様はこんなものだろう。

Gio での Streaming I/O も解ったことだし限界まで Python 標準モジュールを使わずに gi を利用する。
そうしておけば Seed や Vala からも参考にできるという理由だが。
gnome-games の Swell Foop って Seed と Clutter で作っているんだね。

せっかくなので gettext で国際化してみたい。
コレは python モジュールを利用するしか無いみたい。

$ xgettext ui.py #=> messages.po
$ # Rewrite messages.po
$ msgfmt messages.po #=> messages.mo
$ mv messages.mo ja/LC_MESSAGES
import gettext
import os

gettext.bindtextdomain('messages', os.path.dirname(__file__))
_ = gettext.gettext

色々調べてみたけどコレが一番簡単だった。
インストールせずに使いたいので /usr/share/locale には入れたくないし。
ところで GtkActionEntry に stock_id 指定を行い label を None にすれば勝手に国際化表記なメニューやツールバーになるんだね、今頃知ったよ。
Opera とかみたいに自力でやったほうが簡単なことは内緒だよ。
Seed からは _ = imports.gettext.gettext; で使える。

それと GtkActionEntry で <shift><control><alt>Down なんてキーを割り付け上下移動させようと思ったけど GNOME3 では仮想デスクトップの移動になってしまう、<control>D とかにするのも、うーん。
割り付け無しのほうが潔いか。

それより詰まったのはファイルの書き込みだった。
GtkListstore なら seemex でやったけど GtkTreeStore はどうすれば?
再起を使えばいいということは解るけどイザ手段となると全然掴めない。

自力を諦めて素直に gjots2 のコードを参考にする。
SourceArchive.com

def tree_all_text(self, store, it, first):
    if not first:
        self.result += "\\NewEntry\n"
    body = store.get_value(it, 1)
    self.result += body
    if body and not body[-1] == "\n":
        self.result += "\n"
    it_child = store.iter_nth_child(it, 0)
    if it_child:
        if not first:
            self.result += "\\NewFolder\n"
        while it_child:
            self.tree_all_text(store, it_child, 0)
            it_child = store.iter_next(it_child)
        if not first:
            self.result += "\\EndFolder\n"

def write_file(self, store):
    self.result = ""
    it = store.get_iter_first()
    if it:
        self.tree_all_text(store, it, 1)
    f = open("test.txt", "w")
    f.write(self.result)
    f.close()

手段が解ってしまえば「なるほど〜」なんだけど再起ってムズい。
コレを分離する予定な一行めタイトルを入れる処理に変更しなきゃ…
読み取りは簡単なのにまさか書き込みがこんなに難しいとは。
やっぱり実際にアプリを作ってみないと解らないコトって多い。

先はまだ長い、まだ移動処理すら手をつけていない状態。
まあ Y901x は安定させるまでに二年掛かったしこんなものだ。
しかしツリーメモとしか使っていなかったけど gjots2 は多機能なのね。
memopolix は単なるツリーメモでいくけど gjots GTK3 版が出たらどうしよう?

一応バックアップ。
memopolix-0.0.0.tar.gz

G_APPLICATION_HANDLES_OPEN @ C, PyGI, Seed

PyGI で GtkApplication の G_APPLICATION_HANDLES_OPEN が上手くいかない。
引数の有る無しで activate か open シグナルに振り分けされる処理だが…
引数を渡せば open のシグナルが来るのだけど files は空ッポの配列になる。
もしかしてと思い Seed で使ってみたがコレも上手く行かない。

GtkApplication

bloat_pad とかいうサンプルがあるのに…
まてよ、C 言語でなら本当に上手くいくのだろうか?
C で GTK+ をやるつもりは無かったけど GTK+ ヘッダを入れて実験してみよう。

ついでだから Fedora 16 で GTK+ ヘッダ導入方法を少し。

gcc は誰でも解るからいいとして、とうの昔に私は導入済みだし。
「追加/削除」にて gtk3 で探せばアッサリヘッダ群は見つかる。

gtk-devel* だけチェックすれば cairo とかのヘッダも勝手に入る。

devhelp も入れておくと web でマニュアルを読むより楽。
anjuta は個人的にはいらない、IDE の使い方を覚えるのと Makefile の書き方を覚えるのとでどちらの時間がもったいないと思うかで決めればいいと思う。

さて準備はできた、Makefile を作って bloat_pad とやらをビルド。
あたりまえのように files には GFile のリストが入っていた。

でもこのコードではイマイチ理解ができないので単純なコードで作ってみる。
C は普通に g_signal_connect させてみた。

#include <gtk/gtk.h>

static void
newwin (GApplication *app,
        const gchar  *title)
{
    GtkWidget *window;

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_application (GTK_WINDOW (window), GTK_APPLICATION (app));
    gtk_window_set_title(GTK_WINDOW (window), title);
    gtk_window_resize(GTK_WINDOW (window), 200, 10);
    gtk_widget_show_all (GTK_WIDGET (window));
}

static void
on_activate (GApplication *application)
{
    newwin (application, "activate");
}

static void
on_open (GApplication  *application,
         GFile        **files,
         gint           n_files,
         const gchar   *hint)
{
    gint i;

    newwin (application, "open");

    for (i = 0; i < n_files; i++)
        printf ("%s\n", g_file_get_uri(files[i]));
}

int
main (int argc, char **argv)
{
    gtk_init (NULL, NULL);
    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.gtk.apptest",
            G_APPLICATION_HANDLES_OPEN);
    g_signal_connect(G_OBJECT(app), "activate",
            G_CALLBACK (on_activate), NULL);
    g_signal_connect(G_OBJECT(app), "open",
            G_CALLBACK (on_open), NULL);

    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

WARNING で Files==[] になる PyGI
sys.argv をタプルにしてみたが結果は同じだった…

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

import sys
from gi.repository import Gtk, Gio

class Win(Gtk.Window):
    def __init__(self, app, title):
        Gtk.Window.__init__(self)
        self.set_application(app)
        self.set_title(title)
        self.resize(200, 10)
        self.show_all()

def on_activate(application):
    Win(application, "activate")

def on_open(application, files, n_file, hint):
    print files
    Win(application, "open")
    """
    for f in files:
        print f.get_uri()

    Warning: g_value_get_boxed: assertion `G_VALUE_HOLDS_BOXED (value)' failed
    """

Gtk.init(sys.argv)
app = Gtk.Application.new(
        application_id="apps.test.helloworld",
        flags=Gio.ApplicationFlags.HANDLES_OPEN )
app.connect("activate", on_activate)
app.connect("open", on_open)
ARGV = tuple(sys.argv)
app.run(ARGV)

WARNING は出ないけど files がワケワカになる Seed
Seed は set_title しなくてもコレでタイトルは指定できるよ。
それと Seed.argv は配列を一つ減らさないと認識してくれなかった。

#!/usr/bin/env seed

Gio = imports.gi.Gio;
Gtk = imports.gi.Gtk;

Win = new GType({
    parent: Gtk.Window.type,
    name: "SeedWindow",
    init: function (title){
        this.resize(200, 10);
        this.show_all();
    }
});

Gtk.init (null, null);

var app = new Gtk.Application({
    application_id:"apps.test.app",
    flags:Gio.ApplicationFlags.HANDLES_OPEN
});
app.signal.activate.connect( function(application) {
    var w = new Win({title:"activate"});
    w.set_application(application);
});
app.signal.open.connect( function(application, files, n_file, hint) {
    print(files);
    var w = new Win({title:"open"});
    w.set_application(application);
    /*
    for (var i=0; i<n_file; i++) {
        print( files[i].get_uri() );
    }
    TypeError 'undefined' is not an object (evaluating 'files[i].get_uri')
    */
});
var r = new Array();
for (var i=1; i<Seed.argv.length; i++)
    r.push(Seed.argv[i]);
app.run(r.length, r);

Makefile 入りアーカイイブも置いておく。
Wordpress って tar.xz だとアップロードができないじゃん!
まあいい、tar.gz で。

app.tar.gz

実行、引数を入れればドレもタイトルバーが open になるのが解る。
でも上手くいくのは C のみ、Seed はどうにかできそうだが今はワカンネ。
Python は PyGI の処理なのか、gi 自体の限界なのか…

ついでに解ったけど存在しないファイル名でも files に含まれるのね。

てなわけで、現状では HANDLES_OPEN にしたいなら C でやれってことですね。
しかし久々の C 言語はやっぱり面倒くさい。