投稿者「sasakima-nao」のアーカイブ

Next Button

前々回の更新で Comipoli にマウスのスワイプで次ページ機能を付けた。
二週間使ってみたけど駄目だこりゃ、どうしても使い辛い。

スワイプはタッチパネルだから直感的に使えるとよく解った。
マウスはやはり移動とクリック以外をさせてはいけない。
eog の次画像表示がボタンになったのは多分散々議論した結果なのだろう。

それとフルスクリーン化はやはりダブルクリックですよね。
スワイプ操作とバッティングするので外したけどあったほうが良さげ。
なんたって操作性を eog 同様にしたいんだからそうするべき。

ということで Comipoli にもストック画像でボタンを追加。
具体的には go-next-symbolic 等を。

GTK3 Demo – L’Isola di Niente

ってコレはどうやって ClutterImage に取り込みするのだ?
GtkButton なら簡単なんだけど、手段が解らない。

しかたがないので自作画像で。
XPM は透過属性があるので RGBA_8888 を指定。
#2E2E2E でダークテーマの色とほぼ一致するみたい。

#!/usr/bin/env python3

from gi.repository import GdkPixbuf, Clutter, Cogl

ICON_RIGHT = [
"32 32 3 1",
" 	c None",
".	c #2E2E2E",
"+	c #FFFFFF",
".........+......................",
".........++.....................",
".........+++....................",
".........++++...................",
".........+++++..................",
".........++++++.................",
".........+++++++................",
".........++++++++...............",
".........+++++++++..............",
".........++++++++++.............",
".........+++++++++++............",
".........++++++++++++...........",
".........+++++++++++++..........",
".........++++++++++++++.........",
".........+++++++++++++++........",
".........+++++++++++++++........",
".........+++++++++++++++........",
".........+++++++++++++++........",
".........++++++++++++++.........",
".........+++++++++++++..........",
".........++++++++++++...........",
".........+++++++++++............",
".........++++++++++.............",
".........+++++++++..............",
".........++++++++...............",
".........+++++++................",
".........++++++.................",
".........+++++..................",
".........++++...................",
".........+++....................",
".........++.....................",
".........+......................"]

ICON_LEFT = [
"32 32 3 1",
" 	c None",
".	c #2E2E2E",
"+	c #FFFFFF",
"......................+.........",
".....................++.........",
"....................+++.........",
"...................++++.........",
"..................+++++.........",
".................++++++.........",
"................+++++++.........",
"...............++++++++.........",
"..............+++++++++.........",
".............++++++++++.........",
"............+++++++++++.........",
"...........++++++++++++.........",
"..........+++++++++++++.........",
".........++++++++++++++.........",
"........+++++++++++++++.........",
"........+++++++++++++++.........",
"........+++++++++++++++.........",
"........+++++++++++++++.........",
".........++++++++++++++.........",
"..........+++++++++++++.........",
"...........++++++++++++.........",
"............+++++++++++.........",
".............++++++++++.........",
"..............+++++++++.........",
"...............++++++++.........",
"................+++++++.........",
".................++++++.........",
"..................+++++.........",
"...................++++.........",
"....................+++.........",
".....................++.........",
"......................+........."]

class ComipoliGoButton(Clutter.Actor):
    def __init__(self, right):
        Clutter.Actor.__init__(self)
        if right:
            pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON_RIGHT)
        else:
            pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON_LEFT)
        image = Clutter.Image()
        image.set_data(
            pixbuf.get_pixels(),
            Cogl.PixelFormat.RGBA_8888,
            pixbuf.get_width(),
            pixbuf.get_height(),
            pixbuf.get_rowstride()
        )
        self.set_content(image)
        self.connect("enter-event", self.on_enter_event)
        self.connect("leave-event", self.on_leave_event)
        self.props.opacity = 0x00
        self.set_reactive(True)

    def on_enter_event(self, actor, event):
        self.props.opacity = 0xff

    def on_leave_event(self, actor, event):
        self.props.opacity = 0x00

で。
eog の表示はウザいのでマウスカーソルが乗った時だけ表示させる。
ボタンを大きくしたいので縦に引伸し、これはウインドウ側に実装。

Linux アプリケーション – L’Isola di Niente

