Open パネル

ここでは、NSOpenPanel の使い方についてメモしていく。実際にファイルを開く処理は別の項目としてメモる予定。

Open Panel は、Mac OS X 標準でついてる、ファイルを開くときなどに出てくるウィンドウ(パネル)のこと。アプリケーションでファイルやフォルダを選ぶときに使う。

まずは、NSOpenPanel オブジェクトを作る。

panel = NSOpenPanel.openPanel

これ以外に、Ruby っぽく、

panel = NSOpenPanel.new

でもいいはず。

これにいろいろと設定を加えていく。

見た目の設定

setTitle(string) ウィンドウにタイトルをつける

panel.setTitle("Select a file to open")

setMessage(string) ウィンドウのにメッセージを加える。ジーニーパネルのときはタイトルが使えないのでこれを使う

デフォルトでは何も表示がない。

panel.setMessage("Select a file to open")

setPrompt(string) 通常 OK ボタンのある位置のボタンの文字を設定する

デフォルトでは、Open。

panel.setPrompt("Open")

setAccessoryView(view) Open Panel にカスタムビューを追加する。

追加するカスタムビューの作り方については、Accessory View を追加するに詳しく書いてあるのでそちらを参照してください。

panel.setAccessoryView(view)

機能の設定

setCanChooseFiles(true/false) ファイルを選べるようにする

デフォルトでは true。

panel.setCanChooseFiles(true)

setCanChooseDirectories(true/false) ディレクトリ(フォルダ)を選べるようにする

デフォルトでは false。

panel.setCanChooseDirectories(true)

setResolvesAliases(true/false) エイリアスのオリジナルファイルへたどるか設定する

デフォルトでは true。

panel.setResolvesAliases(true)

setAllowsMultipleSelection(true/false) 複数のファイルを選べるようにする

デフォルトでは false。

panel.setAllowsMultipleSelection(true)

setCanCreateDirectories(true/false) 新規フォルダを作るボタンを表示するかどうか設定する

デフォルトでは false。

panel.setCanCreateDirectories(true)

setTreatsFilePackagesAsDirectories(true/false) パッケージになっているフォルダ(アプリケーションや一部のアプリケーションのファイル)

をフォルダとして扱うかどうかを設定する。デフォルトでは false。これをできるようにすると、アプリケーションや特定のアプリケーションの

書類がパッケージになっている場合でも、その中のファイルにアクセスできる。

panel.setTreatsFilePackagesAsDirectories(true)

動作の設定

パネルの設定が終わったら、パネルの動作の設定に入る。4種類あるが、ここでは、使い方がわかった3つだけ説明する。

runModalForTypes(filetype) 普通のウィンドウで、ファイルタイプだけ指定できる

runModalForDirectory_file_types(directory,file,filetype) 普通のウィンドウでディレクトリ(フォルダ)、ファイル、ファイルタイプが指定できる

beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo(directory,file,filetype,window,modalDelegate,didEndSelector,contextInfo)

ウィンドウから現れるウィンドウ(Genie ウィンドウ?)で、ディレクトリ、ファイル、ファイルタイプ、現われるウィンドウが指定できる。

この3つ(2つ)のメソッドに共通しているのは、filetype、directory、file の3つ。

filetype は、選ぶことのできるファイルの拡張子またはファイルタイプを指定できる。拡張子は小文字で、ファイルタイプは大文字で書く。書き方は、配列で、コロンに続けて書く。例えば、プレインテキスト、PDF、HTML を選べるようにする場合、拡張子では [:txt,:pdf,:html] と書くか、通常の文字列の配列で、["txt","pdf","html"] と書く。ファイルタイプでも書けるが、ファイルタイプがどのようにして書かれるか調べるのが面倒なので、拡張子を使うのがいい。どのファイル形式のファイルを開くかを指定しない場合は、nil と書く。

directory は、ウィンドウ(パネル)が開いたときに開くフォルダを指定する。絶対パスを文字列で書く。ただし、ホームフォルダは "~" でいい。だから、デスクトップを常に開くようにするには、"~/Desktop" とすればいい。日本語は通らないので "~/デスクトップ" ではいけない。指定しない場合は、nil と書く。

file は、あらかじめ指定したフォルダに開かせたいファイルがある場合に指定する。ウィンドウ(パネル)が開いたときに、指定したファイルが選択されている。拡張子まで正確に書かないとと選ばれない。指定しない場合は nil と書く。

beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo には、この他に4つあるが、そのうち window と didEndSelector が重要なので(後の2つはよくわからないのもある)、その2つだけ説明する。

window は、その名の通り、ウィンドウ(パネル)が出てくるウィンドウを指定する。ib_outlet で指定したウィンドウの名前をクラス変数の形で書く(@ を付ける)。名前が window なら(ib_outlet :window)@window と書く。

didEndSelctor はよくわかっていないが、ここに、ウィンドウが開いたときに動作させたいスクリプトを書いたメソッド名を文字列としていれる。openPanelDidEnd_returnCode_contextInfo が標準のようなので、この後の例ではそうしてある。ファイルが選ばれると、ここで指定したメソッドが呼ばれる。

modalDelegate はよくわからないが、self と入れておけば動くようだ。

contextInfo はよくわからないので nil にしておく。

この3つのうち、runModalForTypes と runModalForDirectory_file_types はファイルを選んだ後の処理が同じだが、beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo は上で書いたように異なる。

いずれも、panel に対するインスタンスメソッドとして扱う。

panel.runModalForTypes(nil)

panel.runModalForDirectory_file_types(nil,nil,nil)

panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo(nil,nil,nil,@window,self,"openPanelDidEnd_returnCode_contextInfo",nil)

