Python」タグアーカイブ

gst direction next frame

JavaScript でスマホゲームもいいけどメインの GTK+ もやらねば。
久々に Y901x の更新、ポーズからコマ戻しが上手くいく方法を見つけたので。

# self.player @ playbin
# self.settingwin @ My Setting

def set_next_frame(self, rate):
    """
        1 frame up down (rate 1.0 or -1.0)
    """
    if self.settingwin.direction != rate:
        self.settingwin.direction = rate
        self.set_playback_direction(rate)
    event = Gst.Event.new_step(Gst.Format.BUFFERS, 1, 1.0, True, False)
    self.player.send_event(event)

def set_playback_direction(self, rate):
    """
        Change direction
    """
    pos = self.player.query_position(Gst.Format.TIME)[1]
    if rate >= 0.0:
        self.player.seek(
                rate,
                Gst.Format.TIME,
                Gst.SeekFlags.FLUSH | Gst.SeekFlags.ACCURATE,
                Gst.SeekType.SET, pos,
                Gst.SeekType.SET, -1)
    else:
        self.player.seek(
                rate,
                Gst.Format.TIME,
                Gst.SeekFlags.FLUSH | Gst.SeekFlags.ACCURATE,
                Gst.SeekType.SET, 0,
                Gst.SeekType.SET, pos)

def on_play(self, widget, data=None):
    if self.settingwin.direction == -1.0:
        self.settingwin.direction = 1.0
        self.set_playback_direction(1.0)
    if self.player.get_state(1)[1] == Gst.State.PLAYING:
        self.player.set_state(Gst.State.PAUSED)
    else:
        self.player.set_state(Gst.State.PLAYING)

逆転させる手続きを分離して send_event するだけだった。
GLib.idle_add とか色々試したのに実はこんなに簡単だった…

これなら関数を抜けるので逆転した後で 1 コマ移動ということになるのかな。
再生開始で正転に戻すのを忘れないようにと。

それと手段が全然解らず困り果てていたシークバー上のマウスホイール。
event.direction 引数が Gdk.ScrollDirection.SMOOTH になるのよ。
Gdk.ScrollDirection.UP, DOWN になる記事以外見当たらなかったし。

http://nullege.com/codes/show/src%40p%40i%40pitivi-HEAD%40pitivi%40utils%40widgets.py/1035/gi.repository.Gdk.ScrollDirection.UP/python

そういうことだったのか!
やっぱり GPL のコードを見たほうが圧倒的に参考になるね。
何も作品を作っていなさそうな人って何故勉強しているのか、日本人に多過ぎ。

def on_seek_scroll_event(self, widget, event):
    """
        Mouse Wheel event
        widget @ GtkScale
    """
    delta = 0
    if event.direction in [Gdk.ScrollDirection.UP, Gdk.ScrollDirection.RIGHT]:
        delta = 1
    elif event.direction in [Gdk.ScrollDirection.DOWN, Gdk.ScrollDirection.LEFT]:
        delta = -1
    elif event.direction in [Gdk.ScrollDirection.SMOOTH]:
        unused_res, delta_x, delta_y = event.get_scroll_deltas()
        if delta_x:
            delta = math.copysign(1, delta_x)
        elif delta_y:
            delta = math.copysign(1, -delta_y)
    if delta:
        location = widget.get_value() - (delta * 10000000000)
        if location > 0 and location < self.playinfo.duration:
            self.set_play_position(location)
            self.put_time_status(location)

これで約 10 秒送りなホイールスクロールが可能になった。
Fedora 20 でしか試していないけど多分 GNOME なら大丈夫だろう。
次バージョン早く出ないかなぁ、20 が安定しているからいいけど半年サイクルに慣れ過ぎた。

Mini GtkButton

Gedit のステータスバーにあるみたいなちっちゃいボタンが使いたい。

gedit_button

タッチパネルを考えると時代に逆行ですけど。
主に確認用として表示させボタンは補助目的としてならアリかと。
実際 Gedit でもこのボタンを使っている人は少ないだろうし。

普通に GtkButton を作ると当然隙間が多く小さくはならない。

#!/usr/bin/env python3

from gi.repository import Gtk

