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

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 のエスケープ機能だけでもいいのに。

PyObjC Tips 更新

そんなこんなで PyObjC Tips 更新しました。
PyObjC Tips – Paepoi

macOS には PHP を入れていないので Fedora でページ作成。
面倒だけど Fedora 内とミラーリングするほうが面倒だし。

ところで Kate でも GVFS 接続で macOS 内のファイルが編集できるのね。
コレができるならもう Gedit はいらないよな、削除するか。

と思っていたら見つけた、Gedit の Flathub 版は v47 になっている。
https://gedit-technology.github.io/blog/
rpm では v46 のままだが、もしかしたらと入れてみる。

アイコンが変わった。
ダークテーマ対応、ただし OS 連動ではない。
ローカライズされず英語のまま。
~/.local/share/gedit/plugins に自作プラグインを置いても無視。
Python の fstring や mutch 文の色分けはされないまま。
PHP のヒアドキュメントインデント判別にも未対応なまま。
他の言語は解らないけど多分中身は v46 のまま。

だめだこりゃ、管理人はプログラミングをしていないのか?
今後バージョンが進んでも見た目以外の進化はしなさそう。

PyObjC がトンデモなく遅い

今日は久しぶりに PyObjC で小物を作ろうとした。
いやまあ macOS プログラミングは完全に放置気味でしたね。
ということでまずアップグレードを行なってと。

# pip Upgrade
pip3 install --upgrade pip

# PyObjC Install or Upgrade
pip3 install -U pyobjc

で本題、PyObjC コードの初期化がありえないほど遅い。
NSWindow を作ったら表示されるまで十秒くらい待たされる。
Python 標準モジュールのみのコードなら普通に一瞬だ。
しばらく色々試したらこういうことだった。

#!/usr/bin/env python3

'''
    app.old.py
'''

import time
start = time.time()

from AppKit import *
import objc

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)
        # command+Q で閉じるメニュー
        item_quit = NSMenuItem.new()
        item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

# NSApp を作る
NSApplication.sharedApplication()
# command+Q で終了するメニューを入れる
NSApp.setMainMenu_(AppMenu.new())
# コレをしないと最前面に出てこない
NSApp.activateIgnoringOtherApps_(True)
# メインループを回さない
#NSApp.run()

# 経過時間 ms
print(f'{time.time() - start} ms')

from を使う今までやっていたコード。

#!/usr/bin/env python3

'''
    aoo.new.py
'''

import time
start = time.time()

import AppKit, objc

class AppMenu(AppKit.NSMenu):
    def init(self):
        objc.super(AppMenu, self).init()
        item_app  = AppKit.NSMenuItem.new()
        self.addItem_(item_app)
        menu_app = AppKit.NSMenu.new()
        item_app.setSubmenu_(menu_app)
        # command+Q で閉じるメニュー
        item_quit = AppKit.NSMenuItem.new()
        item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

# NSApp を作る
AppKit.NSApplication.sharedApplication()
# command+Q で終了するメニューを入れる
AppKit.NSApp.setMainMenu_(AppMenu.new())
# コレをしないと最前面に出てこない
AppKit.NSApp.activateIgnoringOtherApps_(True)
# メインループを回さないさない
#AppKit.NSApp.run()

# 経過時間 ms
print(f'{time.time() - start} ms')

都度プリフィクスを書く面倒くさいコード。

PyObjC

from で * を使うとトンデモネェ遅さになってしまった。
以前は from でも同じような速度で初期化されていたんですが。
NSApp だけでコレ、NSWindow まで作るとすげぇ悲惨。

Python 3.11 からの高速化の弊害なのかな。
from は全部辿ってキャッシュとかになっていたらまあこうなるよな。
いや PyObjC 側の不具合かもしれないけどプリフィクス化で解決するし。
Cocoa のメソッド名長いんだよな、GTK+ みたくできないのかと。
アスタリスクを使っている人は調べてみたほうがいいかも。

ついでに、objc は import しなくてもよかったのに必須になった。
activateIgnoringOtherApps が動作しない、何故?
ちょっと放置しすぎたな、もう少し調べよう。
秋の渡り鳥が来る前にやらないとまた放置しそうだし。

MPV AspectRate Change (2024)

いつのまにか MPV にてアスペクト比変更ができなくなっていた。
いや Shift+a での変更は可能、自作した拡張 Lua スクリプトが動かない。

How to add 21:9 Aspect ratio option in MPV Android? : r/mpv

色々探して上記を見つける。
video-aspect-override という property があるのね。

--mp.set_property('video-aspect', aspects[aspect_num])
mp.set_property('video-aspect-override', aspects[aspect_num])

