0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

macOSのFinderでNetCDFファイルをダブルクリックしてncviewで開く

0
Posted at

この記事はChatGPT-6 Atlasにより執筆されました。この記事の内容をまるっとcodexなどに渡すと実装してくれると思います。

--

気象・気候データの中身を確認するとき、私はよく ncview を使っています。

通常はターミナルで次のように実行します。

ncview sample.nc

ただ、Finderでファイルを探しているときは、そのままダブルクリックして開けると便利です。

そこで、Finderからファイルを受け取り、XQuartz経由でncviewを起動する小さなmacOSアプリを作成しました。この記事では、その作成方法とファイルの関連付けを紹介します。

動作確認した環境

  • Apple Silicon搭載Mac
  • macOS 27.0.1
  • Homebrew版 ncview:2.1.11_1
  • XQuartz導入済み

この環境で、Finderから .nc ファイルを開き、ncviewが起動することを確認しました。

以下の手順は、ncviewが /opt/homebrew/bin/ncview にインストールされている環境を対象にしています。Intel Macや別のmacOSバージョンでの動作は未確認です。

仕組み

作成する ncview.app は、ncview本体を含まない起動用アプリです。

FinderでNetCDFファイルをダブルクリック
    ↓
ncview.appがファイルのパスを受け取る
    ↓
XQuartzを起動し、接続できるまで待つ
    ↓
インストール済みのncviewにファイルを渡す

アプリは、次の3つで構成します。

ファイル 役割
ncview-launcher Finderから「ファイルを開く」要求を受け取る
launch-ncview.sh XQuartzを準備し、ncviewを起動する
Info.plist アプリ名や、扱うファイル形式をmacOSに伝える

1. ncviewとXQuartzを用意する

Homebrewが利用できる環境で、必要なものをインストールします。すでに導入済みなら、この操作は不要です。

brew install ncview
brew install --cask xquartz

参考:Homebrewのncview、HomebrewのXQuartz

XQuartzの初回インストール後は、ログアウトしてログインし直すと、DISPLAY 環境変数が更新されます。XQuartzのインストール案内

まずは、ターミナルから手元のNetCDFファイルを開けることを確認します。

ncview /path/to/sample.nc

以降のアプリ作成には、Command Line Toolsも必要です。

xcode-select -p

未インストールの場合は、次のコマンドから導入します。

xcode-select --install

2. 作業フォルダを作る

以下のコマンドは、同じターミナルで順番に実行してください。

mkdir -p ~/ncview-finder-build
cd ~/ncview-finder-build

mkdir -p ncview.app/Contents/MacOS
mkdir -p ncview.app/Contents/Resources

すでに同名のフォルダやアプリがある場合は、別の作業フォルダを使ってください。

3. Finderからファイルを受け取るプログラムを作る

Finderから渡されたファイルごとに、起動用スクリプトを実行します。

ファイルのパスはシェルコマンドの文字列に埋め込まず、NSTask の引数として渡します。これにより、ファイル名に空白が含まれていても、1つのパスとして扱えます。

cat > main.m <<'EOF'
#import <Cocoa/Cocoa.h>

@interface Launcher : NSObject <NSApplicationDelegate>
@end

@implementation Launcher

