Programming」カテゴリーアーカイブ

ClutterGst 3.0 and GtkHeaderBar

タイトルどおりのモノを作っているので覚書。
実は GtkHeaderBar って Widget を置けるはずがない所に描写している。
なのでこんなことがおこります。

いきなり少し違うけど PyGObject のオーバーライドバグ。

def do_size_allocate(self, allocation):
    pass

ovverride_error

BUG って出るんだね、親切になったもんだ。
connect メソッドなら普通にコネクトできる。

ただ本体の大きさで ClutterActor のサイズを合わせると

class ClPlayer(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # etc...
        self.connect("size-allocate", self.on_size_allocate)

    def on_size_allocate(self, widget, allocation):
        self.actor.set_width(allocation.width)
        self.actor.set_height(allocation.height)

allocate_size

外枠とタイトルバーの大きさの二倍が加算されてしまうようです。
Box を噛ませてその Box の大きさに合わせてなら問題ないようだ。
GtkHeaderBar さえ使わなければコレでもいいんですけどね。

それと draw シグナルは絶対に処理してはいけない。

def do_draw(self, cr):
    pass

gtkheaderbar_draw

GtkHeaderBar が真っ黒なレゴになってしまいます。
描写処理は Clutter で行うようにする。

そんなこんなで色々困ったことが起こったけどなんとかなった。

#!/usr/bin/env python3

import sys, gi
gi.require_version("Gtk", "3.0")
gi.require_version("ClutterGst", "3.0")
gi.require_version("GtkClutter", "1.0")

from gi.repository import Gtk, Gdk, ClutterGst, GtkClutter, Clutter

# Own Change
URI = "file:///home/sasakima-nao/movie/kawasaki_h2r.mp4"

class ClPlayer(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self)
        # Dark Theme
        settings = Gtk.Settings.get_default()
        settings.props.gtk_application_prefer_dark_theme = True
        # GtkHeaderBar
        self.hbar = Gtk.HeaderBar()
        self.hbar.set_show_close_button(True)
        self.set_titlebar(self.hbar)
        # Full Screen Button
        fullbutton = Gtk.Button.new()
        image_full = Gtk.Image.new_from_icon_name("view-fullscreen-symbolic", Gtk.IconSize.MENU)
        fullbutton.set_image(image_full)
        self.hbar.pack_end(fullbutton)
        # new
        self.actor = Clutter.Actor.new()
        self.content = ClutterGst.Aspectratio.new()
        self.player = ClutterGst.Playback.new()
        # set
        self.content.set_player(self.player)
        self.actor.set_content(self.content)
        # add
        embed = GtkClutter.Embed()
        stage = embed.get_stage()
        stage.add_child(self.actor)
        # box
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(embed, True, True, 0)
        vbox.connect("size-allocate", self.on_vbox_size_allocate)
        self.add(vbox)
        # play
        self.player.set_uri(URI)
        self.player.set_playing(True)
        # show
        self.show_all()

    def do_delete_event(self, event):
        Gtk.main_quit()

    def on_vbox_size_allocate(self, vbox, allocation):
        """
            Fit a Clutter.Actor
        """
        self.actor.set_width(allocation.width)
        self.actor.set_height(allocation.height)

GtkClutter.init(sys.argv)
ClutterGst.init(sys.argv)

win = ClPlayer()
Gtk.main()

gst_headerbar

GtkHeaderBar とダークテーマが実装できた。
次はシークバーだな、また色々出るんだろうな…
だから楽しいんだYO!

ClutterGst 3.0 with Python

ClutterGst PyGI | PaePoi

以前こんなのを書いたけど

Clutter Gst 3.0.14 Reference Manual: Clutter Gst 3.0.14 Reference Manual

ClutterGst.VideoTexture なんて 3.0 の現在ではもう無い。
今はどうすれば ClutterGst を扱えるのかな?

そういえば Sushi は Gjs だったな。
一応、Sushi は Nautilus にて Space Key でプレビューするアプリね。
Fedora には最初から入っているよ。
だったらコイツのコードを参考にすればいいじゃないか。

sushi_gst

って VideoTexture のまんまや!
Nautilus で動画をプレビューすると緑色でやはり表示できない。

[sushi] viewer: gst: port to ClutterGst 3.0

見つけた、十月にはコミットされているはずなんだけどなぁ。
自力で置き換えして試してみる、Gjs は面倒だから Python で。

#!/usr/bin/env python3

import sys, gi
gi.require_version("Gtk", "3.0")
gi.require_version("ClutterGst", "3.0")
gi.require_version("GtkClutter", "1.0")

from gi.repository import Gtk, Gdk, ClutterGst, GtkClutter, Clutter

class ClPlayer(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self)
        # new
        self.actor = Clutter.Actor.new()
        self.content = ClutterGst.Aspectratio.new()
        self.player = ClutterGst.Playback.new()
        # set
        self.content.set_player(self.player)
        self.actor.set_content(self.content)
        self.actor.show()
        # size
        self.actor.set_width(320)
        self.actor.set_height(180)
        # add
        self.embed = GtkClutter.Embed()
        self.stage = self.embed.get_stage()
        self.stage.add_child(self.actor)
        self.add(self.embed)
        # play
        self.player.set_uri("file:///home/sasakima-nao/movie/kawasaki_h2r.mp4")
        self.player.set_playing(True)
        # show
        self.resize(320, 180)
        self.show_all()


    def do_delete_event(self, event):
        Gtk.main_quit()

#Clutter.init(sys.argv) # Error
GtkClutter.init(sys.argv)
ClutterGst.init(sys.argv)

win = ClPlayer()
Gtk.main()

cluttergst3

思っていたより簡単だった。
よしコレで我が Y901x の GtkHeaderBar 化に取り掛かりできそうだ。
いやまあ、いいかげんに自分で気になってきたので。

実は今まで Ubuntu 環境での Clutter がよく解らないので避けていた。
GNOME プロジェクトの成果物依存から脱却したいココは絶対に導入しないだろうし。

Ubuntu Sushi その1 – Nautilusでプレビューを行うアプリの紹介・Sushiの使い方 – kledgeb

でも Sushi を入れるだけで依存関係から必要な環境がそろうようだ。
だったら Clutter も問題ないか、多分まだ 3.0 じゃない気がするけど。

PyGIWarning

Fedora を 23 にして環境の再構築しているのですが。
今回は GStreamer デコーダーが gnome-softwere に出てこない。
RPM Fusion を毎度のようにリポジトリに追加して

sudo dnf install gstreamer1-libav gstreamer1-plugins-bad-free gstreamer1-plugins-base gstreamer1-plugins-good gstreamer1-plugins-ugly

ぶっちゃけ必要なものは何も変わっていないようだ。
今回は”コマンドを打ってね”ということらしい。

ただ我が Y901x 等の GStreamer フロントエンドを使おうとすると

y901x_warn

PyGIWarning という今まで出なかった警告が出るようになった。
一応書くけど PyGObject と PyGI は同じ。
とにかく gi.require_version をする必要があるみたい。

require_version

これで警告は出なくなる。
実は GTK+ 3.16 でリサイズグリップが無くなったのを無視していた。
ついでに一部動画形式でサイズ取得を失敗する不具合が改善されているようだ。
よし一年ぶりに Y901x を更新だ、需要なんてないけど。

ただ最近は動画って Web ブラウザ上でしか見なくなったのよね。
最新動画が次々と出て来るのでダウンロードしようとか全然思わない。
まだデジカメ動画なんかで使うけどその需要もそのうち失くなりそう。

それはいいとして。
Gedit, Eog 自作プラグインにも PyGIWarning が出るや。
多分こっちにも gi.require_version が必要なんだろう。

警告が出るだけで普通に使えてはいるんだが。
いや、Eog のほうは少しおかしいみたい。
こっちの更新は明日、ウチ姫やんなきゃ(ぉい!

Similar image @ Gjs

あるアプリを作るのに近似画像を探す処理が必要になった。
Lab色空間 – Wikipedia
を得て元画像の全体ピクセル近似値を調べればいいと解った。

[PHP]似た画像を検索して近い順番に並べる(類似画像検索) | PHP Archive
手段を見つけた、PHP だけど計算方法はどの言語でも同じだろう。
ただ GD を利用しているので他言語では別の画像解析手段が必要。

ということで、同様な処理を Gjs(Linux) でやってみる。
後で osascript(Mac) 版や Web 版を作りたくなった場合に流用できるので。

Gjs だから画像は当然 GdkPixbuf を GD の代わりに使う。
とはいえ GD と同じ関数があるはずもなく違う手段で同様な処理を作る。

画像処理のサンプル一覧
imagecolorat は gdk_pixbuf_get_pixels からオフセット。
imagecolorsforindex は rowstride 値を使って取得。
実は随分前からコレで詰まっていたおかげでブログの更新が…

正直上手く変換できた自信は無いけどなんとかなったっぽいコード。

#!/usr/bin/gjs

const GdkPixbuf = imports.gi.GdkPixbuf;

// 比較元ファイル
let sample = GdkPixbuf.Pixbuf.new_from_file("nae.jpg");
let sample_lab = getImageLab(sample);

// 比較対象ファイル名の配列
let datas = ["nae01.jpg","nae02.jpg","nae03.jpg","nae04.jpg","nae05.jpg"];

let diff = [];
datas.forEach(function(element, index) {
    let data = GdkPixbuf.Pixbuf.new_from_file(element);
    let lab = getImageLab(data);
    // 比較
    let distance = 0;
    sample_lab.forEach(function(element, index) {
        distance += getLabDistance(element, lab[index]);
    });
    diff.push({n:distance, name:datas[index]});
});

// sort
diff.sort(function(a, b) {
    return a.n > b.n;
});

// 確認
diff.forEach(function(element, index) {
    print(element.name, element.n);
});

// 画像をリサイズしピクセルごとのLab色空間上の座標を取得する
function getImageLab(image){
    let thumb = image.scale_simple(4, 4, GdkPixbuf.InterpType.BILINEAR)
    let pixeldata = thumb.get_pixels();
    let rowstride = thumb.get_rowstride();
    let lab = [];
    for(let x=0; x<4; x++){
        for(let y=0; y<4; y++){
            let i = y * rowstride + x * 3;
            lab.push(rgb2lab([pixeldata[i], pixeldata[i+1], pixeldata[i+2]]));
        }
    }
    return lab;
}

// xyz色空間上の座標をlab色空間上の座標に変換する
function xyz2lab(xyz) {
    let threshold = 0.008856;
      
    //Chromatic Adaptation Matrices
    // D50
    let ref_x = 0.96422;
    let ref_y = 1.0000;
    let ref_z = 0.82521;
 
    let var_x = xyz[0] / (ref_x * 100);
    let var_y = xyz[1] / (ref_y * 100);      
    let var_z = xyz[2] / (ref_z * 100);
      
    var_x = (var_x > threshold) ? var_x = Math.pow(var_x, 1/3 ) : (7.787 * var_x) + (16 / 116);
    var_y = (var_y > threshold) ? var_y = Math.pow(var_y, 1/3 ) : (7.787 * var_y) + (16 / 116);
    var_z = (var_z > threshold) ? var_z = Math.pow(var_z, 1/3 ) : (7.787 * var_z) + (16 / 116);
      
 
    let l = ( 116 * var_y ) - 16;
    let a = 500 * ( var_x - var_y );
    let b = 200 * ( var_y - var_z );
    return [l, a, b];
}
 
// rgb値をxyz色空間上の座標に変換する
function rgb2xyz(rgb) {
    let r = rgb[0] / 255;
    let g = rgb[1] / 255;
    let b = rgb[2] / 255;
 
    r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
    g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
    b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
 
    r = r * 100;
    g = g * 100;
    b = b * 100;
      
    let xyz = new Array();
      
    //sRGB D50
    xyz.push(r * 0.4360747 + g * 0.3850649 + b * 0.1430804);
    xyz.push(r * 0.2225045 + g * 0.7168786 + b * 0.0606169);
    xyz.push(r * 0.0139322 + g * 0.0971045 + b * 0.7141733);
    return xyz;
}
 
// rgb値をlab色空間上の座標に変換する
function rgb2lab(rgb) {
    let xyz = rgb2xyz(rgb);
    let lab = xyz2lab(xyz);
    return lab;
}
 
// 2つの座標を比較し距離を返す
function getLabDistance(p1, p2){
    let dist = Math.sqrt( Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2) + Math.pow(p2[2] - p1[2], 2) );
    return dist;
}

similar_pic

苗ちゃんがカワイイのは当然でどうでもよくて。
同一画像なら完全にゼロ、後は色が似た順に取得できた。
GdkPixbuf なので C や Python でも同じことができるはず。

後は osascript はともかく Web で画像解析はどうするかだ。
というか Web アプリでやりたいんですけどね。

PyObjC

PyObjC で Python から Cocoa にアクセスしてみよう (フェンリル | デベロッパーズブログ)

なんだよ、Mac で Python は全然役立たずじゃないヤン!
ただ Python2 専用だろうな、次期版は Python3 になるかな。

Simple PyObjC Example ? Python recipes ? ActiveState Code

Window も作れ、更にサブクラスも当然のように使えるようだ。
しかし日本語情報が全然見つからない。
日本人は Mac で Python でも計算ばかりやって喜んでいるようだ。
何が面白くてプログラミングしているか理解できないが人それぞれかと。

よし上記をコピーして動かしてみよう。

拡張子を外して実行パーミッションを付けて。
シバンで pythonw を指定してと。

#!/usr/bin/pythonw
#-*- coding:utf8 -*-

これで Finder から command+O してみる。

mac_pythonw

端末エミュレーターが開くジャン!
pythonw は Windows と仕様が違うの?

Macintosh で Python を使う ? Python 2.7ja1 documentation
Windows で Python を使う ? Python 2.7ja1 documentation

確かに Mac のドキュメントには”ターミナル無し”とは書いていない。
ヤラレタって感じ、結局アップルスクリプトと変わらない結果に。
オマケに初期化が凄く遅い、おまえは IronPython か!

やはりスクリプトで GUI はあきらめたほうが良さげ。
でも Objective-C 関数が Python で使えるのは嬉しいかも。
いや、初期化が遅いと結局使わなくなるのは経験済みで。
あー”コレだ!”てのがまだ見つからないYO!