これで動いた、思っていたより簡単でよかった。
Fedora の MPV は更新されていないし ffmpeg 側の仕様変更かな?
なので環境によって変わると思う、Fedora 40 な人は書き換え必須です。

せっかくなので Shift 追加で逆順変更機能も付けてみよう。

-- ~/.config/mpv/scripts/mpv_aspect_rate.lua

-- Shift+a でアスペクト比は変更できますけど
-- スマホの縦動画用が足りないので独自に作ってみました

local aspect_num = 0
local aspects = {'4:3', '16:9', '9:16', '3:4', '1:1', '3:2', '2:3'}

function on_change_aspectrate()
    aspect_num = aspect_num + 1
    if aspect_num > #aspects then
        aspect_num = 0
        --mp.set_property('video-aspect', '-1')
        mp.set_property('video-aspect-override', '-1')
        mp.osd_message('Aspect Rate @ Default')
    else
        --mp.set_property('video-aspect', aspects[aspect_num])
        mp.set_property('video-aspect-override', aspects[aspect_num])
        mp.osd_message('Aspect Rate @ '..aspects[aspect_num])
    end
end

function on_change_aspectrate_r()
    aspect_num = aspect_num - 1
    if aspect_num == -1 then
        aspect_num = #aspects
    end
    if aspect_num == 0 then
        mp.set_property('video-aspect-override', '-1')
        mp.osd_message('Aspect Rate @ Default')
    else
        mp.set_property('video-aspect-override', aspects[aspect_num])
        mp.osd_message('Aspect Rate @ '..aspects[aspect_num])
    end
end
mp.add_key_binding('Ctrl+t', 'aspectrate_func', on_change_aspectrate)
mp.add_key_binding('Ctrl+Shift+t', 'aspectrate_r_func', on_change_aspectrate_r)

こんな感じになった。

しかし Kate は今だに使い辛いなぁ。
コードやブログネタを書いて余計な補完機能を見つけて無効化するばかり。
macOS で Sublime Text は Gedit でやっていたことを再現できたんだけど。
Kate は無理っぽい、でも Gedit は死亡したも同然だし代わりも見つからない。

Node.js: ESM

Node.js って令和になってから全然話題にならないよね。
と思っていたら v20 になっていた。

Node.js ? Run JavaScript Everywhere

ちょっとまて、require は import に変わっていたの?
拡張子が mjs みたいなんですけど、これ何だ?

CJS, UMD, ESMとは?その違い。 | milestones

変わったのではなく標準に合わせて拡張したってことみたい。
でもトップページが ESM ってことは今後はこっちを使えなんだろうな。
ちなみに以下のコードを書いた時はたしか v6 だか v8 だったような。

Gjs, JXA, Node.js – Paepoi

標準である import に合わせたのは Gjs だけじゃなかったのね。
JXA は Apple がやる気ゼロだから今後も放置だろうけど。
書き換えしなきゃ、使わないと解っているけど確認のため導入。

node

うん Fedora 29 の dnf も v20 ですね。
とりあえず readline を試してみよう。

Readline | Node.js v21.7.1 Documentation

スイッチを CJS から ESM に切り替えると import のコードに変わる。
モジュール変数は UpperCamelCase 推奨だったけど小文字推奨に変わった?
無名関数ではなく promises を使えなの?とにかく書いてみる。

#!/usr/bin/env node

/** CJS
const readline = require('readline');
const tty = require('tty');

let rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    terminal: false
});
let [prpt, prefix] = tty.isatty(0) ? ['何か入力して return :', '端末入力'] : ['', 'パイプ'];
rl.question(prpt, (line)=> {
    console.log(`${prefix}から ${line} が渡されました`);
    rl.close();
});
*/
// ESM (extention as 'mjs')
import * as readline from 'node:readline/promises';
import * as tty from 'node:tty';
import { stdin as input, stdout as output } from 'node:process';

let rl = readline.createInterface({ input, output, terminal:false });

let [prpt, prefix] = tty.isatty(0) ? ['何か入力して return :', '端末入力'] : ['', 'パイプ'];

let line = await rl.question(prpt);
console.log(`${prefix}から ${line} が渡されました`);
rl.close();

test.mjs

拡張子は mjs にしないと動かなかった。
Gjs は -m オプションだけど合わせてくるかな。
シバンを書いても問題なく動作しますね。

node

ぶっちゃけ ESM でも process は import せず使えるし無名関数でも動く。
とはいえ公式サンプルががこうなので今後はこうさせたいのだろうと思う。
promises を使うと上級者って感じでかっこいい、ような気がする。
もう少し調べて四月までに tips ページは書き換えよう。