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

Combine

python ファイルの合体スクリプトはわざわざ目印を用意する必要無いと気が付いた。
class から始まっている行から最後までを読み込みすればいい。
一部書き換えが必要だったけど下記に落ち着いた。

#!/usr/bin/env python3

import os

HEAD = '''#!/usr/bin/env python3

import objc, os, shutil, zipfile, re, subprocess
from AppKit import *
from Quartz.CoreGraphics import *

PICEXT = '\.(jpe?g|png|gif)$'
'''
FOOT = 'main()\n'

lines = [HEAD]
begin = False

for filename in [s for s in os.listdir() if s.startswith('comipoli_')]:
    with open(filename) as f:
        lines.append('# {}\n'.format(filename))
        for l in f.read().split('\n'):
            if begin:
                lines.append(l)
            elif l.startswith('class'):
                begin = True
                lines.append(l)
    begin = False

lines.append(FOOT)

with open('Comipoli.py', 'w') as f:
    f.write('\n'.join(lines))

こうなった。

後は pip3 で py2app を導入、py2applet はセットで付いてくる。

pip3 install -U py2app

ビルドスクリプトの作成。
前回のコードで icns を作る。
上記コードでソースファイルを一つに合体。
後は py2applet にこう渡すだけでアイコン付きの app を作ってくれる。
最後に片付け。

#!/bin/sh

# icns
python3 make_icns.py
iconutil -c icns comipoli.iconset

# combine
python3 make_combine.py

# build
py2applet --no-strip Comipoli.py comipoli.icns

# clean
rm -r comipoli.iconset
rm comipoli.icns
rm Comipoli.py

ただ
–no-strip オプションを付けているのは

Xcode インストール無しだとこんなダイアログが出るから。
気にせず [今はしない] を選んでも app は作れるけどね。

unrar はフルパス指定にしたらあっさり動いた。
そんなこんなでとっととサイト公開することに。

macOS アプリケーション – L’Isola di Niente

何か忘れている気がするけどまあいいや。

Create 72dpi Icon in Retina Display

JXA では手抜きをしたけど今回はキチンと ICON を作ろう。

icnsファイルの作り方(Mac) – 2番煎じMEMO

こんなに沢山の画像を作るなんて面倒だよ。
これも PyObjC で一つの画像からリサイズでやってしまおう。

画像のリサイズ保存方法は検索で簡単に見つかる。
しかし、Retina Display の mac では勝手に 144dpi になってしまう!
逆にそれを利用して 144dpi を作る、72dpi を自力で作る手段を探す。

NSImage をリサイズする。

なるほど、丸パクさせていただきます。
PyObjC では下記のように。

API Notes: Quartz frameworks ? PyObjC ? the Python ? Objective-C bridge

ということで。

#!/usr/bin/env python3

# make_icns.py
# This Program is Retina Display Mac Only

import os
from AppKit import *
from Quartz.CoreGraphics import *

# Preference
PNGFILE = 'icon.png'
ICONSET = 'comipoli.iconset'

src_image = NSImage.alloc().initWithContentsOfFile_(PNGFILE)
os.mkdir(ICONSET)
os.chdir(ICONSET)

def create_png(img, name):
    bmp = NSBitmapImageRep.imageRepWithData_(img.TIFFRepresentation())
    data = bmp.representationUsingType_properties_(NSBitmapImageFileTypePNG, {})
    data.writeToFile_atomically_(name, True)


for n in [512, 256, 128, 32, 16]:
    # 72dpi
    img = NSBitmapImageRep.imageRepWithData_(src_image.TIFFRepresentation()).CGImage()
    ctx = CGBitmapContextCreate(None, n, n, 8, 4 * n, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaPremultipliedLast)
    CGContextDrawImage(ctx, CGRectMake(0, 0, n, n), img)
    imgref = CGBitmapContextCreateImage(ctx)
    image72dpi = NSImage.alloc().initWithCGImage_size_(imgref, (n,n))
    create_png(image72dpi, 'icon_{0}x{0}.png'.format(n))
    # 144dpi
    image144dpi = NSImage.alloc().initWithSize_(NSMakeSize(n, n))
    image144dpi.lockFocus()
    NSGraphicsContext.saveGraphicsState()
    NSGraphicsContext.currentContext().setImageInterpolation_(NSImageInterpolationHigh)
    src_image.drawInRect_(NSMakeRect(0, 0, n, n))
    NSGraphicsContext.restoreGraphicsState()
    image144dpi.unlockFocus()
    create_png(image144dpi, 'icon_{0}x{0}@2x.png'.format(n))

で。

よし使える。

実はもう app 化は完成しているんだけど。
app にすると unrar にパスを通しても unrar を認識しない問題が出た。
そりゃ app は bashrc なんて読み込みしないよな、本来 macOS には無いし。

bash: ‘\n’ in read file (part2)

前回の抜き出しシェルスクリプトだけど。
echo -e で出力しようとしたのがいけなかったようだ。

