GTK4: gtk_window_begin_move_drag

前々回に約束したドラッグで移動させる処理をなんとかした。

gtk_window_begin_move_drag は GTK4 で廃止された。
けれどこの関数って Gdk の処理を簡素化しただけだったみたい。
ということで下記をご覧ください。

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

# Picture File
PNGFILE = 'test.png'

# CSS
APP_CSS = 'window { background-color: rgba(255, 255, 255, 0); }'.encode('utf-8')

class Win(Gtk.ApplicationWindow):
    '''
        GTK4: No Decorated Window
    '''
    def __init__(self, app):
        try:
            Gtk.ApplicationWindow.__init__(self, application=app, decorated=False)
            # Transparent
            provider = Gtk.CssProvider()
            provider.load_from_data(APP_CSS)
            context = self.get_style_context()
            context.add_provider_for_display(
                self.get_display(),
                provider,
                Gtk.STYLE_PROVIDER_PRIORITY_USER)
            # Mouse Move Signal
            click = Gtk.GestureClick()
            click.connect('pressed', self.on_gesture_click_pressed)
            self.add_controller(click)
            # Draw
            self.pixbuf = GdkPixbuf.Pixbuf.new_from_file(PNGFILE)
            da = Gtk.DrawingArea()
            da.set_draw_func(self.da_draw_func)
            self.set_child(da)
            # Resize
            self.set_default_size(self.pixbuf.get_width(), self.pixbuf.get_height())
        except Exception as e:
            print(e, file=sys.stderr)
            app.quit()

    def da_draw_func(self, da, cr, width, height):
        Gdk.cairo_set_source_pixbuf(cr, self.pixbuf, 0, 0)
        cr.paint()

    def on_gesture_click_pressed(self, click, n_press, x, y):
        '''
            GTK4: gtk_window_begin_move_drag
        '''
        button = click.get_button()
        toplevel = self.get_surface() # GdkToplevel
        display = self.get_display()
        seat = display.get_default_seat()
        device = seat.get_pointer()
        s, win_x, win_y = device.get_surface_at_position()
        #print(f'{win_x}, {win_y}')
        time = device.get_timestamp()
        toplevel.begin_move(device, button, win_x, win_y, time)

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

    def do_startup(self):
        Gtk.Application.do_startup(self)
        # set Ctrl+Q
        self.set_accels_for_action('app.quit_action', ['<Control>Q'])
        quit_action = Gio.SimpleAction(name='quit_action')
        self.add_action(quit_action)
        quit_action.connect('activate', lambda a, p: self.quit())
        #
        Win(self)

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

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

Gdk の gdk_toplevel_begin_move という関数を使うんだけど。
与える引数がが gtk_window_begin_move_drag と同じじゃん。
いやその引数は自力で得る必要があるんだけーがさ。
とにかく上記で枠無しウインドウの移動ができるようになりました。

注意点として、前々回は released シグナルにしていたけど。
マウスドラッグは pressed シグナルでやらないと動作しません、当然だよね。
これに気が付かず筆者は一時間くらい無駄な時間を使ってしまった。

それと、コレをやると W クリックを検出できなかった。
press した時点で移動処理に入るので初回にリセットされるようだ。
非同期にするとか何か手段はあるのだろうけど、今日はココまで。
今回は Ctrl+Q で終了するようにした、GTK3 と変わっていなかった。

解ってしまえばこんなに簡単だったのね。
GtkGestureDrag でなんとかしようと無駄なコードをイッパイ書いたよ、ばかやろう。

Haru

今日の五条川、周辺な近所の田畑。

kiji

キジの夫婦、いやー久々に見つけた。
こんなにデカいんだからもっと見かけてもおかしくないはずだが。

kochidori

コチドリ、いやー夏鳥が見つかる時期ですね。
ここらでチドリはコイツしか見かけないなって。

hibari

ヒバリ、いやーやっとこさ撮れた。
今月頭からアホみたく鳴いているのに保護色強すぎで全然見つからない。

tsugumi

ツグミ、いやーってお前冬鳥だろ。
他の冬鳥は全然見なくなったけどコイツらだけは時期が違うのかな。

keri

ケリ、いやーお前やかましいんだよ!
人に見つかりたくないんならツグミみたく黙っていろよと。

他にもスズメやカワラヒワがやかましい、春ですね。
しかし近所だけでこんな色々な種類の野鳥がいるんですねって。
野鳥撮影は楽しいというより勉強になる。

GTK4: Transparent Window

GTK4 で背景透過ウインドウを作る。

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

# 背景透過な PNG 画像を用意して指定してください
PNGFILE = 'test.png'