で上記スクリーンショットのようになりました。
いや、バージョンが上がったら画像はまた変更するけど。
ストックの取り込みが解ったら変更するかもしれない。

ウインドウの隅にカーソルを移動するだけだから直感的に使えると思う。
そういえばマウスジェスチャってすっかり廃れたよね。
マウスボタンを押しながら移動ってやっぱり全然直感的じゃないのよ。

キモヲタならロッカージェスチャとかホイールのほうがいいとか言うかも。
それを言うのは Windows しか使えない人だけですから。
特に右クリックはコンテキストメニュー以外には使ってはいけない。
というのが現在の Mac と GNOME が共通する思想です。

Comipoli beta3

Comipoli beta2 のメモリ展開に凄い問題が見つかった。
3000x4000px over の画像を役二百枚まとめた CBZ を試しに作ってみた。
ソレを読み込むと…

memory_swap

8GB もあるメモリがアッサリ埋まって swap 領域に突入。
SSD だから我慢できる程度の遅さだけど HDD だと死ねるレベル。

JPEG 圧縮を zip 圧縮しているからファイルサイズは 150MB 程度なのに。
GdkPixbuf に展開するとこんなサイズになってしまうのか。

16GB あれば、、、、、いやそうじゃない。
とにかくメモリ使用量をなんとかしなきゃ。

都度読み込みならたとえ 100000x100000px でも問題無い…
そこまでいくと展開が超遅くなるから誰もやらないってばさ。

JPEG を GdkPixbuf に変換せずにバイナリのまま配列なら…
前後処理丸ごと作り替えになるし表示効率も悪くなるな。
そもそも Python でバイナリの配列も実体はポインタなのかな?
ここらは C でやったほうがモヤモヤしなくて済むのだが。

単純に一定の大きさ以上は縮小が一番効率良さげ。
現在のディスプレイ市場は 1080p が主流。
ならば 1080p よりデカいなら縮小という手段が多分最高効率だと思う。

def iter_zip(self):
    # unzip
    with zipfile.ZipFile(self.zip.get_path()) as o:
        l = o.namelist()
        l.sort()
        self.datas.clear()
        for name in l:
            ext = os.path.splitext(name)[1].lower()
            if ext == ".jpg" or ext == ".jpeg" or ext == ".png":
                data = o.read(name)
                stream = Gio.MemoryInputStream.new_from_data(data)
                p = GdkPixbuf.Pixbuf.new_from_stream(stream)
                # Escape Swap
                if p.get_height() > 1080:
                    rate = p.get_width() / p.get_height()
                    p = GdkPixbuf.Pixbuf.scale_simple(p, 1080 * rate, 1080, GdkPixbuf.InterpType.BILINEAR)
                stream.close()
                yield p

手抜きに見えるけどフルスクリーン時の描写処理が速くなるメリットがある。
と思えるけど Comipoli ではソコは OpenGL がやるので無意味なことは秘密だよ。

いや問題はメモリ使用量だってば!
再起動して同じファイルを読み込んでみる

memory_after

1/10 以下に、まさかこんなに違うとは。
これだったらメモリは 4GB あれば問題無さそう、今時なら普通だよね。

でもやはり展開時間は倍近くになる。
バックグラウンド展開だから気にならないとはいえ、やはり遅い。
czipfile なんて早いらしいモジュールは Python2 用しか見当たらない。

ただデータの転送量が減るおかげか動作が少し速くなるメリットも。
とりあえず又問題が出るまでコレでいこう、まだベータだし。

Kivy GStreamer

動画プレイヤーを 16 年も作り続けてきた筆者が Mac で Kivy を。
だったらこのネタをやらないわけにはいかないだろう。

#!/usr/bin/env kivy

import kivy
kivy.require('1.9.1')

from kivy.app import App
from kivy.uix.videoplayer import VideoPlayer

VIDEO_PATH = "/Users/sasakima-nao/Movies/nmax.wmv"

class YuliemApp(App):
    def build(self):
        playbin = VideoPlayer(source=VIDEO_PATH, state="play",
            options={"allow_stretch": True})
        return playbin

YuliemApp().run()

kivy_gstplayer

