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

Kivy

筆者は昨今 GUI に Clutter ばかり使っている。
当然だよ、今時の GUI は OpenGL ES ですよ。
デスクトップでスマートフォンに劣る GUI キットを使ってどうする。

さて Mac で使える OpenGL ES ツールキットは何があるのかな?
スクリプト言語、JavaScript か Python で使えるなら嬉しいんですけど。

Kivy: Cross-platform Python Framework for NUI Development

あるジャン!

クロスプラットホームらしいけど GNOME なら Clutter があるからイラネ!
エロゲ専用に落ちぶれた某 OS はまだ WindowsForm 大好きだからシラネ!
断言する、Mac ユーザー以外は使わない!

クロスプラットホームの開発環境が全体に広まった前例は無いよね。
node.js は事実上 Mac 専用、mono はまさかのスマホゲーム専用にとか。
ところでポケモン GO も Unity 製なんですね、マジでスタンダードだわ。

つか 1.9.1 には Python3 用があるヤン。
やっと Mac に Python3 を入れる理由ができた!
早速 Mac に Python3 と kivy のインストール。
なんで 7z なんだよ…

Installation on OS X ? Kivy 1.9.2-dev0 documentation

上記にインストール方法があるけど一応 7z 展開後は以下のコマンドを。

sudo mv Kivy3.app /Applications/Kivy.app
sudo ln -s /Applications/Kivy.app/Contents/Resources/script /usr/local/bin/kivy

Kivy3.app のまま /Applications に置くと動かなかった。
ln は当然 sudo が必要、まあすぐ気が付くと思うけど。

ところで Python3 は同梱している 3.5.0 を使うようだ。
自信で 3.5.1 を起動しても import kivy はできないので注意。
せっかく自分で入れたのに意味無かった。

kivy3_import

#!/usr/bin/env kivy

import kivy

from kivy.app import App
from kivy.uix.image import Image
from kivy.loader import Loader

class TestApp(App):
    def build(self):
        self.title = "タイトルバー"
        proxyImage = Loader.image("[家の手伝い]優木苗+.jpg")
        proxyImage.bind(on_load=self._image_loaded)
        self.image = Image()
        return self.image

    def _image_loaded(self, proxy):
        self.image.texture = proxy.image.texture

TestApp().run()

kivy_image

おぉ、たったこれだけでフルスクリーンも可能なアプリ完成だ!
ウインドウの大きさに合わせて自動的にテクスチャもリサイズされる。
日本語も扱える、但し Label だと豆腐になるのは何故だろう。

シバンを上記のように指定して拡張子無しで保存、実行パーミッション付加。
これで W クリックで起動できるけど Terminal.app が起動するな…
iOS 用も作れるらしいので app 化もできるはず。

コレは想像以上に PyGObject 感覚を Mac で再現できそうだ。
もし我が Comipoli の Mac 版を作ろうと思ったならコイツだな。
ドキュメントが公式くらいしか無いのがチョッピリ痛い。

ClutterSwipeAction

マルチタッチを実装してみたくなった。
具体的には Comipoli にてフリックで次ページ移動をさせたい。
スマートフォンに慣れてしまうとどうしてもね。

swipe

本当は指一本フリックにしたいけど指定する手段が無いようだ。
当然か、マウスカーソルがある OS ではそれマウスカーソルの移動だ。
指一本で操作するには OS をマウスカーソル無しにするしか手段が無い。

上記一つだけでもデスクトップとタブレットで共通 OS は不可能と解る。
iOS と Mac OS みたいにイメージだけ共通にするべき。
結局 GNOME は Mac OS のようにしたいってことなんだろう。

でも GNOME のマルチタッチは本当に動くのかな?
そうだ、筆者は MacBook Air を持っているじゃないか。
MacBook Air に DVD ドライブを付けて Fedora LiveDVD で試す。

