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

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 だけで配列の配列が完成、唖然とした。
調子に乗ってサイズ保存もタプルに変更したら前のキーが残ってしまった。
これどうやって消すのかな?今度調べる。

Google Chrome 51 Chach

Google Chrome はキャッシュの仕様が変わってしまったね。
FLV や MP4 の動画キャッシュにヘッダが着くようになった。

Nautilus をスクリプトで拡張 – L’Isola di Niente
つまり上記に書いた手段が使えない。
YouTube は随分前から駄目だけどニコニコなんかはまだキャッシュに残るし。

いやまあ強引にダウンロードする手段はいくらでもあるんだけど。
せっかくキャッシュにあるのにもう一回通信なんてアホ臭い。
GHex 等で地味に余計なヘッダを削るなんて面倒臭い。

ならば余計なヘッダを取っ払ってからコピーすればいいじゃない!
Nautilus スクリプトです、Linux じゃない人は参考程度に。

FLV が取得できなかったので小変更

#!/usr/bin/env python3

"""
    chrome_catch (~/.local/share/nautilus/scripts)
    Copy Chrome Cache File to Nautilus Current Directry
    Google Chrome 51 Version
"""

import os
from gi.repository import GLib

FLV_FILE = "FLV".encode("utf-8")
MP4_FILE = "ftyp".encode("utf-8")

# Src Directory is Google Chrome Cache Path
src_path = os.path.expanduser("~/.cache/google-chrome/Default/Cache")
# Copy Directory is Nautilus current directory
dst_path = GLib.filename_from_uri(GLib.getenv("NAUTILUS_SCRIPT_CURRENT_URI"))[0]

ls = os.listdir(src_path)
for f in ls:
    src = os.path.join(src_path, f)
    # size < 1MB is continue:
    if os.path.getsize(src) < 1048576:
        continue
    with open(src, "rb") as o:
        data = o.read()
        data2 = data
        try:
            i = data.find(MP4_FILE)
            o.seek(i-4)
            with open("{0}/{1}.mp4".format(dst_path, f), "wb") as ff:
                ff.write(o.read())
        except Exception as e:
            pass
        #data2 = o.read()
        try:
            i = data2.find(FLV_FILE)
            o.seek(i)
            with open("{0}/{1}.flv".format(dst_path, f), "wb") as ff:
                ff.write(o.read())
        except Exception as e:
            pass

find で見つからないと例外なのでコレでいいと思う。
早速ニコニコでバイク動画を再生して、よしよし拾うことができた。
いやバイク動画目的ですよ、決してえっち(以下略