GTK+」タグアーカイブ

Notify Signal

すっかり写真ブログ化しているこのブログですが。
今月末には新しい Fedora が出るはずなのでそろそろ本筋でも。
GTK4 のサンプルコードもそろそろ出そろってきたかなって。

GitHub – johnfactotum/quick-lookup: Simple GTK dictionary application powered by Wiktionary

このあたりが解りやすいかな。
省略表記多すぎ、てか文末セミコロンすら絶対に書かない派なのね。
まあ Gjs で GTK4 はこんなふうに書けばいいのかって参考にはなる。

気になったのは notify::is-active シグナル。
アクティブ化の真偽値をこのシグナルで捕まえられるのかな。

#!/usr/bin/gjs

imports.gi.versions.Gtk = '4.0'
const {GObject, Gtk} = imports.gi;

var TestWindow = GObject.registerClass({
    GTypeName: 'TestWindow'
}, class TestWindow extends Gtk.ApplicationWindow {
    _init(app) {
        super._init({
            application: app,
            title: '1'
        });
        try {
            this.connect('notify::is-active', () => {
                if (this.is_active) this.title += '1';
            });
        } catch(e) {
            printerr(`@@@Error@@@@:\n${e.message}`);
            app.quit();
        }
    }
});

var TestApplication = GObject.registerClass({
    GTypeName: 'TestApplication'
}, class TestApplication extends Gtk.Application {
    _init() {
        super._init({
            application_id: 'org.lumix.gh6'
        });
    }
    vfunc_activate() {
        let w = new TestWindow(this);
        w.present();
    }
});

let app = new TestApplication();
app.run(null);

おぉコレは使いどころがあるぞ。
昔筆者がやってた初回起動時間のセコい短縮なんかにも使えるね。
ウインド表示直後に処理 | Paepoi Blog

ところでこの Notify というシグナルって何だろう?

GObject.Object::notify

どうやら GObject のシグナルらしい。
そして is-active は GtkWindow のプロパティ。

Gtk.Window

つまりこのシグナルは何かプロパティをセットした時に吐きだすようだ。
別のプロパティで試してみよう、PyGObject でもイケるかな?

#!/usr/bin/env python3

import gi, sys
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        try:
            self.connect('notify::default-width', self.on_width_change)
        except Exception as e:
            print(e, file=sys.stderr)
            app.quit()

    def on_width_change(self, this, pspec):
        self.set_title(f'width: {self.props.default_width}')

class TestApplication(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id='org.olympus.pen')

    def do_activate(self):
        w = TestWindow(self)
        w.present()

app = TestApplication()
app.run()

width

イケた、ウインドウの横サイズを変更すると即座にタイトルが変更される。
Notify::** シグナルでプロパティを監視することが可能なんですね。
引数の GParamSpec から値を得ることもできると思うけど手段がワカラン。
プロパティを見ればいいだけなので別にいいか。

GTK4 main loop

GTK4 は PyGObject ばかりやっていたけど。
たまには Gjs もやろうかなって、gir なのは同じなんだけど。

gjs/gtk4-template.js at master ? GNOME/gjs ? GitHub

あれ?
GtkApplication を使わないでイケる方法があったんだ。
同様なはずなので PyGObject で書いてみる。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib

class TestWindow(Gtk.Window):
    def __init__(self):
        Gtk.ApplicationWindow.__init__(self)
        btn = Gtk.Button(label='The best camera for wild birds')
        btn.connect('clicked', self.on_button_clicked)
        self.label = Gtk.Label(label='...')
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        box.append(btn)
        box.append(self.label)
        self.set_child(box)
        #self.connect('delete-event', self.on_close_request) # v3
        self.connect('close-request', self.on_close_request)
        self.present()

    def on_button_clicked(self, widget):
        self.label.set_text('MICRO FOUR THIRDS !')

    def on_close_request(self, widget):
        loop.quit()

loop = GLib.MainLoop.new(None, False)
#GLib.set_prgname('LUMIX')
TestWindow()
loop.run()

mft

GMainLoop でも良かったようです、知らなかった。

でも何故かこの方法ではタイトルバーに何も表示されないね。
アプリ名表示は使っていないはずの GtkApplication になる、なんだかな。
ウインドウを作る前に g_set_prgname すればいいんだけどさ。

