PyObjC」タグアーカイブ

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 が動作しない、何故?
ちょっと放置しすぎたな、もう少し調べよう。
秋の渡り鳥が来る前にやらないとまた放置しそうだし。

macOS: Get UTI (Ventura)

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

# Deprecated
UTTypeCreatePreferredIdentifierForTag

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

UTType | Apple Developer Documentation

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

#!/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

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

Image conversion @ PyObjC and JXA

前回書いた検索で簡単に見つかる画像変換ですが。
Objective-c での公式は以下に。

Viewing, Editing, and Saving Images in an Image View

PyObjC でしたら以下がありますね。

[PyObjC CoreGraphics API Example] Shows how to use the functional API of the CoreGraphics framework through PyObjC and Python 2.7. #pyobjc #python #coregraphics #quartz #macos #examples ? GitHub

ただし Python2 コードですし kUTTypeJPEG はとうの昔に非推奨です。
Python3 にて UniformTypeIdentifiers.UTTypeJPEG を使うよう書き換え。

System-declared uniform type identifiers | Apple Developer Documentation

したんだけど何度やってもエラー、原因が解らず困っていた。
本日原因が判明した、こんな理由だったとは。

#!/usr/bin/env python3

import UniformTypeIdentifiers, LaunchServices

print(type(LaunchServices.kUTTypeJPEG))
print(type(UniformTypeIdentifiers.UTTypeJPEG))

''' output
<class 'objc.pyobjc_unicode'>
<objective-c class _UTCoreType at 0x1e3f02bd0>

'''

まさかの。
PyObjC の kUTTypeJPEG は単なる文字列だった。

UTTypeJPEG は普通にポインタ、そりゃエラーになるよと。
ということで PyObjC ではこんなコードになります。
破棄は Python にまかせないと二重破棄でエラー。

#!/usr/bin/env python3

from AppKit import *
from Quartz import *
from UniformTypeIdentifiers import *

src = NSURL.fileURLWithPath_('panaleica.heic')
dest = NSURL.fileURLWithPath_('panaleica.jpg')

isr = CGImageSourceCreateWithURL(src, None)
image = CGImageSourceCreateImageAtIndex(isr, 0, None)

destimg = CGImageDestinationCreateWithURL(dest, str(UTTypeJPEG), 1, None)
if destimg:
    CGImageDestinationAddImage(destimg, image, None)
    res = CGImageDestinationFinalize(destimg)
    print(res)
    #CFRelease(destimg) # Error
else:
    print('error')

HEIF や WebP からでも JPEG に変換できる。
ただし iPhone 写真等の回転情報は継承されず横向きになるので注意。

rotate

いやオートメーションで使うなら AppleScript か JXA にしないと。
こちらでは UniformTypeIdentifiers が使えないので直接文字列で。

#!/usr/bin/osascript

ObjC.import('Cocoa');
ObjC.import('Quartz');
//ObjC.import('UniformTypeIdentifiers');

let src = $.NSURL.fileURLWithPath('gara.webp');
let dest = $.NSURL.fileURLWithPath('gara.heic');

let isr = $.CGImageSourceCreateWithURL(src, null);
let image = $.CGImageSourceCreateImageAtIndex(isr, 0, null);

//let destimg = $.CGImageDestinationCreateWithURL(dest, $('public.jpeg'), 1, null); // jpeg
//let destimg = $.CGImageDestinationCreateWithURL(dest, $('public.png'), 1, null); // png
let destimg = $.CGImageDestinationCreateWithURL(dest, $('public.heic'), 1, null);
if (destimg) {
    $.CGImageDestinationAddImage(destimg, image, null);
    let res = $.CGImageDestinationFinalize(destimg);
    console.log(res);
} else {
    console.log('error');
}

後は同じですね。
PyObjC を先にやらなかったら文字列ということに気が付かなかったかも。
HEIF にもコレで変換可能でした。

#!/usr/bin/osascript

// webp is bad

ObjC.import('Cocoa');
ObjC.import('Quartz');

let src = $.NSURL.fileURLWithPath('panaleica.heic');
let dest = $.NSURL.fileURLWithPath('panaleica.webp');

let isr = $.CGImageSourceCreateWithURL(src, null);
let image = $.CGImageSourceCreateImageAtIndex(isr, 0, null);

let destimg = $.CGImageDestinationCreateWithURL(dest, $('org.webmproject.webp'), 1, null);
if (destimg) {
    $.CGImageDestinationAddImage(destimg, image, null);
    let res = $.CGImageDestinationFinalize(destimg);
    console.log(res); // false
} else {
    console.log('error');
}

ただ WebP から変換はできるけど WebP へはどちらでも変換できなかった。
多分 Objective-c や Swift でも、public.*** しか駄目なのだろうか?
一番やりたいことなのに、もう少し調べます。

ところで、こんなことをやって今頃知ったんですけど。
UTTypeCreatePreferredIdentifierForTag が廃止になっています。

UTTypeCreatePreferredIdentifierForTag | Apple Developer Documentation

筆者が書いた UTI を調べるコマンドが使えなくなっていて焦った。
というか UTI を調べるコードを書いているブログは全滅だ、わーい!
上記で使おうとして動かなかった、新しい方法を探さなくちゃ。

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 も無い、検索で簡単に見つかる手段は使えないか。
もう少し調べます。