class Win(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        vbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        for s in "ABC":
            button = Gtk.Button.new_with_label(s*10)
            vbox.pack_start(button, False, False, 0)
        self.add(vbox)
        self.show_all()

Win()
Gtk.main()

どうやっているか調べると CSS を使っていた。
Style プロパティの border と padding の値を全部ゼロにすればいいらしい。
ようするに HTML の CSS と同様。

GtkButton はコレしか使わないなら直接 CSS 指定すればいい。
しかしこういう部品は GtkButton のサブクラスを作ったほうが使い易い。
ので gtype を指定した class を作ってこんな感じにすると便利。

#!/usr/bin/env python3

from gi.repository import Gtk

style = """
MiniButton {
    -GtkButton-default-border : 0;
    -GtkButton-default-outside-border : 0;
    -GtkButton-inner-border: 0;
    -GtkWidget-focus-line-width : 0;
    -GtkWidget-focus-padding : 0;
    padding: 0;
}"""

class MiniButton(Gtk.Button):
    __gtype_name__ = "MiniButton"
    def __init__(self, label):
        Gtk.Button.__init__(self, label=label)
        # css
        provider = Gtk.CssProvider()
        provider.load_from_data(style.encode("utf-8"))
        context = self.get_style_context()
        context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
        #self.set_relief(Gtk.ReliefStyle.NONE)

class MiniButtonWin(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        vbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        for s in "ABC":
            button = MiniButton(s*10)
            vbox.pack_start(button, False, False, 0)
        self.add(vbox)
        self.show_all()

MiniButtonWin()
Gtk.main()

minibutton

こんなにちっちゃくなりました。
css の指定で Widget のサイズも変わるんだね、これは面白い。

GtkHeaderBar and GStreamer

なんということだ。
GtkHeaderBar を set_titlebar したら GStreamer 再生画面が表示できない。
だから Totem 3.10 も使っていなかったのか、OpenGL の干渉?
やはり Widget を表示できるはずがない部分に描写するのは色々影響が出るよね。

動画プレイヤー(GStreamer フロントエンド)を作る – L’Isola di Niente
筆者が書いた上記に下記を追記すれば解ると思う。
今現在は GNOME 3.10 の人しか試せないけど。

hbar = Gtk.HeaderBar()
hbar.set_show_close_button(True)
self.set_titlebar(hbar)

逃げ道で GtkBox に pack_start なら問題なく GStreamer は使える。

hbar_gst

なんとかなる手段が解るまでしばらくはコレで進めていこう。
ClutterGst なら大丈夫なのかな?
しかし Clutter では Ubuntu の人は Clutter を入れてくれになってしまうのがチト。

Ubuntu 14.04 LTS: Pictures of First Set of Proposed New Icons | Unixmen

Ubuntu 14.04 LTS は GTK+ が 3.10 になり GtkHeaderBar はタイトルバーの下でお茶を濁すみたい。
予想通りだけど本当に魅力の無いディストリになってしまったなぁ、そのうち Mageia にも負けるぞ。
ま、Ubuntu がコレなら pack_start でいいかなと思ったり。

で、どうしても GtkHeaderBar にしたいのは理由があって。

Initiatives/GnomeGoals/HeaderBars – GNOME Wiki!

GNOME は全部 GtkHeaderBar に切り換えるつもりマンマンですし。

gEdit 3.12 Migrates to HIG 3! | woGue

GNOME 3.12 はついに Gedit さえもメニューバーがなくなる。
Totem もゴロッと変わるようだ、やばい浦島太郎になってしまう。

更に今日気が付いたけど Gtk 3.10 以降は GtkUIManager が非推奨になってしまった。
GtkActionGroup, GtkRadioAction, GtkToggleAction も非推奨に。
思いっきりコレらを利用している自アプリは変更せざるをえなくなってしまった。

こんなのは Linux では毎度のことでして。
それを楽しめないフヌケは使うなというスタンスに痺れるZE!

つか覚書ページで上記をお勧めしている部分を全書き換えしなきゃ!
チップスページを書くための実験用と化している我がアプリ達であった。

GtkListBox

GTK+ 3.10 で追加された Widget に GtkListBox がある。

Windows でいうところの LISTBOX とは全然違って GtkBox のサブクラス。
GtkListStore のデータを表示するのではなく GtkWidget を普通に表示する。
その詰め込んだ GtkWidget を選択可能にする、ということみたい。

何に利用すれば便利なのかよく解らない。
自力で色々試しても GtkTreeView で別にイイじゃんと思ってしまう。
GtkTreeView の代わりに使うとデータバインドができなくて不便なだけ。

使い道が解らないのも悔しいのでもっと調べてみる。
GNOME(GTK+) 3.10 で導入されたのだからどこかで使っているはず。
どうやらユニバーサルアクセスや検索の設定ダイアログで使っているっぽい。

univ

久々にこのダイアログを見たけどこんなに変わっていたのか。
そういえば Android スマートフォンの設定画面がこんな感じだ。
ListBox という名前に惑わされて想像力が偏っていたみたい。
よし使い道はなんとなく理解した、次はサンプルコードを探そう。

https://github.com/bratsche/gtk-/blob/master/tests/testlist.c

良さげなのを見つけたけど gtk_main_quit(); 処理が無いのは何故だろう。
海外を探すとこんなのが多いよね、ささやかなイジワルなのでしょうか。
端末からテスト起動して Ctrl+C で終了するだけだから気にしていないだけかも。

とりあえず上記を参考にユニバーサルアクセスダイアログっぽいのを作ってみる。
もちろん Python で、C は面倒臭いもん。

#!/usr/bin/env python3

from gi.repository import Gtk

CSS = '''
GtkListBoxRow {
    border-width: 1px;
    border-style: outset;
    border-color: lightgray;
}'''

DATA = ["YAMAHA", "HONDA", "KAWASAKI", "SUZUKI"]

class Row(Gtk.ListBoxRow):
    def __init__(self, label):
        Gtk.ListBoxRow.__init__(self)
        self.label = Gtk.Label(label)
        self.switch = Gtk.Switch()
        hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        hbox.pack_start(self.label, False, False, 0)
        hbox.pack_end(self.switch, False, False, 0)
        self.add(hbox)
        self.show_all()

class ListWin(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        # GtkCssProvider
        provider = Gtk.CssProvider()
        provider.load_from_data(CSS.encode("utf-8"))
        # GtkStyleContext
        Gtk.StyleContext.add_provider_for_screen(
                self.get_screen(),
                provider,
                Gtk.STYLE_PROVIDER_PRIORITY_USER)
        # ListBox
        listbox = Gtk.ListBox()
        listbox.connect("row-activated", self.on_listbox_row_activated)
        for data in DATA:
            row = Row(data)
            listbox.add(row)
        # Selection
        combobox = Gtk.ComboBoxText.new()
        combobox.append_text("Gtk.SelectionMode.NONE")
        combobox.append_text("Gtk.SelectionMode.SINGLE")
        combobox.set_active(listbox.get_selection_mode())
        combobox.connect("changed", self.on_combobox_changed, listbox)
        # CheckButton
        checkbutton = Gtk.CheckButton.new_with_label("Single Click Mode")
        checkbutton.set_active(listbox.get_activate_on_single_click())
        checkbutton.connect("clicked", self.on_checkbutton_clicked, listbox)
        # Pack
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 20)
        vbox.pack_start(listbox, False, False, 0)
        vbox.pack_start(combobox, False, False, 0)
        vbox.pack_start(checkbutton, False, False, 0)
        self.add(vbox)
        self.set_border_width(10)
        self.show_all()

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

    def on_listbox_row_activated(self, listbox, row, data=None):
        b = row.switch.get_active()
        row.switch.set_active(not b)

    def on_combobox_changed(self, combo, listbox):
        b = combo.get_active()
        listbox.set_selection_mode(b)

    def on_checkbutton_clicked(self, button, listbox):
        b = button.get_active()
        listbox.set_activate_on_single_click(b)

ListWin()
Gtk.main()

listtest

データを生で管理するのではなく構造体やクラスにするといいみたい。
ソート機能はたとえばよくクリックするものをカウントして順に並べるとかに使えそう。
GtkBox の各ペーンに選択機能とクリック感知機能を追加したと考えればよさげ。
名前に惑わされなくなると結構使い道が思い付く、不思議なものです。

Gst step

Y901x 1.1.0 を公開。
多重起動防止は行わないけど GtkApplicationWindow 化。
アプリケーションメニューに About を移動、新しいウインドウ追加。

つまり今の GNOME アプリっぽくしたかっただけ。
次でメニューバーを取っ払う予定、どうにでもなれ。

おまけでポーズ時のみ左右矢印キーでコマ送りできるように。
しかし Gst.Event.new_step() 関数ではゼロ以下の指定ができない。
ようするにコマ送りはできるけどコマ戻しはできない。

self.player.seek() の第一引数をマイナスにすると逆転再生できる。
実はその状態でコマ送りするとコマ戻しになる、なんとも変な感じだけど。

なので一度逆転させて送った後に元に戻せばイケるはず。
なんだけどちっとも上手く動作してくれないんだな、これが。

tinbergen/tinbergen.py at master ? biogeo/tinbergen ? GitHub

PyGtk の頃から同じだったみたい。
やっぱりみんな最初はそうしようとするよね。

しかたがないのでフレームレートから自力計算でシーク処理。
したんだけど上手く動くファイルがほとんどないや。
GstSeekFlags をどう変更しても変わらないってどうよ。

二秒送りとかなら少しはマシだけどソレさえ綺麗に動かない場合がある。
Avidemux くらいの細かいコマ戻しが理想なんだけど、どうすれば…

ちなみに上記をまんま PyGI 化してもフレームレートは取得できない。

framerate = 0

vsink = self.player.props.video_sink
if vsink:
    for pad in vsink.pads:
        caps = pad.get_current_caps()
        success, num, denom = caps.get_structure(0).get_fraction('framerate')
        framerate = num

こんな方法を使ったけどもっとイイ方法があるかも。