- (void)application:(NSApplication *)app
          openFiles:(NSArray<NSString *> *)paths {
    for (NSString *path in paths) {
        NSTask *task = [NSTask new];

        NSString *script = [[NSBundle mainBundle]
            pathForResource:@"launch-ncview"
                     ofType:@"sh"];

        task.executableURL = [NSURL fileURLWithPath:script];
        task.arguments = @[path];

        NSError *error = nil;
        if (![task launchAndReturnError:&error]) {
            NSAlert *alert = [NSAlert new];
            alert.messageText = error.localizedDescription;
            [alert runModal];
        }
    }

    [app replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
}

- (BOOL)applicationShouldHandleReopen:(NSApplication *)app
                    hasVisibleWindows:(BOOL)flag {
    NSOpenPanel *panel = [NSOpenPanel openPanel];
    panel.allowsMultipleSelection = YES;

    if ([panel runModal] == NSModalResponseOK) {
        NSMutableArray *paths = [NSMutableArray new];

        for (NSURL *url in panel.URLs) {
            [paths addObject:url.path];
        }

        [self application:app openFiles:paths];
    }

    return NO;
}

@end

int main(int argc, const char *argv[]) {
    @autoreleasepool {
        NSApplication *app = [NSApplication sharedApplication];
        Launcher *delegate = [Launcher new];

        app.delegate = delegate;
        [app run];
    }

    return 0;
}
EOF

clang -fobjc-arc -framework Cocoa \
    main.m \
    -o ncview.app/Contents/MacOS/ncview-launcher

この記事では、主にNetCDFファイル側から開く使い方を想定しています。

4. XQuartzとncviewを起動するスクリプトを作る

Finderから起動したアプリでは、ターミナルと同じ環境変数を利用できるとは限りません。

そこで、コマンドのパスを明示し、launchctl getenv DISPLAY で取得できる値を使います。XQuartzの起動直後はまだ接続できない場合があるため、xdpyinfo で確認しながら待ちます。

cat > ncview.app/Contents/Resources/launch-ncview.sh <<'EOF'
#!/bin/zsh

export PATH=/opt/homebrew/bin:/opt/X11/bin:/usr/bin:/bin:/usr/sbin:/sbin

log_dir="$HOME/Library/Logs/ncview-finder"
mkdir -p "$log_dir"
exec >>"$log_dir/launcher.log" 2>&1

/usr/bin/open -a /Applications/Utilities/XQuartz.app

for attempt in {1..30}; do
    session_display=$(/bin/launchctl getenv DISPLAY)

    [[ -n "$session_display" ]] && export DISPLAY="$session_display"
    [[ -z "$DISPLAY" ]] && export DISPLAY=:0

    /opt/X11/bin/xdpyinfo >/dev/null 2>&1 && break
    sleep 1
done

/opt/X11/bin/xdpyinfo >/dev/null 2>&1 || exit 1

exec /opt/homebrew/bin/ncview "$1"
EOF

chmod +x ncview.app/Contents/Resources/launch-ncview.sh

DISPLAY=:0 は、環境変数が取得できないときの補助的な設定です。すべての環境で接続できることを保証するものではなく、最後に xdpyinfo で接続できなければ終了します。

5. NetCDFを扱うアプリとして登録する

Info.plist にアプリの情報と、扱うファイル形式を記述します。

ここでは .nc、.nc4、.cdf、.netcdf を登録します。ただし、拡張子が一致していても、ファイルの内容がNetCDFとは限りません。特に .cdf は、ほかの形式でも使われるため、関連付けは必要に応じて行ってください。

cat > ncview.app/Contents/Info.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleIdentifier</key>
    <string>local.kanon.ncview-finder</string>

    <key>CFBundleName</key>
    <string>ncview</string>

    <key>CFBundleExecutable</key>
    <string>ncview-launcher</string>

    <key>CFBundlePackageType</key>
    <string>APPL</string>

    <key>CFBundleVersion</key>
    <string>1</string>

    <key>LSUIElement</key>
    <true/>

    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeName</key>
            <string>NetCDF data</string>

            <key>CFBundleTypeRole</key>
            <string>Viewer</string>

            <key>LSHandlerRank</key>
            <string>Owner</string>

            <key>LSItemContentTypes</key>
            <array>
                <string>edu.ucar.unidata.netcdf</string>
            </array>

            <key>CFBundleTypeExtensions</key>
            <array>
                <string>nc</string>
                <string>nc4</string>
                <string>cdf</string>
                <string>netcdf</string>
            </array>
        </dict>
    </array>

    <key>UTImportedTypeDeclarations</key>
    <array>
        <dict>
            <key>UTTypeIdentifier</key>
            <string>edu.ucar.unidata.netcdf</string>

            <key>UTTypeDescription</key>
            <string>NetCDF data</string>

            <key>UTTypeConformsTo</key>
            <array>
                <string>public.data</string>
            </array>

            <key>UTTypeTagSpecification</key>
            <dict>
                <key>public.filename-extension</key>
                <array>
                    <string>nc</string>
                    <string>nc4</string>
                    <string>cdf</string>
                    <string>netcdf</string>
                </array>

                <key>public.mime-type</key>
                <string>application/x-netcdf</string>
            </dict>
        </dict>
    </array>
</dict>
</plist>
EOF

構文を確認して、ローカル実行用の署名を付けます。

plutil -lint ncview.app/Contents/Info.plist
codesign --force --sign - ncview.app

この署名は ad-hoc署名です。Appleが発行するDeveloper IDによる署名や、Appleの公証とは異なります。

6. アプリを配置し、Finderで関連付ける

Finderで作業フォルダを開きます。

open .

作成した ncview.app を、ユーザー用の ~/Applications フォルダへ移動します。フォルダがなければ、先に作成します。

mkdir -p ~/Applications

同名のアプリがある場合は、上書きする前に既存のアプリを確認してください。

次に、Finderで以下の操作を行います。

  1. .nc ファイルを1つ選択する。
  2. ⌘ + I で「情報を見る」を開く。
  3. 「このアプリケーションで開く」を展開する。
  4. ncview.app を選ぶ。表示されない場合は「その他…」から選択する。
  5. 「すべてを変更…」をクリックする。

この操作は、選択したファイルだけでなく、同じ種類のファイルに適用されます。Appleの関連付け手順

設定後、.nc ファイルをダブルクリックすると、XQuartz経由でncviewが起動します。

.nc4 なども使う場合は、その拡張子のファイルでも開き方を確認してください。

うまく開かないとき

まず、ターミナルから同じファイルを開けるか確認します。

/opt/homebrew/bin/ncview "/path/to/sample.nc"

ターミナルからも開けない場合は、ncview・XQuartz・入力ファイルの状態を確認します。

ターミナルからは開けるのにFinderから開けない場合は、起動ログを確認します。

tail -n 80 ~/Library/Logs/ncview-finder/launcher.log

今回の起動用スクリプトでは、ncviewの出力やエラーをこのファイルに保存しています。

XQuartzへの接続を確認する場合は、次のコマンドも使えます。

launchctl getenv DISPLAY
/opt/X11/bin/xdpyinfo

なお、この簡易版では、XQuartzへの接続失敗を専用のダイアログでは通知しません。開いても何も起きない場合は、ログとXQuartzの起動状態を確認してください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?