Programming」カテゴリーアーカイブ

GtkStyleContext to GtkWidget css_classes

Gtk.StyleContext

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/gjs -m
 
/*
 * GTK4 Application in Gjs
 * Requires -m option in command line
 */
 
// version set
 
// 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 が人気なのはまあ当然というかなんというか

GtkFileLauncher

Gtk.FileLauncher

Gtk 4.10 にこんな class が追加されていた。
いやえっと、ファイルからアプリを起動するなら gio コマンドでよくね?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python3
 
import gi, subprocess
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gio
 
PICFILE = './test.jpg'
 
class Win(Gtk.ApplicationWindow):
    '''
        Default App Launcher: Sample Code
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # button
        button = Gtk.Button(label='Launch test.jpg')
        button.connect('clicked', self.on_button_clicked)
        self.set_child(button)
 
    def on_button_clicked(self, button):
        subprocess.run(['gio', 'open', PICFILE])
 
app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

みたいに。

GtkUriLauncher もある、gio コマンドは URI でもイケるんだが。
とはいえ、わざわざ追加したということは何かあるんだろうなって。
とっとと試してみる、使い方は GtkFileDialog と同じみたいね。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/env python3
 
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gio
 
PICFILE = './test.jpg'
 
class Win(Gtk.ApplicationWindow):
    '''
        GtkFileLauncher: Sample Code
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # button
        button = Gtk.Button(label='Launch test.jpg')
        button.connect('clicked', self.on_button_clicked)
        self.set_child(button)
 
    def on_button_clicked(self, button):
        f = Gio.File.new_for_path(PICFILE)
        # GtkFileLauncher
        launcher = Gtk.FileLauncher(file=f)
        launcher.launch(self, None, self.on_launch_finish)
 
    def on_launch_finish(self, launcher, res):
        try:
            result = launcher.launch_finish(res)
            if result:
                print('Success')
            else:
                print('Failed')
        except Exception as e:
            print(f'Launch Error: {e}')
 
app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

launch

そういうことか!

デフォルトアプリではなく何で開くかを選択できる、ということだった。
てか GtkAppChooserDialog の後継でした、名前がまぎらわしいわ!
デフォルトアプリ起動なら上のコードほうが簡単です、と一言。
ということで。

hibari

今日が囀り続けるキビタキを三十分追いかけ、とうとう一枚も撮れず。
ヤケクソで午後はキジを探したら、ものすごく遠くにしか出てこないし。
あきらめて帰ろうと思ったところにヒバリが現れてくれました。
ヒバリのトサカって正面から見るとこんな感じなんだね。

Gtk.FileDialog

Gtk.FileDialog

GtkDialog 関連が廃止予定なので GTK+ 4.8 の間に置き換え。
と早めにしようとしたら GtkFileDialog が 4.10 にならないと用意されない。
ということでのんびり 4.10 を待っていたのは筆者だけではないと思う。

FileChooserDialog は GtkDialog ベース。
GtkDialog は GtkWindow ベース、更に GtkWidget ベース更に…
継承の仕組みとはいえ限られた用途な部品とは思えない作りだった。
GtkFileDialog は GObject からの単一継承と解りやすくなった。

いやそんな細かいことはどうでもいいですよね。
とっととサンプルコード。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/env python3
 
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gio
 
WRITE_TEXT = 'Python 文字列は UTF-8 に変換する'.encode('utf8')
 
