GTK+」タグアーカイブ

Adw.AlertDialog

本日は休日出勤無しだが雨、野鳥撮影ヤメにしてプログラミングでも。
とりあえず Adw.AlertDialog を試しに書いてみることにした。

#!/usr/bin/env python3

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

class Win(Adw.ApplicationWindow):
    '''
        Gtk.ApplicationWindow is Gtk-CRITICAL
        Widget of type “AdwAlertDialog” already has an accessible role of type “GTK_ACCESSIBLE_ROLE_GENERIC”
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Adw.ApplicationWindow.__init__(self, application=a)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        # Header
        title = Adw.WindowTitle(title='test')
        header = Adw.HeaderBar(title_widget=title)
        vbox.append(header)
        for s in ('Simple', 'Yes No', 'Vertical'):
            # Button
            button = Gtk.Button(label=s)
            button.connect('clicked', self.on_button_clicked, s)
            vbox.append(button)
        # self
        self.set_content(vbox)
        self.set_default_size(600, 500)

    def on_button_clicked(self, button, text):
        match text:
            case 'Simple':
                alert = Adw.AlertDialog(body='Same for ESC')
                alert.add_response('close', 'OK')
                alert.present(self)
            case 'Yes No':
                alert = Adw.AlertDialog(body='Close to the Edge')
                # add_responces is no...
                alert.add_response('close', 'No')
                alert.add_response('yes', 'Yes')
                alert.set_response_appearance('yes', Adw.ResponseAppearance.SUGGESTED)
                alert.choose(self, None, self.on_dialog_responce)
            case 'Vertical':
                alert = Adw.AlertDialog(heading='Head', body='body', prefer_wide_layout=True)
                alert.add_response('close', 'Cancel')
                alert.add_response('no', 'Destroy this document')
                alert.add_response('ok', 'Save this document')
                alert.set_response_appearance('ok', Adw.ResponseAppearance.SUGGESTED)
                alert.set_response_appearance('no', Adw.ResponseAppearance.DESTRUCTIVE)
                alert.choose(self, None, self.on_dialog_responce)
            case _:
                alert = Adw.AlertDialog(body='???')
                alert.add_response('close', 'ok')
                alert.present(self)

    def on_dialog_responce(self, alert, res):
        text = alert.choose_finish(res)
        match text:
            case 'ok':
                print('Saved')
            case 'no':
                print('Discarded')
            case 'yes':
                print("You're a fan of Yes")
            case 'close':
                print('Oh my good!')

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

GtkAlertDialog

Gtk.ApplicationWindow から呼び出ししても普通に動くんだけど。
Gtk-CRITICAL エラーを stdout に吐くのを気にしなければ、ですけど。
素直に Adw.ApplicationWindow から呼び出したほうが無難。

それと呼び出し Window より Dialog のほうが大きいと Warning になる。
ボタンは基本が横並びで横幅がはみ出しする場合は自動的に縦並びになる。
強制縦並びにしたければ prefer-wide-layout プロパティで可能。

id の文字列は日本語でも使えた、Python だからかもだけど。
外国人も検索で来るかもだからここでは英語にしましたけど。

Adw.MessageDialog 同様に Widget を載せることもできる。
gnome-text-editor で変更破棄すればどんな感じなのかは見れます。

Adw も洗練された Widget という謳い文句らしくなってきましたね。
というのを Kate というダサい Qt のエディタで書いているのがなんとも。
gnome-text-editor に拡張機能付けてくれないかなぁ、見た目はいいのに。
スニペットのショートカット挿入と html のエスケープ機能だけでもいいのに。

signal

先日 Python コードが実行できなくなっていて焦った。
でもソースを他のディレクトリに移動したら普通に動いた。
パーミッションや SE Linux コンテキストは問題無い、あれ?

本日原因が判明。
signal.py というファイルが同一ディレクトリにある、中身は関係ない。
from gi.repository import *** を行う。
という条件を満たすとエラーになる、他の from なら問題ないようだけど。
ついでに __pycache__ も同一ディレクトリに作られてしまう。

signal

import しなくても存在するだけで影響を受ける場合があるんですね。
モジュールに存在するファイル名を付けては駄目というお話でした。

それだけではアレなので。
下記のサンプルコードがいつのまにか GTK4 になっていました。

Overview ? PyGObject

いつまで GTK3 のまんまなんだよと思っていたけどやっとか。
set_application_name は意味無いだろ、は置いといて。
せっかくこんなネタなので signal の所を見てみよう。

Signals ? PyGObject

PyGObject で signal の定義はデコレータを付けるだけなのか。
シグナル名は関数名そのままってことでいいのかな、実験してみよう。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, GObject

class Win(Gtk.ApplicationWindow):
    def __init__(self, a):
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        Gtk.ApplicationWindow.__init__(self, application=a)
        button = Gtk.Button(label='Click!')
        button.connect('clicked', self.on_button_clicked)
        self.set_child(button)

    def on_button_clicked(self, button):
        self.emit('my_signal')
        print('Button Signal')

    @GObject.Signal
    def my_signal(self):
        print('My Signal')

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

signal

うん、普通に emit をキャッチできますね。
非同期になるわけではないしこういう使い方では関数と変わらないですが。
自作 widget に signal を実装したい場合こんなに簡単にできるみたいです。

Signals ? Building an Application ? GTK4 + GJS Book

ちなみに Gjs はこう、メンドクセー。
そりゃみんな PyGObject を選ぶよって。

hibari

今日は何故か芝生の中でヒバリのメスがハアハアしていました。
暑いなら日陰にいけばいいのに、日陰はカラスが占領していたけど。

GTK4: Label

GTK4 の基本と外枠のまとめはゴールデンウイークに作った。
細かい Widget はどうするか、ほとんど GTK3 と変わっていないのですし。
でもその時だけ GTK3 のページを見てくれじゃアレなんで少し書き足そうかと。

Gtk(PyGObject) Tips | ラベル – Paepoi

とりあえずコイツの GTK4 化だな。

Gtk.Label

angle プロパティが無くなった以外は特に問題無さそうにみえる。
回転するにはどうするのかな、pango を使えになったのだろうか?

Pango ? 1.0: Text Attributes and Markup

angle に相当するものはないようで。
text_transform に rotate(deg90) はやっぱりダメか。
でも gravity という重力の指定でどの方向が下なのか決めることはできる。
これで 90 度単位な回転ができるっぽい、他も含めてやってみる。

#!/usr/bin/env python3

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

class Win(Gtk.ApplicationWindow):
    def __init__(self, a):
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        Gtk.ApplicationWindow.__init__(self, application=a)
        # mouse move
        labels = [
            Gtk.Label(label='中心'),
            Gtk.Label(label='左寄せ', xalign=0),
            Gtk.Label(label='右寄せ', xalign=1),
            #Gtk.Label(label='180度回転', angle=180),
            Gtk.Label(label='<s>打ち消し線</s>を入れたり', use_markup=True),
            Gtk.Label(label='<span bgcolor="red">赤くする</span>とか', use_markup=True),
            Gtk.Label(label='<span fgcolor="blue">青くする</span>とか', use_markup=True),
            Gtk.Label(label='<span font_weight="heavy">太くする</span>とか', use_markup=True),
            Gtk.Label(label='<span font_style="italic">italic にしたり</span>とか', use_markup=True),
            Gtk.Label(label='エスケープ文字も\n\t使えます', xalign=0),
            Gtk.Label(label='エスケープ文字を\n\t無効にする', xalign=0, single_line_mode=True),
            Gtk.Label(label='薄くする', opacity=0.3),
            #
            Gtk.Label(label='<span text_transform="uppercase">uppercase</span>とか', use_markup=True),
            #Gtk.Label(label='<span text_transform="ratate 45deg">回転</span>Error', use_markup=True),
            Gtk.Label(label='<span gravity="north" baseline_shift="13pt">反転</span>とか', use_markup=True),
            Gtk.Label(label='<span gravity="west" baseline_shift="8pt">西が下</span>とか', use_markup=True)
        ]
        #
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        for label in labels:
            vbox.append(label)
            vbox.append(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL))
        # 
        self.set_child(vbox)
        self.set_default_size(200, 200)

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

gtklabel

なんとかなった。
baseline_shift に注意、元のベースラインを軸に回転するので上に動かす。
でもコレ環境によるよなぁって、まあ回転は可能ってことで。
それと single_line_mode だと改行が矢印になるんだね、笑った。

kochidori

今日はコチドリを見つけました、やっとツバメ以外の夏鳥が撮れた。
駐車場でウロウロしていたけどそんな所にミミズいないだろ。

isohiyodori

デカいカワセミ、ではなくイソヒヨドリです。
五条川で見たのは二回目、前回は超望遠をもっておらず点でしたけど。

GtkStyleContext to GtkWidget css_classes

Gtk.StyleContext

GTK 4.10 は GtkStyleContext も廃止予定になっていた。
get_style_context から背景の透過にしかつかっていなかったとはいえ。
Tips ページを書き換えなきゃ、背景透過は今後どうすりゃいいんだ?

「API は GtkWidget に移した」とあるけど何をどう移したのかは説明無し。
Linux はそんなものだ、自力で色々プロパティを弄くってみる。

#!/usr/bin/env python3

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

# Picture File
PNGFILE = 'test.png'
 
class Win(Gtk.ApplicationWindow):
    '''
        GTK4: No Decorated Window
    '''
    def __init__(self, a):
        Gtk.ApplicationWindow.__init__(self, application=a, decorated=False)
        ''' Deprecated
        APP_CSS = 'window { background-color: rgba(255, 255, 255, 0); }' #.encode('utf-8')
        provider = Gtk.CssProvider()
        provider.load_from_data(APP_CSS, -1)
        context = self.get_style_context()
        context.add_provider_for_display(
            self.get_display(),
            provider,
            Gtk.STYLE_PROVIDER_PRIORITY_USER)
        '''
        #
        # Transparent
        #
        print(self.get_css_classes()) #=> [background]
        self.set_css_classes(['no_name'])
        #
        # Mouse Signal
        click = Gtk.GestureClick()
        #click.connect('pressed', self.on_gesture_click_pressed)
        click.connect('stopped', self.on_gesture_click_stopped)
        click.connect('released', self.on_gesture_cllick_released)
        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())
 
    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_stopped(self, click):
        '''
            Mouse Move
        '''
        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()
        time = device.get_timestamp()
        toplevel.begin_move(device, button, win_x, win_y, time)

    def on_gesture_cllick_released(self, widget, n_press, x, y):
        '''
            Double Click
        '''
        if n_press == 2:
            self.close()

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

css

結果 GtkWidget の css_classes を変更したいが定義がワカラン。
存在しない CSS にしたらどうなる?って適当にやったらイケてしまった。
多分正しい手段ではないので今後は解らないですけど。

他に CSS 文字列は UTF-8 にする必要がいつのまにかなくなっていたり。
いやそれは廃止されるのだからどうでもいいんですけど。

ついでに、GtkGestureClick で今頃発見。
pressed シグナルを処理すると released シグナルが発行されなくなるのですけど。
stopped シグナルなら releace も発行される、よし今度からこの方法にしよう。

GTK4 Application in Gjs

そういえば GTK4 になってから Gjs をまったくやっていない。
GTK3 では PyGObject のほうが圧倒的に簡単だし情報がほぼ公式だけだったし。
もしかして GTK4 になってから少しは簡単になっているのだろうか?

GTK4 + GJS Book ? Learn to build a GTK4 application with GJS

#!/usr/bin/gjs -m

/*
 * GTK4 Application in Gjs
 * Requires -m option in command line
 */

// version set
import 'gi://Gtk?version=4.0';
import 'gi://Adw?version=1';

// imports to Dynamic Import
import Gtk from 'gi://Gtk';
import GObject from 'gi://GObject';
import Adw from 'gi://Adw';

export const MyWindow = GObject.registerClass({
    GTypeName: 'MyWindow'
}, class extends Gtk.ApplicationWindow {
    _init(app) {
        // Set Adwaita Style
        let manager = Adw.StyleManager.get_default();
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT);
        // No change from GTK3
        super._init({application: app});
        let button = new Gtk.Button({label: 'Click'});
        button.connect('clicked', ()=> {
            this.title = 'Hello';
        });
        this.set_child(button);
    }
});

export const MyApplication = GObject.registerClass({
    GTypeName: 'MyApplication'
}, class extends Gtk.Application {
    // use _init instead of constructor
    _init() {
        super._init({
            application_id: 'org.myapp.test'
        });
    }
    // override is vfunc_***
    vfunc_activate() {
        let win = new MyWindow(this);
        win.present();
    }
});

new MyApplication().run(null);

gir を使うには -m オプションが必要になった。
gir バージョンの指定はこうするようです。
imports という独自関数は MDN 準拠の Dynamic import っぽく変更。
class の作成も同様に export を指定。
ビルドする場合は main 関数というコンストラクタを用意する。
Adwaita テーマの適用は PyGObject と同じ。
後は GTK3 の時と変わっていないみたい。

全然簡単になっていなかった、むしろ面倒さが増している。
というか GObject.registerClass を隠蔽する仕組みが欲しい。

しかし Python に慣れていると new や let をよく書き忘れる。
Python が人気なのはまあ当然というかなんというか