更に外付け HDD を繋いで Gedit にてテキストファイルを開く。
うん、二本指で上下スクロールは可能だ、慣性スクロールにも対応している。
次に EoG で画像を表示。
回転も拡大もできない、フリックで次ファイルのつもりがスクロールに。
駄目だこりゃ、多分 GNOME は Windows 10 のマルチタッチ仕様なんだろう。

他にも Mac で Fedora はキーボードのバックライトが点灯しないとか。

Fedora 24 でも WiFi には繋がらなかった。
多分下記と同じようにすればイケると思うけど。
MacBookへLinux(Fedora)をインストール – Qiita

ところで command キーがそのまま Super キーとして使えるのね。
control が Ctrl のままだ、Function キーは fn キーを押す必要あり。
fn+delete で Delete キーなんかは Mac OS と同じ。
うん、キーボードに関しては何も問題ない。

マルチタッチさえ気にしないならなんとか使えそうだが、うーん。
MacBook Air はやはり Mac のまま使うのが正解だろうな。
そもそも現状で何も困っていない。

脱線した、フリックで次ページ移動の話ね。

実はマウスで次ページに移動する手段を作ろうと考えた。
EoG のように画像に被さるボタンは嫌い、ならばフリックだ!
という理由でこんなことになった。
ClutterSwipeAction を使えば簡単にフリック動作を得られるようだ。

swipe シグナルの direction 引数は
CLUTTER_SWIPE_DIRECTION_UP | CLUTTER_SWIPE_DIRECTION_RIGHT
のように or 演算で届くので and 演算を使って確認する。

class ComipoliWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        #
        # etc...
        #
        # Gesture
        swipe = Clutter.SwipeAction()
        swipe.connect("swipe", self.on_swipe)
        self.stage.add_action(swipe)
        self.stage.set_reactive(True)
        #
        # etc...

    def on_swipe(self, action, actor, direction):
        if direction & Clutter.SwipeDirection.RIGHT:
            self.change_pixbuf(2)
        elif direction & Clutter.SwipeDirection.LEFT:
            self.change_pixbuf(-2)
        return True

なんだ、これだけでマウス左ボタンでのフリック動作が可能だ。
Mac のタッチパッドフリックじゃやっぱり動かないな。

EoG は同じようにマウスでは動かないので別の実装が必要なのかな?
マルチタッチ対応マシンを買わないと解らない、いつか気が向いたら。

focus-on-click

GTK+ 3.20 には focus-on-click という便利な property が追加された。
その名のとおり mouse で click した直後に focus を得るかの指定。

GUI アプリを作っている人なら泣いて喜ぶレベルのプロパティです。
実は我がアプリは GtkShortcutWindow よりもコレがあるから 3.20 仕様にした。

だって Y901x でも mouseup シグナルで親ウインドウにフォーカスを逃がすとか…
マジで GUI アプリを作っている人”だけ”が解る待望のシロモノ。
細かい説明は省略、GUI アプリを本気で作れば必ず問題になることだし。
意味不明なら…御察し。

使い方は簡単。

//Gjs
let button = new Gtk.Button({focus_on_click: false});

# Python
button = Gtk.Button(focus_on_click=False)

でフォーカスを持たないボタンの完成。

Gjs, PyGObject 共に同じように書ける。

筆者の説明が悪かったのかプロパティは作成時の引数で全部指定できる。
勘違いした人を検索で多々見つける気がするんですけど。

devhelp での *.new のメソッドを使うなら *_new の引数を全部指定。
ソレを使わないなら Gjs は json にてプロパティを指定。
PyGObject はデフォルト引数にてプロパティを指定できる。
指定しなかったプロパティはデフォルトが適用される。

GtkBox が一番解りやすいかな。

#vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

この 2 つはまったく同じ結果になる。
GtkBox は GtkOrientable をインプリメントしている。
GtkOrientable のプロパティをまんま利用できるというわけです。

余計に解り辛くする説明かもだけど。

get_size, resize

