月別アーカイブ: 2019年3月

Automator Clipboard

クイックアクションのネタもう一つ。
やってみたら上手くいったので。

macOS でフルパスが必要な時、Terminal.app にドロップしてからコピー。
が有名な方法だけど正直面倒臭い、クリップボードに一発でコピーしたい。
minipoli を作った作者だもん、もう誰も覚えていないだろうけど。

クリップボードを使ってみる ? Swiftをはじめよう!

macOS では NSPasteboard を使うようだ。
クイックアクションで簡単に作れそう。
NSRect を使わないなら JXA でイケるな、よし。

Automator 起動、クイックアクションを新規作成。
[指定された Finder 項目を取得] を配置
[JavaScript を実行] を配置

ObjC.import("Cocoa");
function run(argv) {
    let s = argv.join('\n');
    let clipboard = $.NSPasteboard.generalPasteboard;
    clipboard.clearContents;
    clipboard.setStringForType($(s), $.NSPasteboardTypeString);
}

ファイル名コピー.workflow

NSPasteboardItem は必要なかった。
後はフルパスを得たいファイルを選択してコンテキストメニューから

後はそのままテキストエディタ等に貼り付けできます。
join(‘ ‘) にすれば端末用の空白区切り、等に応用できます。

ちなみに GNOME(GTK+) 環境では同様な処理は不可能。
クリップボードは特殊なバッファではなく単なるポインタだから。
コピー元アプリを終了させるとバッファを保持できなくてペースト不可になる。
Gedit と gnome-terminal で試してみるとすぐ解ると思う。
Linux ではフルパスを使うことは皆無に近いから別にいいんだけど。

macOS Zip compress without ‘__MACOSX’

前回の続き。
よく考えたら zip コマンドなら __MACOSX は作られないんだよね。
だったらクイックアクションを作ってしまえばイイじゃん。

せっかくなのでプロンプトを出して圧縮名を決められるようにする。
デフォルトはディレクトリ名で、Fedora の file-roller と同じように。

Automator 起動、クイックアクションを新規作成。
[指定された Finder 項目を取得] を配置
[シェルスクリプトを実行] を配置

dpath=`dirname $1`
cd $dpath
dname=`basename $dpath`

res=`osascript -e "set result to display dialog \"Archive Name ?\" default answer \"$dname\"
text returned of result"`

for f in "$@"; do
	s=`basename $f`
	param+=" $s"
done

zip -r $res.zip $param

保存、日本語でもいいよ。

注意点はパラメーターはフルパスだということ。
$@ で zip に渡すと展開時にパスを再現してしまいます。
dirname, basename コマンドをフル活用しよう。

Finder で何か選択して実行、よし __MACOSX の無い zip のできあがり。
二本指タップから ZIP 圧縮するんだから同じだよね。
ただ、Automator のエディタはなんとかならんのか、編集し辛すぎ。

macOS tar

macOS で作った tar.gz を Fedora の flie-roller で開く。

何ですかこのドットディレクトリは!
中身は ._*** というドットファイルがファイル数だけ作られる。
展開すると普通にドットファイルに、普段は見えないから本当に困る。

前回の macOS 版 comipoli の配布の時まで気が付かなかった。
よく解らないから file-roller で削除して差し替えしたけど原因を探らないと。
使えるオプションとかは同じだけど、バージョンの違いかな?

バージョンどころか、そもそもまったくの別物だった。
というか、よく考えたら何か見覚えが。

「__MACOSX」の意味は? いま敢えて「zip」を見直す – 新・OS X ハッキング!(30) | マイナビニュース

__MACOSX の件はあまりにも有名だから zip ではなく tar.gz で配布していたのに。
tar よ、オマエもだったのか。。。。。

Fedora から samba でログインして file-roller を使うか、アプリを探すか迷う。

Python3 plistlib

plistlib — Mac OS X .plist ファイルの生成と解析 ? Python 3.7.2 ドキュメント

こんなモジュールが Python3 のデフォルトであったのか!
Info.plist の編集はどうやろうか迷ったけどコイツでいこう。

#!/usr/bin/env python3

import plistlib

PATH = 'Comipoli.app/Contents/Info.plist'

doctype = dict(
    CFBundleDocumentTypes = [
        dict(
            CFBundleTypeExtensions = ['cbz'],
            CFBundleTypeRole = 'Viewer',
            LSTypeIsPackage = False,
            NSPersistentStoreTypeKey = 'Binary'
        ),
        dict(
            CFBundleTypeExtensions = ['cbr'],
            CFBundleTypeRole = 'Viewer',
            LSTypeIsPackage = False,
            NSPersistentStoreTypeKey = 'Binary'
        ),
        dict(
            CFBundleTypeExtensions = ['cb7'],
            CFBundleTypeRole = 'Viewer',
            LSTypeIsPackage = False,
            NSPersistentStoreTypeKey = 'Binary'
        ),
        dict(
            CFBundleTypeExtensions = ['pdf'],
            CFBundleTypeRole = 'Viewer',
            LSTypeIsPackage = False,
            NSPersistentStoreTypeKey = 'Binary'
        )
    ]
)
plist = None
with open(PATH, 'rb') as fp:
    plist = plistlib.load(fp)

plist.update(doctype)

with open(PATH, 'wb') as fp:
    plistlib.dump(plist, fp)

make_plist.py

他のアプリの Info.plist を参考にこれだけ追加してみた。
アイコンを変更することもできるけどまあいいや。

ということで、ビルドスクリプトの最後でコレを実行。
/Applications に移動して pdf で二本指タップしてみる。

Comipoli.app が見事に登録されています。
選択すれば開くことができるしデフォルトアプリにすることもできる。

ところで、0.0.2 は app 化すると引数起動ができなかった。

(旧) Cocoaの日々: アプリ起動時に渡される引数の処理

App にすると application:openFiles: でしか引数を受け付けしないのね。
Window を作るのを applicationWillFinishLaunching: に変更。
application:openFiles: のほうが良さげなのでこっちで処理。
よし日本語でも問題なく引数付き起動できるようになった。

しかし PyObjC だけでここまで作れるとは自分でも思わなかった。
macOS でも Python3 は使うべき。

__getitem__ for in

Python で自作クラスに __getitem__ を定義すれば for 文が使えるようになる。
しかし場合によっては無限大になったり存在しない値を戻したりしかねない。

len から自前で最大値を取得して、という方法ではないようだ。
for 文はいったい何を基準で抜けているのか。

配列の範囲外を得ようとすると IndexError の例外を投げてくる。
ということはコレを利用して for 文を抜けているのだろうか。
下記の __getitem__ が無限になってしまうコードに入れて実験。

#!/usr/bin/env python3

class Test():
    def __getitem__(self, n):
        if n > 3:
            raise IndexError
        return n * 2

t = Test()

for n in t:
    print(n)

'''output
0
2
4
6
'''

なるほど。

Poppler で範囲外を指定すると None を戻すだけで例外にならず親でエラーになっていた。
cbz 等は self.namelist が IndexError を吐いて親に raise していただけ。
Python が用意してくれた例外に頼らず自身で raise していこう。