class Win(Gtk.ApplicationWindow):
    '''
        GtkFileDialog: Sample Code
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # Button
        button_open = Gtk.Button(label='_Open', use_underline=True)
        button_open.connect('clicked', self.on_button_open_clicked)
        button_save = Gtk.Button(label='_Save', use_underline=True)
        button_save.connect('clicked', self.on_button_save_clicked)
        button_old = Gtk.Button(label='_GtkFileChooserDialog', use_underline=True)
        button_old.connect('clicked', self.on_button_old_clicked)
        # box
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.append(button_open)
        vbox.append(button_save)
        vbox.append(button_old)
        self.set_child(vbox)
 
    def on_button_open_clicked(self, button):
        dlg = Gtk.FileDialog()
        dlg.open(self, None, self.on_open_dlg_callback)
 
    def on_open_dlg_callback(self, dlg, res):
        try:
            f = dlg.open_finish(res)
            print(f.get_path())
        except Exception as e:
            print('Cancel')
 
    def on_button_save_clicked(self, button):
        dlg = Gtk.FileDialog()
        dlg.save(self, None, self.on_save_dlg_callback)
 
    def on_save_dlg_callback(self, dlg, res):
        try:
            f = dlg.save_finish(res)
            stream = f.replace(None, False, Gio.FileCreateFlags.NONE)
            stream.write(WRITE_TEXT)
            stream.close()
        except Exception as e:
            print('Cancel')
 
    def on_button_old_clicked(self, button):
        dlg = Gtk.FileChooserDialog(title='Open', action=Gtk.FileChooserAction.OPEN, modal=True)
        dlg.add_buttons('_Cancel', Gtk.ResponseType.CANCEL, '_Open', Gtk.ResponseType.ACCEPT)
        dlg.set_transient_for(self)
        dlg.connect('response', self.on_open_dlg_response)
        # CSS CLASS
        dlg.get_widget_for_response(Gtk.ResponseType.ACCEPT).add_css_class('suggested-action')
        # Gtk.Widget.show() is deprecated
        dlg.set_visible(True)
 
    def on_open_dlg_response(self, dlg, response_id):
        if response_id == Gtk.ResponseType.ACCEPT:
            f = dlg.get_file()
            print(f.get_path())
        dlg.destroy()
 
app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

filedialog

GtkFileChooserDialog は GTK+ 4.10 でもエラーにはなりません。
AppKit|UIKit 同様に廃止予定にして頃合で削除するみたい。

open と save の振り分けはプロパティではなく関数になりました。
複数選択の指定も関数になったけど説明はいらないよね。
シグナルではなくコールバック関数にて処理だけどあまり変わらない。
GtkDialog とは違い自前で破棄する必要は無くなったようです。

open file

ACCEPT ボタンは CSS 指定せずとも勝手に青くなります。
GtkFileFilter は default-filter プロパティで指定可能。
後は公式を見れば使い方は解ると思う。

ついでに気がつく、Gtk.Widget.show|hide も廃止なんだね。
visible プロパティと被っていたし整理したのかな。

ところで WordPress のアップロードがこの FileDialog になった。
複数選択ができないんですけど、一個づつアップロードするのメンドイ。

ということで我が Comipoli も更新しようと思ったのですが。
svg アイコンが摘要されない、128×128 強制になったのだろうか?
現在 Boxy SVG というアプリと格闘中、ということで。

kiji

昨日今日と夏鳥を求めて歩き回って成果はゼロ。
キジなら何故か簡単に見つかってこんなカッコイイ写真が撮れるのに。
筆者は今年も夏鳥とは相性が悪いのか。。。。。

macOS: Get UTI (Ventura)

macOS 13 Ventura で以下の関数が使えなくなった件。

# Deprecated
UTTypeCreatePreferredIdentifierForTag

UTI を調べる方法にて検索するとメッチャ見つかります。
でももう使えません、別の方法を探す。

UTType | Apple Developer Documentation

UTType クラスに typeWithFilenameExtension メソッドがある。
+ だから static method ですね、これ使えるかも。
以下 PyObjC です、他言語の人は変換してね。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python3
 
'''
    PyObjC @ Get UTI
'''
 
import sys, UniformTypeIdentifiers
 
for filename in sys.argv[1:]:
    n = filename.rfind('.') + 1
    if n > 0:
        ext = filename[n:]
        uti = UniformTypeIdentifiers.UTType.typeWithFilenameExtension_(ext)
        print(f'{ext}: {uti}')
 
 
''' Deprecated
 
import sys, CoreServices
 
for ext in sys.argv[1:]:
    uti = CoreServices.UTTypeCreatePreferredIdentifierForTag(
        CoreServices.kUTTagClassFilenameExtension, ext, None)
    arr = CoreServices.UTTypeCopyDeclaration(uti)['UTTypeConformsTo']
    con = ','.join(arr)
    print(f'{ext}: {uti} [{con}]')
'''
 
# ex: ft=py

get uti

よしデジカメ RAW ファイルでも問題なく UTI が得られるぞと。
本サイトのほうも書き換えしておきます。
しかし以前貼ったスクリプトで WebP 化したけど文字がチト見づらいな。
これも要改良かな、ということで。

aoji

今日はアオジが撮れました。