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

NSImage: WebP and HEIF

前回 Fedora での WebP 変換について書いたんですけど。
WebP を使いたい層は Web デザイナ、つまり macOS ユーザーが大半かと。
アプリも色々出ているけどオートメーションでサクッと変換できればなって。
ところで NSImage って WebP を扱えるのかな?

#!/usr/bin/env python3

from AppKit import *

RECT = ((0, 0), (400, 300))

class MyView(NSView):
    def initWithFrame_(self, rect):
        objc.super(MyView, self).initWithFrame_(RECT)
        return self

    def drawRect_(self, rect):
        # WebP or HEIF
        image = NSImage.alloc().initWithContentsOfFile_('gara.webp')
        #image = NSImage.alloc().initWithContentsOfFile_('panaleica.heic')
        image.drawInRect_(RECT)

class MyWindow(NSWindow):
    def init(self):
        objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
            RECT,
            NSTitledWindowMask | NSClosableWindowMask |
            NSResizableWindowMask | NSMiniaturizableWindowMask,
            NSBackingStoreBuffered, False)
        self.setTitle_('WebP Test')
        self.setDelegate_(self)
        # MyView
        self.canvas = MyView.alloc().initWithFrame_(RECT)
        self.contentView().addSubview_(self.canvas)
        #
        return self

class AppDelegate(NSObject):
    def applicationDidFinishLaunching_(self, notification):
        self.mywindow = MyWindow.new().autorelease()
        self.mywindow.makeKeyAndOrderFront_(self.mywindow)

class AppMenu(NSMenu):
    def init(self):
        objc.super(AppMenu, self).init()
        item_app  = NSMenuItem.new()
        self.addItem_(item_app)
        menu_app = NSMenu.new()
        item_app.setSubmenu_(menu_app)
        # quit menu
        item_quit = NSMenuItem.new()
        item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

pool = NSAutoreleasePool.new()

NSApplication.sharedApplication()
NSApp.setMainMenu_(AppMenu.new())
NSApp.setDelegate_(AppDelegate.new())
NSApp.activateIgnoringOtherApps_(True)
NSApp.run()

del pool

PyObjC でごめん。

mac webp

普通に扱えるんですね、HEIF(heic) 画像でも問題なく。
後はコレを変換だ、と思ったんですけど。

NSBitmapImageFileType | Apple Developer Documentation

NSBitmapImageFileTypeWEBP なんて定義は無いんですね。
HEIF も無い、検索で簡単に見つかる手段は使えないか。
もう少し調べます。

GTK4: DirectoryList

Gtk.DirectoryList

こんなものを今更見つけた。
g_file_enumerate_children_async をラップしているらしい。
つまりコレを使えばファイルマネージャみたいな UI が簡単に作れるようだ。

しかしコレだけじゃどうやって使うのかちっともわからんぞ。
誰かサンプルコードでも書いていないか、探す。

GitHub – ToshioCP/Gtk4-tutorial: A gtk4 tutorial for beginners

おぉありがとう、なんと日本人ではないですか。
Ruby 屋さんのようですがごめん、筆者は PyGObject でやる。
ListItem.get_item で Gio.FileInfo が得られるようで。

#!/usr/bin/env python3

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

