PyObjC」タグアーカイブ

Graphics

18 日に上げた PyObjC Tips の日付が 2018 年になってた!
よしさっさと更新してごまかしてまえwwwww

PyObjC Tips – L’Isola di Niente

てか、AppKit に関しては comipoli で必要な API しか調べていないんで。
全部調べて作ってだとスゲェ時間が掛かるので細切れで出すことにした。
ゴールデンウイークは 10 連休なのでもちっと進めたい。

あっちにも書いたけど。
Cocoa Dev Central: Cocoa Graphics with Quartz
Cocoa Dev Central: Cocoa Graphics with Quartz II
すごく解りやすいよね。
今後のページ作りの参考にしよう。

cairo だとココとか。
Cairo samples

日本語でこの手を探すとスクショだらけな Xcode の使い方解説ばかりで困る。
Visual Studio もそうだったね、Mac も Windows も本当に何も変わらないなと。
インストールのスキルしか上がらない Node.js よりマシ程度。
日本人はコードを書くのが嫌いなんだろうか?

import objc

今頃気が付いた。
PyObjC の AppKit に objc は含まれていた。

#!/usr/bin/env python3

#import objc
from AppKit import *

いらなかったんや!!!

PyObjC Tips ページを作っていて気がつく、書いていないのに動いたって。
作っているうちに又気がついたことがあったらコッチに書くわ、ネタがないし。
しかしゼロから作るのはどう書くか迷って中々進まない。

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 は使うべき。

WebKit on PyGObject and PyObjC

WebKit バインドは PyObjC に含まれている。
Fedora の gir には WebKit2 が含まれている。
もしかしたら共通コードでイケるんでね?

#!/usr/bin/env python3

import objc
from AppKit import *
from WebKit import *

class WView(WKWebView):
    def init(self):
        config = WKWebViewConfiguration.new()
        objc.super(WView, self).initWithFrame_configuration_(NSZeroRect, config)
        return self

class Win(NSWindow):
    def init(self):
        frame = NSMakeRect(100, 400, 900, 600)
        objc.super(Win, self).initWithContentRect_styleMask_backing_defer_(
            frame,
            NSTitledWindowMask |
            NSClosableWindowMask |
            NSResizableWindowMask |
            NSMiniaturizableWindowMask,
            NSBackingStoreBuffered,
            False)
        self.setTitle_('Web')
        self.setDelegate_(self)
        # view
        self.webview = WView.new()
        self.webview.setFrameSize_(self.contentView().frame().size)
        self.contentView().addSubview_(self.webview)
        # url
        url = NSURL.URLWithString_('https://www.google.com/')
        req = NSURLRequest.requestWithURL_(url)
        self.webview.loadRequest_(req)
        return self

    def windowDidResize_(self, sender):
        self.webview.setFrameSize_(self.contentView().frame().size)

class Menu(NSMenu):
    def init(self):
        objc.super(Menu, self).init()
        item_app  = NSMenuItem.new()
        self.addItem_(item_app)
        menu_app = NSMenu.new()
        item_app.setSubmenu_(menu_app)
        item_quit = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Quit', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

NSApplication.sharedApplication()
window = Win.new()
window.makeKeyAndOrderFront_(window)
NSApp.setMainMenu_(Menu.new())
NSApp.activateIgnoringOtherApps_(True)
NSApp.run()

PyObjC

#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
gi.require_version('WebKit2', '4.0')
from gi.repository import Gtk, GLib, WebKit2

class WebView(WebKit2.WebView):
    def __init__(self, ctx):
        WebKit2.WebView.__init__(self, web_context=ctx)

class Win(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        #
        ctx = WebKit2.WebContext()
        self.webkit = WebView(ctx)
        self.add(self.webkit)
        self.webkit.load_uri('https://www.google.com/')
        #
        self.resize(900, 600)
        self.show_all()

class App(Gtk.Application):
    __gtype_name__ = 'App'
    def __init__(self):
        GLib.set_prgname('App')
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)
    
    def do_activate(self):
        self.props.active_window.present()

App().run(sys.argv)

PyGObject

全然違ったwwwww
Apple さん、なんで URL は文字列じゃダメなの?
GNOME さん、WebKit2.WebView() ではエラーになるんですけど?

実は我がアプリに本棚機能を付けようかと考えて。
WebKit からローカル HTML でやれば GNOME, macOS 共通にできるかなって。
こりゃ無理だ。

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

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