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

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 ってのでイケるらしいけど分割ファイルでの手段が見付からない。
たいした行数ではないので一つのソースにまとめてもいいんだけど。

PyObjC NSWindow Cascade Position

macOS では二つ目以降のウインドウは規則的にズレて表示される。
でも NSWindow は NSRect を指定して作成する、どうやっているのか。

cascadeTopLeftFromPoint: – Cocoa API解説(macOS/iOS)

そういうことか、解説ありがとう。
window を作った後に自分で移動するのね。

objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
    frame,
    NSTitledWindowMask |
    NSClosableWindowMask |
    NSResizableWindowMask |
    NSMiniaturizableWindowMask,
    NSBackingStoreBuffered,
    False)
# Cascade Position
if len(NSApp.windows()) > 1:
    point = NSApp.keyWindow().cascadeTopLeftFromPoint_(NSZeroPoint)
    self.setFrameTopLeftPoint_(point)

で。

うん macOS アプリっぽくなった。
次は設定ダイアログの位置決めをどうするかだ。
小ネタでした。

PyObjC NSAttributedString

NSCollectionView は色々カスタムできるようなのでとやってみたけど。。。。。
いくら探してもチュートリアル Interface Builder しか出てこない。
しかもボコボコ落ちる、無意味なクラッシュレポートを Apple に沢山送ったよ。
もうあきらめて普通の NSView として使うことにするよ。

ということで。
NSView に文字列を表示したい。

[Objective-C]NSAttributedStringの背景色あたりの話|杏z 学習帳(旧)

NSMutableAttributedString というトンデモネェものがあるようだ。
筆者の用途では継承元の NSAttributedString で十分そう。

てか UIColor なの?
PyObjC には UIKit は無いぞ、あってどうするという話だが。
NSColor で当然のようにイケた、これは Apple の解説が悪いな。

comipoli オリジナルのサムネイル文字列は赤、背景色は 66ffff で 50% 半透明。
これを再現するには。

colorWithSRGBRed:green:blue:alpha: – NSColor | Apple Developer Documentation

最大値を 1.0 にして RGBA 指定でイケるのね。
やってみる。

class ItemView(NSView):
    def init(self):
        objc.super(ItemView, self).init()
        self.image = None
        self.num = 0
        return self

    def drawRect_(self, rect):
        NSColor.blackColor().set()
        NSRectFill(rect)
        if (self.image):
            self.image.drawInRect_(rect)
            bg_color = NSColor.colorWithSRGBRed_green_blue_alpha_(0.4, 1.0, 1.0, 0.5)
            text = NSAttributedString.alloc().initWithString_attributes_(
                '{}'.format(self.num + 1),
                {
                    NSForegroundColorAttributeName: NSColor.redColor(),
                    NSBackgroundColorAttributeName: bg_color
                })
            x = rect.size.width / 2
            text.drawAtPoint_(NSMakePoint(x, 0))

オリジナル

よし完成、といいたいけどまだ超不安定。