runModalForTypes と runModalForDirectory_file_types では、この後、続けて filname もしくは filenames でファイル名を取り出す。前者は、複数のファイルが選べないときにつかい、ファイルへのパスが NSCFString として取り出せる。後者は複数のファイルが選べるときに使い、ファイルへのパスが配列(NSCFArray)で得られる。Ruby で処理する場合は、to_s または to_a をつけて Ruby の文字列、配列に変換して処理する。配列の中は NSCFString なので、それぞれを to_s で Ruby の文字列に変換する。変換しなくても扱えるが、正規表現など扱えない場合があるので注意する。RubyCocoa のメソッドで扱う場合は、そのままでいい。

panel.filename.to_s

panel.filenames.to_a

filename/filenames の代わりに URL/URLs を使うと NSURL のオブジェクトが得られる。これを to_s で Ruby の String オブジェクトにすると、パスが URL の書式で得られる。ローカルやネットワーク上だと file:// で URL になる。

panel.URL.to_s

panel.URLs.to_a

panel.runModalForTypes(nil) と panel.runModalForDirectory_file_types(nil,nil,nil) は、ファイルが選ばれたかどうかを返すので、結果を result などのオブジェクトとして扱えば、戻り値によって処理を選べる。例えば、

result = panel.runModalForDirectory_file_types(nil,nil,nil)

if result == 1

fileName = panel.filename.to_s

else

return

end

などとして、ファイルもしくはフォルダが選ばれたときだけファイル名を取り出す処理ができる。

panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo(nil,nil,nil,@window,self,"openPanelDidEnd_returnCode_contextInfo",nil) の場合は、この後の処理の仕方が違う。上で書いたように、"openPanelDidEnd_returnCode_contextInfo" というメソッドを、この OpenPanel のメソッドと別に作り、それを実行させる。例えば、次のようなメソッドを書く。メソッドの名前は、別にこの通りでなくてもいいみたい。多分全く別でもいいんじゃないかと思うけど、たくさんあってわからなくなると困るので最初の部分に元となるメソッドの名前を加えてる。たとえば、addFiles(sender) なんて ib_action を作ったら、addFilesOpenPanelDidEnd_returnCode_contextInfo なんて具合。長すぎるから短くした方がいいかな?ただ情報は3つ送られるようなので、それは得られるようにする。

この returnCode には、上の result と同じように戻り値が入る。あと、この方法だと、このメソッドでの処理が終わるまで、Open パネルが閉じない。複雑な処理をさせる場合などは、明示的に close で閉じる。

def openPanelDidEnd_returnCode_contextInfo(panel,returnCode,info)

if returnCode == 1

panel.close

fileName = panel.filename.to_s

else

return

end

end

サンプル

最後にサンプルスクリプトを2つのせておく。これらは、Interface Builder で、ウィンドウに一列のテーブルとボタン一つを作り、テーブルの列(1列目)に col1 という Identifier を付ける。テーブルの設定については、Table view を Xcode と Interface Builder で設定するを参照。ボタンは、Received Action で openOpenPanel と結びつける。これを実行すると、選んだテキストファイルのパスがテーブルに表示される。試しにやってみたところ、なぜか一行目が表示されない。クリックすると出てくるんだけど。ほかでもあったから、これはバグなのかな?まあ、お試しあれ。

一行目が表示されない => これは、tableView_objectValueForTableColumn_row(tableView,col,row) の中で、col を col.to_s に、@table.tableColumnWithIdentifier('col1') を @table.tableColumnWithIdentifier('col1').to_s にすることで解決できた。

require 'osx/cocoa'

class AppController < OSX::NSObject

include OSX

ib_outlets :window, :table

def initilize

@output = []

end

def openOpenPanel(sender)

panel = NSOpenPanel.openPanel

panel.setTitle("Select a File to Open")

panel.setCanChooseDirectory(false)

panel.setAllowMultipleSelection(true)

result = panel.runModalForDirectory_file_types("~",nil,[:txt])

if result == 1

@output = panel.filenames

@table.reloadData

else

return

end

end

ib_action :openOpenPanel

def numberOfRowsInTableView(tableView(tableView)

case tableView.to_s

when @table.to_s

@output ? @output.length : 0

end

end

def tableView_objectValueForTableColumn_row(tableView, col, row)

case col.to_s

when @table.tableColumnWithIdentifier('col1').to_s

@output[row]

end

end

end

もう一つは、beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo を使ったもの。

どちらも結果は同じになるはず。この長いメソッドは表示の都合で途中で改行されているが、実際はしない。

require 'osx/cocoa'

class AppController < OSX::NSObject

include OSX

ib_outlets :window, :table

def initilize

@output = []

end

def openOpenPanel(sender)

panel = NSOpenPanel.openPanel

panel.setTitle("Select a File to Open")

panel.setCanChooseDirectory(false)

panel.setAllowMultipleSelection(true)

panel.beginSheetForDirectory_file_types_modalForWindow_modalDelegate_didEndSelector_contextInfo("~",

nil,

[:txt],

@window,

self,

"openPanelDidEnd_returnCode_contextInfo",

nil)

end

ib_action :openOpenPanel

def openPanelDidEnd_returnCode_contextInfo(panel,returnCode,info)

if returnCode == 1

@output = panel.filenames

@table.reloadData

else

return

end

end

def numberOfRowsInTableView(tableView(tableView)

case tableView.to_s

when @table.to_s

@output ? @output.length : 0

end

end

def tableView_objectValueForTableColumn_row(tableView, col, row)

case col.to_s

when @table.tableColumnWithIdentifier('col1').to_s

@output[row]

end

end

end