GtkApplication を使えば今までどおりファイル名が表示される。
いや application-id を指定するとそちらが優先されるみたい。
g_set_prgname 無指定の場合だけの話ですけど。

close-request は delete-event の名前が変わっただけです。
×ボタンを押したよってシグナル、destroy は破棄されたよってシグナル。
GtkApplication を使う場合はこんな処理いりませんからね。

しかしのんびりしている間に GNOME 41 が出てしまった。

GNOME 41 Release Notes

Multitasking って GSettings で変更できていたジャン。
GUI 設定が付いただけ、いや筆者はデフォルトで使うけど。
それよりランチャを左に戻す設定が欲しい、macOS でも左にしているんだ。
ちなみに普段は隱して三本指スワイプでランチャを出す GNOME3 式で使っている。

The GTK Project

GTK の stable は 4.2.1 か、こっちは今までどおり奇数が開発版なのか。
でもドキュメントは 4.5 かよ、バージョンの関連性がもうよくワカラン。

関係ないけどサンプルコードでカメラネタはやっぱりイマイチだ。
やっぱりスズキのバイクが一番だなって。

GTK4 Clipboard (String)

GTK4 で Clipboard を使うテスト。
文字列のコピペはできた、ただし自分のインスタンス内のみで。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
#gi.require_version('Gdk', '4.0') # not need
from gi.repository import Gtk, Gdk

class TestWindow(Gtk.ApplicationWindow):
    '''
        GTK4 Clipboard @ String Copy
        This Code is Instance Only
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        self.clip = self.props.display.get_primary_clipboard()
        print(self.clip) #=> GdkWaylandPrimary (GdkClipboard)
        '''
        self.clip = self.get_clipboard()
        self.clip = self.props.display.get_clipboard()

        display = Gdk.Display.get_default()
        self.clip = display.get_primary_clipboard()
        self.clip = display.get_clipboard()

        # local is All True...
        print(self.clip.props.local)
        '''
        self.clip.set('スズキのバイクはカッコイイ')
        print(self.clip.get_formats().to_string()) #=> { gchararray, text/plain;charset=utf-8, text/plain }
        #
        #self.clip.set_text('No GIR Binding') # Error!
        #
        self.clip.read_text_async(None, self.read_text_async_cb)
        #
        self.label = Gtk.Label(label='Motor Cycle')
        self.set_child(self.label)
        self.set_default_size(400, 100)
        self.present()

    def read_text_async_cb(self, clip, res):
        text = clip.read_text_finish(res)
        self.label.set_label(text)

class TestApplication(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id='org.suzuki.katana')

    def do_activate(self):
        TestWindow(self)

app = TestApplication()
app.run()

clip

何をどうやっても local Property が True になってしまう。
False にしないと Gedit 等の外部インスタンスとはコピペできない。
バグなのか?
他にやらないといけないことがあるのか?

それと set_text 等がバインドされていないのはどうなんだ?
set で文字列を渡したら普通にセットできたけど「ん?」って感じ。
バイナリの転送は GdkContentProvider でいいのかな?

GtkGestureDrag

GtkGesture ラスト、GtkGestureDrag を。
マウスでドラッグすると文字列が着いてくるサンプルコード。

#!/usr/bin/env python3
 
import gi, sys
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

class Win(Gtk.ApplicationWindow):
    '''
        GtkGestureDrag Sample Code
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        # var
        self.offset_x = 50
        self.offset_y = 50
        self.draw_x = 0
        self.draw_y = 0
        # gesture drag
        drag = Gtk.GestureDrag()
        drag.connect('drag-begin', self.on_gesture_drag_begin)
        drag.connect('drag-update', self.on_gesture_drag_update)
        drag.connect('drag-end', self.on_gesture_drag_end)
        self.add_controller(drag)
        # view
        self.view = Gtk.DrawingArea()
        self.view.set_draw_func(self.view_draw_func)
        self.set_child(self.view)
        # resize
        self.set_default_size(400, 300)

    def view_draw_func(self, da, cr, width, height):
        cr.move_to(self.draw_x + self.offset_x, self.draw_y + self.offset_y)
        cr.set_font_size(36)
        cr.show_text('Drag and Drop')

    def on_gesture_drag_begin(self, ges, offset_x, offset_y):
        pass

    def on_gesture_drag_end(self, ges, offset_x, offset_y):
        self.offset_x += offset_x
        self.offset_y += offset_y

    def on_gesture_drag_update(self, ges, offset_x, offset_y):
        self.draw_x = offset_x
        self.draw_y = offset_y
        self.view.queue_draw()

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

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)