と。
特に別途必要なものはなく QuickTime 非対応形式でも再生可能。
VIDEO_PATH は自前のファイルパスに書き換えてね。

正直驚いた、GStreamer がデコーダーを含めて同梱なんだね。
おまけに最初からボタン類やシークバーまで、どんだけ親切なのよ。

kivy.uix.video.Video を使えばボタン類を無しにできる。
でもそれだとビデオサイズがソースサイズ固定になってしまう。
ボタン類は自前描写にしたかったのだが、方法模索中。

Mac でしか試していないので他の環境はわかりません。
何故か何も指定していないのに esc で終了できることに今頃気がつく。
あ、クラス名は気にしないでください。

Kivy BoxLayout

Mac で Kivy (Python3 版)をもう少しやってみた。
Widget が日本語が豆腐になる理由は簡単だった。

「KivyではじめるPythonプログラミング」サポートページ 「Kivyアプリに日本語を表示させる」

こんなことをしなくても普通に ttf ファイルを指定すればいい。
ttc, otf も拡張子まで指定すれば使えるけど otf では何故か文字化けした。
クロスプラットホームにしたいならフォント同梱しか手が無い。

Mac なのに閉じるボタンで終了するのは気になる人もいるかも。
Electron 製アプリが上手くやっているだけに。
最初から command+Q で終了できるようにしているのは評価。

そんなことより、レイアウタがビックリするほど GTK+ と似ている。
ただしパッキング情報は子が持つ、いやソレが普通だと思うんですけど。
更にコンテンツの大きさに合わせて拡大とかはしてくれない。
width, height を固定するには size_hint に NULL を指定する必要あり。

一行エディットとマルチラインエディットは共通で TextInput を使う。
GtkEntry 同様最初から多機能だけど OLE DnD は未対応みたいだな。
日本語が入力できる時とできない時があるがなんだこれ?

__init__(self) も使えるようです。
Python なので親の __init__ を呼び出すのを忘れずに。

そんなこんなを確認するコードを以下に。

#!/usr/bin/env kivy

import kivy

from kivy.app import App
from kivy.uix.button import Button
from kivy.base import EventLoop
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.config import Config

class TestApp(App):
    def __init__(self):
        App.__init__(self)
        self.title = "__init__ も使える"

    def build(self):
        #self.title = "タイトルバー"
        Config.set('graphics', 'width', '300')
        Config.set('graphics', 'height', '150')
        Config.set("kivy", "exit_on_escape", 1)
        # Button
        button = Button(
            text="クリックしてください",
            #font_name="Osaka", # Japanese Font
            #font_name="SFNSText-Regular.otf",
            font_name="ヒラギノ角ゴシック W9.ttc",
            valign="top",
            font_size="20sp" # Default 15sp
        )
        button.bind(on_press = self.on_button_press)
        # Entry
        entry = TextInput(
            font_name="osaka",
            multiline=False,
            size_hint=(1, None), # (width_percent, height_percent)
            height=30
        )
        # Packing
        vbox = BoxLayout(orientation="vertical")
        vbox.add_widget(button)
        vbox.add_widget(entry)
        return vbox

    def on_button_press(self, button):
        button.text += ", しちゃった"

TestApp().run()

kivy_layout

レイアウタがあるのは嬉しいけどコンテンツサイズに合わせてくれないのは痛い。
Clutter ではそういう部品を GTK+ に丸投げすることができるんだけど。
OpenGL ES だけで実現しようとするとすると難しいんだろうな。
なんたって 3D なんだから奥行きという概念があるし。

そういえば前回
「エロゲ専用に落ちぶれた某 OS はまだ WindowsForm 大好きだからシラネ!」
と書いた。

ウチのアクセス解析だけで解るんだけど、確認のため日○ソフトウエアを立ち読み。
やっぱり WindowsForm な記事しかない。

実は会社の業務用アプリは最近 VB6 から WindowsForm に進化(?)した。
Windows でプログラミングの勉強をしている人さぁ、現実はコレだから。

WPF が出てからもう十年たっているのに。
筆者がまったく Windows を使わなくなった最大の理由です。

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 版を作ろうと思ったならコイツだな。
ドキュメントが公式くらいしか無いのがチョッピリ痛い。