GtkHeaderBar 付きの GtkWindow のリサイズ。
以前こんなことを書いた。

GTK+ 3.20 GtkHeaderBar and CSS | PaePoi

もう少し細かく判った。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class Win(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        # Headerbar
        self.headerbar = Gtk.HeaderBar(show_close_button=True)
        self.set_titlebar(self.headerbar)
        # Button
        button = Gtk.Button(label="Click !!!")
        button.connect("clicked", self.on_button_clicked)
        self.add(button)

    def on_button_clicked(self, button):
        print(self.get_allocated_width(), self.get_allocated_height())
        w, h = self.get_size()
        print(w, h)

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_activate(self):
        w = Win(self)
        w.show_all()

app = App()
app.run()

aloc_size

get_allocated_* と get_size で値が違う。
ちなみに GtkHeaderbar を消すと同一の値になる。

見た目で解るけどクライアント領域のサイズですね。
window.get_size と window.resize が共通サイズになるようだ。
ということでサイズの保存にはコチラを使いましょう。

ついでに、GVariant.deep_unpack は Gjs でないと使えないのね。
Gjs も地味に便利にしているところがあったのね。

Gjs VariantBuilder

しまった、ポケモン GO にどっぷりハマってもーた。

hf_kari

むったんと五十鈴とくるみんと柊の 4M ゲット、ココミンも確定済。
苗ちゃんごめん、無課金ではランキング上位報酬なんて無理です。

ボケはこれくらいにして。

オジサンはポケモンなんてピカチュウとサトシしか知らないけどやっているよ。
と言ったら驚く人がたまにいる、こっちが驚くってばさ。
こんな人達はポケモン自体の人気でフィーバーしていると思っているようだ。
つまり、まだまだ伸びる可能性があるという恐ろしさ。

すでに個人がアプリを作ったって存在に気付いてくれる人はゼロに近い。
16 年前はゆりあるなんてしょーもないモンが万単位だったのにな。
プログラミングは仕事か個人で使うスクリプトだけの時代がもう来ている。

とにかくポケモン GO (と GF) でサボっていたぶんを取り返さなきゃ。

今回はずっと後回しにしてきた GSettings に配列の配列を保存する方法を調べる。
もちろん Gjs、まあ Python でもほとんど同じだけど。

GSettings への保存は基本的に Variant 型で保存する。
配列にするには VariantBuilder というものを使うようだ。
配列の配列だけどタプルの配列として保存するのが効率よさげ。

this.set_size = [[320, 240], [640, 360], [1280, 720]];

//
// Write
//
let settings = new Gio.Settings({schema:"org.sasakima.y901x"});
let xy = new GLib.Variant("(ii)", this.get_size());
settings.set_value("window-size", xy);
// Error
//let builder = new GLib.VariantBuilder({type: new GLib.VariantType("a(ii)")});
//let builder = new GLib.VariantBuilder(GLib.VariantType("a(ii)"));
let builder = GLib.VariantBuilder.new(new GLib.VariantType("a(ii)"));
for (let i=0; i<3; i++) {
    let t = new GLib.Variant("(ii)", [ this.set_size[i][0], this.set_size[i][1] ]);
    builder.add_value(t);
}
settings.set_value("set-size", builder.end());


//
// Read
//
let settings = new Gio.Settings({schema: "org.sasakima.y901x"});
let [w, h] = settings.get_value("window-size").deep_unpack();
this.resize(w, h);
this.set_size = settings.get_value("set-size").deep_unpack();

これでなんとかなった。

dconf

GLib.VariantBuilder
のように new キーワードを使うとエラー。
この構造体はプロパティが無いので JSON では対応できない、うーむ。

保存で詰まりまくったけど読み込みは簡単すぎ。
get_value して deep_unpack だけで配列の配列が完成、唖然とした。
調子に乗ってサイズ保存もタプルに変更したら前のキーが残ってしまった。
これどうやって消すのかな?今度調べる。