drag

それと残りに GtkGestureStylus なんてのがあるけど。
どう考えてもスタイラスペン用途だよなって。
持っていないし GNOME てか Linux での使い道も思いつかない。
クリスタが Linux 対応するはずがないし、適材適所だよ。
なので無視でいいかなって。

ところでコレはファイルマネージャからのドロップではない。
GTK4 は drag_dest_add_uri_targets が使えないね。

Drag & dropping files with GTK4 – Platform – GNOME Discourse

こんなのを見つけたけど同じ所でクラッシュする。
うーん、今日もゲンナリするくらい進まない。

GtkGestureSwipe on Macbook

GTK4 ページを作ろうとしていますが、進んでいない。
てゆーか GtkGestureSwipe が Macbook で動作しません。

1

そもそも Eye of GNOME にて上記が動作がしません。
ちなみに X11 でログインすると拡大や回転もできなくなります。

2

Macbook Air 2011 でも Wayland がデフォルトになります。
この状態でスワイプ以外は全部動く、やはり X11 ではズームできません。

#!/usr/bin/env python3

import gi, sys
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

class Win(Gtk.ApplicationWindow):
    '''
        Fedora 34 on Macbook Air 2011
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        # var
        self.mem_pan = -1
        # touch || mouse button
        click = Gtk.GestureClick()
        click.connect('released', self.on_gesture_click_released)
        # long press
        lpress = Gtk.GestureLongPress()
        lpress.connect('pressed', self.on_gesture_long_press_pressed)
        # pan
        pan = Gtk.GesturePan(orientation=Gtk.Orientation.VERTICAL)
        pan.connect('pan', self.on_gesture_pan)
        # swipe (no...)
        swipe = Gtk.GestureSwipe(touch_only=True)
        swipe.connect('swipe', self.on_gesture_swipe_swipe)
        # zoom
        zoom = Gtk.GestureZoom()
        zoom.connect('scale-changed', self.on_gesture_scale_change)
        #
        self.label = Gtk.Label(label='start')
        self.label.add_controller(click)
        self.label.add_controller(lpress)
        self.label.add_controller(pan)
        self.label.add_controller(swipe)
        self.label.add_controller(zoom)
        # resize
        self.set_child(self.label)
        self.set_default_size(600, 600)

    def on_gesture_pan(self, ges, direction, offset):
        if direction != self.mem_pan:
            self.mem_pan = direction
            if direction == Gtk.PanDirection.UP:
                self.label.props.label += '\npan up'
            else:
                self.label.props.label += '\npan down'

    def on_gesture_long_press_pressed(self, ges, x, y):
        self.label.props.label += '\nlong press'

    def on_gesture_click_released(self, ges, n_press, x, y):
        if n_press == 2:
            self.label.props.label += '\ndouble clicked'

    def on_gesture_swipe_swipe(self, ges, x, y):
        self.label.props.label += f'\nswipe x={round(x)} y={round(y)}'

    def on_gesture_scale_change(self, zoom, scale):
        w = round(600 * scale)
        h = round(600 * scale)
        self.set_default_size(w, h)

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

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)

3

ちなみに Macbook に Fedora 34 を入れた場合。
以下のトラックパッドジェスチャがデフォルト状態の GNOME 40 で可能。

三本指の上スワイプでアクティビティ画面。
もう一度同じ動作でアプリケーションランチャ。
三本指の横スワイプで仮想デスクトップの異動。
Eye of GNOME にて画像の拡大縮小と回転。

探せばジェスチャを追加できるサードパーティ拡張もあるんだけど。
上記のデフォルトとコンフリクトすると思う、試していないけど。
X11 でログインするとやはり何も動作しません。

考えてみれば二本指ジェスチャってスクロール動作と同じだし。
スワイプジェスチャと両立している macOS ってスゲェんだなって。
サーフェスとかは触ったことすら無いので知らない。

しかし本当に思う、やっぱり Macbook は macOS で使うのが一番だよ。
Fedora で使うと command と control キーの違いをどうしても間違えるし。