Python」タグアーカイブ

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 言語はやっぱり面倒くさい。

Gio Streaming I/O @ Seed and PyGI

そういえば Seed で環境変数を使うにはどうすればいいのだろう?
Python のように便利な標準モジュールみたいなのは無いわけで。
多分 glib 等に頼るしかないのだろう。

Miscellaneous Utility Functions

glib に env 関連はあるね。

#!/usr/bin/env seed

GLib = imports.gi.GLib;

Seed.print(GLib.getenv("HOME"))

GLib.setenv("HOGE", "Madoka Magica")
Seed.print(GLib.getenv("HOGE"))

これでイケる、GLib.get_home_dir() でも $HOME は取れる。
でも日本語を getenv だと ? になって print されてしまった。

インタラクティブシェルなら問題なく日本語表示している。
? の文字数は合っているから UTF-8 だと認識はしているみたいだが何故だろう。
Python の coding:utf-8 指定のようなものが何か必要なのか?

そういえばファイルを読み書きする open() も無いよな。
gio で読み書きするしか方法が無いと思うけど。

Seed/Tutorial – GNOME Live!
Seed/Tutorial/Simple_file_io – GNOME Live!

GDataInputStream, GDataOutputStream を噛ます必要があるようです。
Streaming I/O
なんか面倒くさいけど以下で読み書きできた。
読み込みはよくある一行毎に処理する方法にしている。

#!/usr/bin/env seed

/*
    Streaming I/O File Read and Write Sample
    if the Seed
    ( null parameter is not needed )
*/

Gio = imports.gi.Gio;

var filename = "test_js.txt";
var lines = "madoka\nhomura\nmami\nsayaka\nkyoko";

// Write
var f = Gio.file_new_for_path(filename);
var fstream = f.replace();
var dstream = new Gio.DataOutputStream.c_new(fstream);
dstream.put_string(lines);
fstream.close();

// Read
f = Gio.file_new_for_path(filename);
fstream = f.read();
dstream = new Gio.DataInputStream.c_new(fstream);
while (1) {
    var text = dstream.read_line_utf8();
    if (text == null) break;
    Seed.printf("%s(%d)", text, text.length)
}
fstream.close();

日本語でも書き込みはできた、けれどアウトプットはやはり ? になる。
それと length を取得する方法が無いみたい、int では値渡しになるし…

コレで gi にて Streaming I/O を使う方法が解ったぞと。
ということは PyGI で同様にするには以下のように。
ほぼ同じだったけど微妙に違う。

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

'''
    Streaming I/O File Read and Write Sample
    if the Python
    ( All None parameter @ GCancellable )
'''

from gi.repository import Gio

filename = "test_py.txt"
lines = "madoka\nhomura\nmami\nsayaka\nkyoko"

# Write
f = Gio.file_new_for_path(filename)
fstream = f.replace("", False, Gio.FileCreateFlags.NONE, None)
dstream = Gio.DataOutputStream.new(fstream)
dstream.put_string(lines, None)
fstream.close(None)

# Read
f = Gio.file_new_for_path(filename)
fstream = f.read(None)
dstream = Gio.DataInputStream.new(fstream)
while 1:
    text, length = dstream.read_line_utf8(None)
    if text == None:
        break
    print "{0}({1})".format(text, length)
fstream.close(None)

Seed は基本的に NULL でいい引数は書く必要は無い、書いても結果は同じ。
Python は GCancellable を強要する、None で通常なら問題ない。
つか Python はやっぱりタプルを戻す、つまり Python なら length も取れる。
慣れていないと困惑するよなこの仕様。

DataInputStream の c_new って何だろう?

#!/usr/bin/env seed

Gio = imports.gi.Gio;

for (var s in Gio.DataInputStream) {
    Seed.print(s);
}
/* output
type
c_new
prototype
*/

static 状態では prototype のメソッドを使えないということか。
そういうことなら new という名前でイイと思うが理由があるのだろう。

とりあえずこれだけ解れば小物スクリプト程度なら作れると思う。

どうでもいいけど。

var f = Gio.File.new_for_path(fn)
var f = Gio.file_new_for_path(fn)

PyGI でも同じである、gi ってどっちでもいいんだね。