while ループの中でガシガシとリダイレクトすればイケそう。
と思って Fedora で試すとバッチリ狙いどおりに出力された。

mac にソースを持って行くと、あれ?
なんかおかしいけど原因が解らない、仕事中も含め半日悩む。
下記の実験コードでやっと理解。

#!/bin/sh

while read -r line; do
    echo $line
    echo 0
done << __DOC__
aaa bbb
ccc\nddd
eee
__DOC__

Fedora

macOS

macOS の read は改行コードと文字列の \n を区別してくれない。
のではないな、echo で改行されてしまっているようだ。
echo “aa\nbb” だと普通に \n が出力されるのに、よくわからない。

# 追記
sh ではなく bash 指定なら Fedora 同様の出力になるようです。
zsh の場合 echo に -E オプションを付けないと同様な症状になる。
# 追記おしまい

後、前回は気が付かなかったけど macOS の echo には -e オプションが無い!
bash のバージョンが違うのは知っているけどまさかこんな所が違うとは。

同じ bash だから同じ挙動やオプションだと思ってはいけない教訓になった。
拡張の [[ とかは同様に使えるのにね。
てか、macOS って中身は本当に色々と古いんだよなぁ。

bash: ‘\n’ in read file

py2app で分割で作っている Python ファイルを扱う手段がわからない。
ならば合体スクリプトを作って単体にしてしまえばいいんでね?

目印に #begin, #end みたいなのを書いてその中間だけを取り出せば簡単そう。
ということで早速シェルスクリプトを書いてみた。

##!/bin/sh

ifs=$IFS
IFS=$'\n'

for filename in *.py; do
    while read -r line; do
        [[ $line =~ "#end" ]] && break
        [[ $begin = begin ]] && lines+="$line\n"
        [[ $line =~ "#begin" ]] && begin=begin
    done < $filename
    begin=""
done

echo -e $lines

IFS=$ifs

のですけど。。。

#!/usr/bin/env python3

#begin

class TestClass():
    def __init__(self):
        print('hello\nworld')

#end
TestClass()

こんなファイルで試すと。

class TestClass():
    def __init__(self):
        print('hello
world')

えぇ。。。

ソースコード中の \n まで改行とみなされてしまうのカヨ!
ちなみに -r 無しだと hellonworld になるのでもっと注意。
cat ならイケるかな。

##!/bin/sh

ifs=$IFS
IFS=$'\n'

for filename in *.py; do
    for line in `cat $filename`; do
        [[ $line =~ "#end" ]] && break
        [[ $begin = begin ]] && lines+="$line\n"
        [[ $line =~ "#begin" ]] && begin=begin
    done
    begin=""
done

echo -e $lines

IFS=$ifs

同じだ。。。

#!/usr/bin/env python3

import os

lines = []
begin = ''

for filename in [s for s in os.listdir() if s.endswith('.py')]:
    with open(filename) as f:
        for s in f.read().split('\n'):
            if '#end' in s: break;
            elif begin == 'begin': lines.append(s)
            elif '#begin' in s: begin = 'begin'
    begin = ''

print('\n'.join(lines))

python 等なら問題ない。

connect.py みたいなファイルを用意してシェルから呼ぶことにするか。
シェルってほんとに色々と困ったちゃん。

cancelOperation

OS Xアプリケーションにおける環境設定ウィンドウの作り方 ? Genji App Blog

esc でウインドウを閉じるには cancelOperation を処理するだけなのか。
でも書いてあるとおり macOS でこの動作は一時的なウインドウのみ。

オリジナルは Eye of GNOME に合わせて esc で終了できるようにしているけど。
macOS 版では本体を閉じる設定は無しにしたほうがいいようだ。
設定ウインドウやサムネイルウインドウに適用することに。

それから、macOS ってスピンボタンが全然使われていないよね。
NSStepper というものが一応あるんだけど、どのアプリも使っていない。
macOS っぽいものにしたいのでサムネイルサイズ設定は NSSlider にする。
80-200 の 20px ステップで問題無いだろう。

それから、macOS アプリの設定はどれも全体設定になっている。
つまりグローバル変数にする必要がある。
どうせなら NSApp にくっつけときたいよね、ということで。

class ComipoliApplication(NSApplication):
    # global variable
    init_frame = None
    esc_close = False
    is_pdf = False
    thumbnail_height = 120
    is_unrar = False
    is_7za = False

def main():
    #NSApplication.sharedApplication()
    ComipoliApplication.sharedApplication()

サブクラスにしちゃえです。

それと macOS でマウスを使っている人はいない、と思う。
マウスカーソルを変更してクリックでページめくりもいらないな。

設定は二つだけになってもーた。
そんなこんなで、こいつでやりたかった機能は全部付いた。
PyObjC の参考にでも使ってね。
comipoli_pyobjc_3.tar.gz

後は app 化。
py2app ってのでイケるらしいけど分割ファイルでの手段が見付からない。
たいした行数ではないので一つのソースにまとめてもいいんだけど。