APP_CSS = 'window { background-color: rgba(255, 255, 255, 0); }'.encode('utf-8')

class Win(Gtk.ApplicationWindow):
    '''
        decorated プロパティだけでは透過できなくなりました
        CSS にて透明にする必要がある
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, decorated=False, application=app)
        # CSS にて透過させる
        provider = Gtk.CssProvider()
        provider.load_from_data(APP_CSS)
        context = self.get_style_context()
        context.add_provider_for_display(
            self.get_display(),
            provider,
            Gtk.STYLE_PROVIDER_PRIORITY_USER)
        # 画像のロード
        self.pixbuf = GdkPixbuf.Pixbuf.new_from_file(PNGFILE)
        # button-press-event 等は無くなりました
        ges = Gtk.GestureClick()
        ges.connect('released', self.on_gesture_click_released)
        self.add_controller(ges)
        # Draw
        da = Gtk.DrawingArea()
        da.set_draw_func(self.da_draw_func)
        self.set_child(da)
        # 画像サイズにリサイズ
        self.set_default_size(self.pixbuf.get_width(), self.pixbuf.get_height())

    def da_draw_func(self, da, cr, width, height):
        Gdk.cairo_set_source_pixbuf(cr, self.pixbuf, 0, 0)
        cr.paint()

    def on_gesture_click_released(self, ges, n_press, x, y):
        '''
            ダブルクリックで終了
        '''
        if n_press == 2:
            self.props.application.quit()

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)

transpalent

面倒になった。

GTK3 は decorated を False にしてウインドウの draw シグナルを処理。
だけで良かったけれど GTK4 は CSS を使わないと透過できなくなっていた。

それに draw シグナルが廃止されたので GtkDrawingArea を使う必要がある。
CSS で透過させれば上に乗せた GtkDrawingArea も背景透過になるのは助かる。

ついでに、Gtk.StyleContext.add_provider_for_screen が廃止されていた。
GDK4 は GdkScreen が廃止されていたのか、気が付かなかった。
add_provider_for_display に変更、引数は同じ感じでイケた。

そんなこんなでやっとココまでできた。
後はドラッグで移動させる処理をなんとかせねば。

日本語情報が無いのはいつもの事だけど英語情報も全然無くて厳しい。
GTK3 は GTK2 のままから徐々に変更だったから楽だったんだよなって。

Yozakura

時期外れになる前に、日曜夜の五条川。

yozakura

せっかくの満開が曇りや雨で残念なら夜桜を撮影すればいいじゃない!

yozakura

と、月曜日に貼ろうと思っていたけど月曜は晴天だったので。

yozakura

ちなみに照明はもう撤去されています。

yozakura

さて五条川も葉桜になってきました。
梅から続く写真好きな人へのボーナスタイムも終わり。
次はゴールデンウイークの野鳥ボーナスタイムが待っている。
あ、サクラカワセミー今年も撮っていない。

GTK4: activate-link

先日書いた GtkLabel のマークアップで a href ですけど。
GTK4 ではクリックすると activate-link シグナルが発生するようだ。
確認ダイアログを出して振り分けなんかができますね。
ということで書いてみたんですけど。

#!/usr/bin/env python3

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

class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        try:
            Gtk.ApplicationWindow.__init__(self,
                application=app,
                default_width=300,
                default_height=300)
            label = Gtk.Label(
                label = '<a href="trash:///">Open the Trash</a>',
                use_markup = True)
            label.connect('activate-link', self.on_label_activate_link)
            self.set_child(label)
        except Exception as e:
            print(e, file=sys.stderr)
            app.quit()

    def on_label_activate_link(self, label, uri):
        msg = Gtk.MessageDialog(
            buttons = Gtk.ButtonsType.YES_NO,
            modal = True,
            transient_for = self,
            text = 'Do you want to open the trash can?')
        msg.connect('response', self.on_label_message_response, uri)
        msg.show()
        # Stop the Open Link
        return True

    def on_label_message_response(self, dialog, response_id, uri):
        if response_id == Gtk.ResponseType.YES:
            GLib.spawn_command_line_async(f'gio open {uri}')
        dialog.destroy()

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

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

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

ちと面倒。

activate_link のハンドラは True を戻さないと普通に実行してしまう。
msg.show() は非同期なのでとにかく True を戻す。
uri の処理は別シグナルなので user_data に入れて渡す。
実行は gio コマンドで、という流れになりました。

複数のリンクを作ったら全部コレ書かなきゃいけないのか、みたいな。
いやプログラミングってそういうものですけど。