class Win(Gtk.ApplicationWindow):
    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)
        #
        f = Gio.File.new_for_path('.')
        ls = Gtk.DirectoryList(file=f, attributes='standard::name,standard::content-type,standard::size')
        #
        sel_model = Gtk.SingleSelection(model=ls)
        #
        factory1 = Gtk.SignalListItemFactory()
        factory1.connect('setup', self.on_listitem_setup)
        factory1.connect('bind', self.on_listitem_bind1)
        #
        factory2 = Gtk.SignalListItemFactory()
        factory2.connect('setup', self.on_listitem_setup)
        factory2.connect('bind', self.on_listitem_bind2)
        #
        factory3 = Gtk.SignalListItemFactory()
        factory3.connect('setup', self.on_listitem_setup)
        factory3.connect('bind', self.on_listitem_bind3)
        #
        columnview = Gtk.ColumnView(model=sel_model)
        columnview.connect('activate', self.on_columnview_activate)
        #
        column1 = Gtk.ColumnViewColumn(title='Filename', factory=factory1)
        columnview.append_column(column1)
        column2 = Gtk.ColumnViewColumn(title='ContentType', factory=factory2)
        columnview.append_column(column2)
        column3 = Gtk.ColumnViewColumn(title='Size', factory=factory3)
        columnview.append_column(column3)
        #
        self.set_child(columnview)

    def on_listitem_setup(self, factory, item):
        item.set_child(Gtk.Label(xalign=0))

    def on_listitem_bind1(self, factory, item):
        info = item.get_item()
        label = item.get_child()
        label.props.label = info.get_name()

    def on_listitem_bind2(self, factory, item):
        info = item.get_item()
        label = item.get_child()
        label.props.label = info.get_content_type()

    def on_listitem_bind3(self, factory, item):
        info = item.get_item()
        label = item.get_child()
        label.props.label = f'{info.get_size()} byte'

    def on_columnview_activate(self, listview, pos):
        print(1)

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

directorylist

イケるじゃん。
と思ったけどコレってソートはどうするんだ?
あと隠しファイル等を弾くには bind ハンドラ全部に同じ処理がいるよね。
たしかに簡単だけど実用にはチト使い辛い感じ。

GTK4: Adw.ButtonContent

Adw.ButtonContent という Widget がある。
アイコンと文字列を両方ボタンに表示させるものらしい。
何故こんなものがあるんだ?
Gtk.Button には icon-name プロパティがあるんですけど。

#!/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):
    '''
        Adw: 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
        b1 = Gtk.Button(label='普通のボタン(_A)', use_underline=True)
        #
        b2 = Gtk.Button(icon_name='edit-undo-symbolic',)
        #
        b3 = Gtk.Button(icon_name='edit-redo-symbolic', label='Label 指定すると画像が表示されない')
        #
        bc = Adw.ButtonContent(icon_name='starred-symbolic', label='_SVG 画像付きボタン', use_underline=True)
        b4 = Gtk.Button(child=bc)
        # pack
        box = Gtk.ListBox() #show_separators=True)
        box.append(b1)
        box.append(b2)
        box.append(b3)
        box.append(b4)
        # Clamp
        clamp = Adw.Clamp(maximum_size=300, tightening_threshold=200)
        clamp.set_child(box)
        self.set_child(clamp)
        self.set_default_size(400, 200)

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

gtk4button

試してみたらすぐ解った。
label を指定した場合に image を表示させる手段が無いんだね。
GTK3 の時は普通にできていたような、ちと試してみよう。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class Win(Gtk.ApplicationWindow):
    '''
        GTK3
    '''
    def __init__(self, a):
        # Set Adwaita Style
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # Button
        b1 = Gtk.Button(label='普通のボタン(_A)', use_underline=True)
        #
        im = Gtk.Image(icon_name='edit-undo-symbolic')
        b2 = Gtk.Button(label='アンドゥ', image=im, always_show_image=True)
        # pack
        box = Gtk.ListBox()
        box.insert(b1, 0)
        box.insert(b2, 1)
        # Add
        self.add(box)
        self.resize(400, 200)
        self.show_all()

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

gtk3button

思い込みだった、普通にはできない。
always-show-image プロパティを TRUE にするという手順が必要。
いつぞやの更新でストックが廃止になったので間に合わせで機能追加って感じかな。

ちなみに GTK2 では文字列も画像なストック画像から選べだった。
OK や Open 等の限られたものしか選べなかったけど当時はそれが普通。
PyGtk は Python2 なのでサンプルコードは書かないけど。

GTK4 は極力シンプル化、表示は全部 Widget でまかなうように。
結果両方を表示するには Box に両方をセットして上に載せる手順が必要に。
それを単純化したのが ButtonContent ということみたいです。

よく見るとアイコンの位置が端になる演出もあるんだなって。
関係ないけど ListBox への Widget 挿入関数に append が追加されていた。
ListBox って本当は用途が違うけど並べたときに隙間ができるので今回使用。

ちなみに使えるアイコンは gtk4-icon-browser から。

browser

Symbolic のほうにあるアイコンはどれでも利用できます。
ということで、最後に。

jyoubitaki

今日もジョウビタキ、以降ドン曇りでロクなの撮れず。
しかしオスばかり見つかるんですけど、悪いが筆者はゲイじゃない。

GTK4: Adw.Clamp

前回まで Widget 配置を中心にするのに margin_*** を使っていた。
Nautilus の設定ウインドウを参考にしたのですけど、何か違うなって。

リサイズすると普通に伸び縮み、ある一定のサイズ以上になると固定される。
どうやら Adw.Clamp でこのような動作にできるようだ。

#!/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):
    '''
        Adw: 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)
        # Round Button
        pills = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        pills.append(Gtk.Button(label='pill', css_classes=['pill']))
        pills.append(Gtk.Button(label='circular', css_classes=['circular']))
        # Button and Entry
        buttons = [
            Gtk.Button(label='nomal'),
            Gtk.Button(label='flat', css_classes=['flat']),
            Gtk.Button(label='suggested', css_classes=['suggested-action']),
            Gtk.Button(label='destructive', css_classes=['destructive-action']),
            pills,
            Gtk.Entry(placeholder_text='success', css_classes=['success']),
            Gtk.Entry(placeholder_text='warning', css_classes=['warning']),
            Gtk.Entry(placeholder_text='error', css_classes=['error'])
        ]
        # pack
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) #, margin_start=48, margin_end=48)
        for b in buttons:
            box.append(b)
        # Clamp
        clamp = Adw.Clamp(maximum_size=300, tightening_threshold=200)
        clamp.set_child(box)
        self.set_child(clamp)
        self.set_default_size(400, 400)

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

前回のを書き換え。
margin 指定をヤメて Adw.Clamp を利用にしてみたのでリサイズしてみよう。
なるほど、中心配置はコレを使ったほうがいいな。

tightening-threshold はしきい値ということだけど。
指定値以下にはならないと思って縮めてみたら違った。

clamp

正直指定しても意味ない、コンテンツ最小サイズまで縮められる。
多分内部的な値なのだろう、てか最小サイズプロパティが欲しいかも。

GTK4: css-classes Property

GTK4 の Widget には css-classes というプロパティがある。
定義された文字列を入れれば css が適用されるみたいなんだが。

Adw ? 1: Style Classes

なんで Adw のほうに書いてあるんだよ。
とにかくボタンとエントリーを着色してみる。

#!/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):
    '''
        Adw: 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)
        # Round Button
        pills = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        pills.append(Gtk.Button(label='pill', css_classes=['pill']))
        pills.append(Gtk.Button(label='circular', css_classes=['circular']))
        # Button and Entry
        buttons = [
            Gtk.Button(label='nomal'),
            Gtk.Button(label='flat', css_classes=['flat']),
            Gtk.Button(label='suggested', css_classes=['suggested-action']),
            Gtk.Button(label='destructive', css_classes=['destructive-action']),
            pills,
            Gtk.Entry(placeholder_text='success', css_classes=['success']),
            Gtk.Entry(placeholder_text='warning', css_classes=['warning']),
            Gtk.Entry(placeholder_text='error', css_classes=['error'])
        ]
        # pack
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, margin_start=48, margin_end=48)
        for b in buttons:
            box.append(b)
        self.set_child(box)
        self.set_default_size(400, 400)

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

css

css は自分で定義もできますけどまだ調べていない。
てかこれだけあれば十分だと思うけど。