Home > Tags > 208
208
もっとRubyCocoaでQuicksilverプラグインを書く
- 2008-02-06 (Wed)
- default
RubyCocoaを使ってQuicksilverプラグインを書く の続編です。 前回書いたあと、hisaさんからアドバイスをもらい、RubyCocoaでプラグインを書くことができるようになりました。
ポイントは以下の2つ。
- NSPrincipalClass
- RubyActionClass < OSX::QSActionProvider
コードは、CodeReposのものをupdateしておきました。 hisaさんコードがたくさん入っていますが、煮るなり焼くなり好きにせよとのことなので、前回と同じ修正BSDライセンスとします。
NSPrincipalClass
Quicksilver プラグインに限らず、Cocoaで何らかのBundleを書く際には、Info.plistの NSPrincipalClass にクラス名を指定しておくと、そのクラスの +load() メソッドが呼ばれてプラグインが組み込まれるようです。 なので、ここでRBBundleInit()を呼んでRubyCocoaを初期化すると。とてもスマート。 前回はこのことを知らずに、無理矢理なハックをしていたのでした。
RCLoader.m:
ここでは、NSPrincipalClassにRCLoaderというクラスを指定しています。
@implementation RCLoader + (void)load { static bool installed = 0; if (! installed) { if (! RBBundleInit("load_ruby.rb", [self class], self)) { installed = true; } } }
load_ruby.rb:
そんで、RubyCocoaの初期化の中で、必要な.rbを読み込んでおく、と。
def load_ruby_programs(bundle, logger) path = bundle.resourcePath.fileSystemRepresentation rbfiles = Dir.entries(path).select {|x| /\.rb\z/ =~ x} rbfiles -= [ File.basename(__FILE__) ] rbfiles.each do |path| require( File.basename(path) ) end end OSX.init_for_bundle do |bundle, param, logger| load_ruby_programs(bundle, logger) end
RubyActionClass < OSX::QSActionProvider
前回説明したように、QuicksilverのActionを記述するには、QSActionProviderを継承したクラスでメソッドを用意します。 今回は、RubyのクラスでQSActionProviderを実装します。RubyCocoaのパワーがすごすぎる。
class RubyAction < OSX::QSActionProvider def act(arg) val = arg.stringValue OSX::QSObject.objectWithString('Hello world, ' + val) end end
あとは、このRubyActionクラスを、Info.plistのactionClassに、actionSelectorをact:に指定しておけば、ちゃんとactが呼ばれます。
疑問
- OSX.require_framework とか使わないで、QSActionProviderとかのQS frameworkが使えてるのはなぜ?
- hisaさんは、この方法だとCPU usageが高くなると懸念されているのですが、僕のところではいまひとつ高くなってるように見えません。why?
まとめ
RubyCocoaで、Quicsilverプラグインを書く方法をまとめました。 これで、あとはRubyのクラスとInfo.plistを書くだけでどんどんプラグインが書けますね。みんながんばれ!
あと、自分でもイマイチだなーと思うことでも、何かしら書いて晒すことが大事なんだなと。そんで改善していければOKなんだ。 hisaさん、ありがとうございました。
- Comments: 10
- Trackbacks: 1
Write a Quicksilver plugin with RubyCocoa
- 2008-02-03 (Sun)
- default
I wrote a Quicksilver plugin with RubyCocoa, that adds “hello world” to the passed string.
- Xcode project codes : in CodeRepos
- License : revised BSD
It should work on Tiger/Leopard if RubyCocoa is installed.
How does it work
As I mentioned in QuartzComposer CustomPatch with RubyCocoa, we just call RBBundleInit() function in plugin initializaiton phase to write some plugin with RubyCocoa. But wait, where should be the initializaiton code in Quicksilver plugin ?
We write actual plugin behavior in the class that inherits QSActionProvider, so I tried to call RBBundleInit() in the init() method in that class… however, that resulted in crashing Quicksilver :(
Then I called RBBundleInit() at only first time in performActionOnObject() of the actual Action class, and made references between Objective-C and Ruby class instances.
After that, I implemented an actual action behavior in Ruby class, and delegates from Objective-C to Ruby method, thats’ all. This is ugly, confusing, … I know, but it does work well :)
Code Snippets
ActionProvider in Objective-C:
(QSObject *)performActionOnObject:(QSObject *)dObject{ // initialize RubyCocoa static bool loaded = false; if (!loaded) { if (RBBundleInit("qs_action.rb", [self class], self)) { NSLog(@"[RubyCocoaPluginAction.performActionOnObject] RBBundleInit failed" ); abort(); } loaded = true; } // delegate actual action to Ruby class QSObject *ret = [QSObject objectWithString:[rb_ act:dObject]]; return ret; }
RubyCocoa side:
class Action def initialize(logger) @logger = logger end # write something great :) # - arg : QSObject def act(arg) val = arg.stringValue @logger.info(val) 'Hello world, ' + val end end # Action require 'osx/cocoa' OSX.init_for_bundle do |bdl, owner, log| # bdl - the bundle related with the 2nd argument of RBBundleInit # owner - the 3rd argument of RBBundleInit as optional data # log - logger for this block act = Action.new(log) owner.setInstance act end
Future works
- Better initializaiton. It should be in constractor of some class)
- Inherits QSActionProvider by Ruby class (more pure Ruby)
- Use ns_import ?
It should not be “With RubyCocoa”, but rather “By RubyCocoa”.
Conclusion
I made a start point to write a Quicksilver plugin by RubyCocoa. It is timing of initialization that is to be considered about writing some bundle in RubyCocoa. I made it in this article somehow. This article is for someone who wants to create Quicksilver plugin by Ruby, not learning unfamiliar Objective-C.
Reference
- QuartzComposer CustomPatch by RubyCocoa
- PyObjC Plug-ins : Article about writing Quicksilver plugin by PyObjC (a bit obsolete…)
- Comments: 0
- Trackbacks: 2
RubyCocoaを使ってQuicksilverプラグインを書く
- 2008-02-03 (Sun)
- default
RubyCocoaを使って、Quicksilverのプラグインを書いてみました。 受けとった文字列に、”hello world” をくっつけるだけのものです。
プロジェクト一式のコードは、CodeReposに。 ライセンスは修正BSDで。 RubyCocoaがインストールされていれば、TigerでもLeopardでも動くと思います。
仕組み
RubyCocoaでQuartzComposer CustomPatchのときに書いたように、 CocoaアプリじゃなくてプラグインをRubyCocoaで書くには、RBBundleInit() という関数をプラグインの初期化の際に呼んであげればよいわけです。 ところが、Quicksilverプラグインの初期化ってどこなんだろうという。
QSActionProviderを継承したクラスで実際のプラグインの動作 (Action) を記述するので、このクラスのinit()で RBBundleInit() を呼べばいいかなーと考えてやってみたところ、Quicksilverをクラッシュさせてしまいます。こまった。
しかたないので、Actionを記述する performActionOnObject() の中で、RBBundleInit()を最初の一度だけ呼ぶようにしました。その中で、Objective-CからRubyクラスのインスタンスを参照できるようにしておきます。
あとは、RubyのクラスでActionを実装し、Objective-CからRubyのメソッドを呼んで移譲します。これでできあがり。 かなり強引で美しくないのですが目的は果たせるかな、というダーティハックです。
コード片
Objective-CのActionProvider:
(QSObject *)performActionOnObject:(QSObject *)dObject{ // initialize RubyCocoa static bool loaded = false; if (!loaded) { if (RBBundleInit("qs_action.rb", [self class], self)) { NSLog(@"[RubyCocoaPluginAction.performActionOnObject] RBBundleInit failed" ); abort(); } loaded = true; } // delegate actual action to Ruby class QSObject *ret = [QSObject objectWithString:[rb_ act:dObject]]; return ret; }
RubyCocoa側:
class Action def initialize(logger) @logger = logger end # write something great :) # - arg : QSObject def act(arg) val = arg.stringValue @logger.info(val) 'Hello world, ' + val end end # Action require 'osx/cocoa' OSX.init_for_bundle do |bdl, owner, log| # bdl - the bundle related with the 2nd argument of RBBundleInit # owner - the 3rd argument of RBBundleInit as optional data # log - logger for this block act = Action.new(log) owner.setInstance act end
あと、やるとしたら
- もうちょっときれいに初期化する (何らかのクラスのコンストラクタが望ましい)
- QSActionProviderをRubyのクラスで継承する (よりRubyだけで書けるように)
- ns_importとかつかってみる?
「RubyCocoaを使って」 よりも、「RubyCocoaで」、スマートにプラグインを書けるようにしたいものです。 まぁ、まずは第一歩ということでひとつ。
まとめ
RubyCocoaを使って、Quicksilverプラグインをつくるためのとっかかりについて書きました。 RubyCocoaでバンドルを書くときに悩むのは初期化のタイミングだと思います。 今回はここを無理矢理解決しました。 Objective-Cを学んでる時間があったら、慣れ親しんでるRubyでちゃちゃっと コード書きたいよ! という方の参考になればと思います。
参考
- RubyCocoaでQuartzComposer CustomPatch : QuartzComposerのCustomPatchもCocoaのバンドルなので、このときの経験が活かせました。
- PyObjC Plug-ins : 本家による、PyObjCでQuicksilver pluginをつくるための情報。ただし、リンク先があちこちロストしてる ><
- ひ日誌 : RubyCocoaで Quicksilver pluginは書けるのか? : ここが発端。
2008.02.03 追記
RubyCocoaのhisaさんにコメントいただき (!)、もっとよい方法を教えてもらいました。 こちらについても、まとめて別エントリにします。
- Comments: 3
- Trackbacks: 0
QSTwitter 1.2
- 2008-01-23 (Wed)
- default
QuicksilverからTwitterに投稿するプラグイン、QSTwitterを1.2にアップデートしました。 ダウンロードはこちら。
変更点は2つ。
‘+’ 問題 を解決
1.1までは、‘+’を含むメッセージを入力するとスペースに変換されてしまうという問題がありました。 これは、NSString.stringByAddingPercentEscapesUsingEncoding が ‘+’ をエスケープしてくれないことに起因していたのですが、今回はやっつけで /+/%2B/g するようなクイックハックで解決しています。
かわいいアイコン :)

Twitter Development Talk のアイコンがかわいかったので、使っていい? とMLで聞いたところ快諾されたので、使うことにしました。(Thanks Alex !) プラグイン選択画面、プラグインの設定画面、そしてメッセージを送るときに表示されます。 ドット絵風でとってもキュート。
ぜひぜひお使いくださいね。
- Comments: 0
- Trackbacks: 1
QSTwitter 1.1 (Quicksilver Twitter Plugin)
- 2007-12-22 (Sat)
- default
QSTwitter (Quicksilver Twitter Plugin) 1.1を公開します。
QSTwitter.zip をダウンロードしてダブルクリック!
変更点は、Twitterエントリの”from=…”のところに、”QSTwitter”が入る、というものです。ユーザーエージェントみたいなものですね。
おまけ : どうやって自作Twitterアプリでfrom=”…”に自分のアプリ名を入れるか
- Twitterの中の人にメールを送る
- 自分アプリで投稿するときに “source=YourAppName“というパラメータを追加
- Twitter側の対応を待つ
でした。 僕の場合は、10日ほどで対応してもらえました。Twitterの中の人に感謝!
- Comments: 0
- Trackbacks: 0
Quicksilverプラグインのつくりかた
- 2007-12-15 (Sat)
- default
Quicksilverでプラグインをつくる方法をチュートリアルにしてまとめました。
このチュートリアルを読むことで、以下のものがつくれるようになります。
- 基本的なQuicksilverプラグイン
- プラグイン用の設定画面
- 設定画面からのデータ取得
目次
- ここでの環境
- 必要なもの
- 準備
- つくりはじめる
- PreferencePaneをつくる
- Actionのコードを書く
- おわりに
- 参照
ここでの環境
- Mac OS X Leopard 10.5.1
- Quicksilver B53 (3814)
- Xcode 3.0
必要なもの
- Quicksilver Develop版
- Xcodeのテンプレート。ほんとは本家からダウンロードできるはずなんだけど、12/15現在ダウンロードできないので、VACUOUS VIRTUOSOさんのところから相当するものをダウンロードします。
準備
ビルドできるようになるまでの準備をします。
1. ダウンロードしたXcodeのテンプレートをインストール
Xcode 3.0 からは、カスタムテンプレートを置く場所が /Developer/Library/Xcode/Project\ Templates/ になっています。 (VIRTUOSO VIRTUOSOのところでは、 /Library/Application Support/Apple/Developer Tools/Project Templates/ に置くよう書かれている)
ここでは、 /Developer/Library/Xcode/Project\ Templates/Bundle/Quicksilver\ Plug-in というパスになるように、zipを展開したディレクトリを置きます。
2. Xcode プロジェクトをつくる
1がうまくいっていれば、このように Quicksilver Plug-in が選択できます。
すすめると、こんな画面に。
このチュートリアルでは、設定画面 (PreferencePane) もつくるので、 QSInterface.frameworkにもチェックして、リンクされるようにしておきましょう。
3. Source Trees
プラグインをビルドするには、Quicksilverのフレームワークが必要です。 ダウンロードしておいたQuicksilver Developer版の中に、フレームワークがあるのでそこをXcodeに指定します。
XcodeのPreferencesを開き、Source Treesタブに移動して、画面のように設定します (Pathのところは、Quicksilver Developer版を置いた場所に読み替えてください。 例:/Applications/QS-dev.app/Contents/Frameworks)
つくりはじめる
フレームワークにのっかってビルドできるところまでいってみましょう。
4. Info.plist
さて、ここまでで準備が完了しました。 ここからは実際にプラグインをつくっていくことになります。
プラグインの情報のほとんどは、Info.plistに書くことになります。 .plistなファイルはXMLなので、Property List Editorで開くと編集にラクができます。
デフォルトのInfo.plistをProperty List Editorで開くと、次のような感じ。
個々のキー/値については、 Anatomy of a plugin - Info.plist Part 1 や Plist Specification が詳しいです。 ここでは、最小限の設定項目を埋めていきます。
5. QSPlugin
プラグインについてのメタ情報を書きます。
Before | After:
6. QSActions
実際にプラグインが何をするかについての情報を書きます。 まず、QSActionsTemplateからQSActionsに名前を変えておきます。
nameのところの値をタイプすることで、Quicksilverからこのプラグインが呼ばれるので、 それっぽい値を入れておくとよいでしょう。
directTypesのところで、受けつける入力の種類を判定するぽいです。 ここでは “*”を指定してなんでもこいにしてみました。
Before | After:
7. QSRegistration
いろいろ設定項目はありますが、ここではPreferencePaneについて。 まず、QSRegistrationTemplateからQSRegistrationに名前を変えておきます。
QSPreferencePanesの中に、classを指定できるところがあります。 ここには、後でつくるPreferencePane用のクラス名を指定します。
Before | After:
8. ビルド
そろそろこのページを見ながらちまちまと作業をするのにも飽きてきたことででしょう。 ここらでビルドして、実際に動いてるのを見ようではないですか。
Xcodeでふつうにビルドすると、*.qsplugin というファイルが生成されます。 これがQuicksilverのプラグインなのですね。
ダブルクリックすると、Quicksilverに “プラグインをインストールする? “みたいに聞かれるのでYesで進みましょう。
9. 動いてるのを見る
「.」でテキスト入力モードにして何かうちこみ、TABでActionに移りましょう。 6.で設定してある名前を入力すればほら、いまつくったプラグインが呼び出せるではないですか! (とはいえ、いまのところ何もActionを実装していないので、実行したところで何も起こりません)
次に、QuicksilverのPreferenceを開いてみます。 左のリストに、いまつくったプラグインがあると思います。やったね !
PreferencePaneをつくる
ここからがお楽しみです。
10. PreferencePaneのクラスをつくる
File→Newから、Objective-C class をつくります。 ファイル名は、7で指定してたクラス名にするとよいでしょう。
@interfaceを、QSPreferencePaneを継承するように書き換えます。
QSPluginTutorialPreferencePane.h:
#import <qsinterface/QSPreferencePane.h> @interface QSPluginTutorialPreferencePane : QSPreferencePane { } @end
@implementationに、mainNibNameというメソッドを加えます。 このメソッドは、次につくるnibファイルの名前 (から.nibを除いたもの) を返すようにします。
QSPluginTutorialPreferencePane.m:
#import "QSPluginTutorialPreferencePane.h" @implementation QSPluginTutorialPreferencePane -(NSString *)mainNibName { return @"QSPluginTutorialPreference"; } @end
ここまでのプロジェクトはこんな感じになるでしょう。
11. NIBをつくる
File→Newから、Window NIB をつくります。
12. File’s OwnerのクラスをPreferencePaneのクラスにする
このへんを 参考にして、File’s Ownerのクラスを、10でつくったクラスにします。
まず、QSPreferencePane.hと10でつくったクラスのヘッダファイルを、 NIBのウィンドウにドラッグ&ドロップします。
次に、File’s Ownerのオブジェクトを選択した状態でObject Identityの設定ウィンドウに行き、 Classを10でつくったクラス名にします。
13. File’s OwnerとWindowをつなげる
File’s OwnerのオブジェクトからCtrl+ドラッグしてWindowにドロップすると、 オブジェクトをつなげることができます。 Outlets _windowと出るので、_windowを選択しましょう。
14. コントロールを配置
好きなコントロールをWindowにつけましょう。 ここでは、シンプルなText Fieldコントロールをつけました。
15. 動かす
ふたたびXcodeでビルドし、.qspluginをつくり、インストールします。 QuicksilverのPreference→つくっているプラグインを選ぶと、ちゃんと右側にいまつくったText Fieldが 表示されることでしょう。やった !
16. バインドする
しかしこのままでは、設定画面で入力した値をどうやって使うのか分かりませんよね。 ここで、Cocoa Binding というテクニックをつかいます。
14で配置したコントロールを選択してBindngs設定ウィンドウに行き、次のように設定します。
- Bind to: Shared User Defaults
- Controller key: values
- Model Key Path: 好きな文字列 (他のところからこの値を参照するときに使うキー)
ここでは、Model Key Path を QSPluginTutorial.someKey という値にしました。
Actionのコードを書く
したいことを何でも書くところ。
17. 入力 + 設定から得た値 => 出力
Actionのクラスの実装を書きます。 ここでは、入力された文字列に、PreferencePaneから得た値を組合せて、出力にすることにしました。 コードは以下のようになります。
QSPluginTutorialAction.m:
@implementation QSPluginTutorialAction - (QSObject *)performActionOnObject:(QSObject *)dObject{ // get value from Preference Pane id values = [[NSUserDefaultsController sharedUserDefaultsController] values]; NSString *value = [values valueForKey:@"QSPluginTutorial.someKey"]; QSObject *ret = [QSObject objectWithString: [NSString stringWithFormat:@"%@ hello, %@",[dObject stringValue], value]]; return ret; } @end
18. 完成 !!!
長い道のりでしたが、ついにこれで終わりです。
ビルドし、インストールして、PreferencePaneから値を入れておきます。
テキストを入力し、Actionを選択して実行します。
もういちどQuicksilverを起動すると、入力のところに結果が入っています。やったね !
おわりに
Quicksilverでプラグインをつくる方法をチュートリアルにしてまとめました。
Quicksilver Twitter Plugin (日本語) で、Quicksilverのプラグインをつくっているとき、まとまった情報がない、情報が古い、日本語の情報が見当たらない、と何かと苦労してました。 このチュートリアルでQuicksilver野良プラグインを書く人が増えればいいなあと。 (そして素晴しいプラグインが増えて僕も幸せになれればいいなぁとw)
Catalogの扱い方とかまだ調べてみたいところがたくさんあるので、情報持ってる方いればぜひご連絡ください。
参照
Vacuous Virtuoso: ほぼ唯一のQuicksilverプラグインづくりの情報源。 基本はここを以下の順に読むとよいです。
- Quicksilver plugins in Objective-C Anatomy of a plugin - Info.plist Part 1
- Writing Quicksilver Plugins: Actions Write your own Quicksilver interface
docs.blacktree.com: 本家。でもドキュメントが散逸してます…
- Comments: 5
- Trackbacks: 0
Quicksilver Twitter Plugin
- 2007-12-13 (Thu)
- default
Update (2008.3/5) : More Information is in Trac. Please check and see the latest version there. Text below is about version 1.0, and current version is 1.4 including more features.
I wrote a small Quicksilver plugin to send messages to Twitter directly.

Advantages
- Easy to install. No additional software required to be installed
- Configurable via Quicksilver Preference
- You can send messages containing non-ASCII characters
Download
QSTwitter.zip (1.2) (2008.01.23)
Environment
Tested under Leopard 10.5.1, Quicksilver B53 3814.
Install
Unzip the downloaded file and double-click it. that’s all !
Configuration
You will find “Twitter option” in the Quicksilver preference pane. Enter your screen name and password there.
Usage
- Activate Quicksilver (by Ctrl-SPACE or so)
- type . (period/dot) key to enter text input mode
- type a message
- hit TAB to move into Action
- type “Twitter”
- return !
The more familar you are with it, the faster you can send messages than ever.
Screencast
Seeing is Believing, as you know :) I don’t know why the video is collapsing for first 15 seconds. Sorry for inconvinience.
Code
You can see the code in CodeRepos.
License
Considering…
Restriction
- I could not post messages throught HTTP proxy. Let me know if anyone succeeded to make it.
ChangeLog
- 1.2 (2008.01.23) : fixes the bug that ‘+’ is not shown in status, uses pretty girl icon .
- 1.1 (2007.12.22) : now it shows “QSTwitter” in your post on “from …” .
- 1.0 (2007.12.13) : initial release.
There seems no comprehensive documents to develop Quicksilver plugin as far as I searched. In order to share my experience to avoid falling into pitfalls, I will write an tutorial developing a Quicksilver plugin under up-to-date environment later.
- Comments: 20
- Trackbacks: 5
Quicksilver Twitter Plugin (日本語)
- 2007-12-13 (Thu)
- default
Quicksilverから直接Twitterにメッセージを送ることのできるプラグインを書きました。

ダウンロード
QSTwitter.zip (1.2) (2008.01.23)
インストール
zipを展開してでてきたpluginファイルをダブルクリックするだけ。
設定
Quicksilverの設定画面に、”Twitter option” という項目が現れるので、そこに自分のscreen nameとパスワードを入れます。
使い方
- Quicksilverを起動 (Ctrl-SPACEとかで)
- . (ピリオド) キーを押して、テキスト入力モードに
- メッセージを打ち込む
- TABでActionに移動
- “Twitter”と入力
- return !
慣れると一瞬です。
スクリーンキャスト
百聞は一見に如かずで。 開始15秒くらい見苦しくなっています。すみません。
コード
CodeReposに置いてあります。
ライセンス
修正BSD
売り
- インストールが簡単
- QuicksilverからTwitterに投稿するには、他にTweetとか選択肢がありますが、スクリプトをコピペしたりKeychainに設定を書かねばならなかったりで面倒です。
- 日本語が通る
- Quicksilverでは日本語の扱いに難があるとか聞きますが、少なくとも僕が試している限り (Leopard + AquaSKK + Quicksilver B53) では問題なく入力できています。
- 超はやい
- IMやコマンドライン、IRCから入力すればいいじゃん! という声もありましょうが、僕にとってはそれぞれのアプリにコンテキストスイッチするコストが高すぎまして。Quicksilverなら、いつでもどこからでもすぐ呼び出せて、ささっとメッセージを入力できます。
制限事項
プロキシ越しでのpostができてません→ 1.3 で入れました。
ToDo
- ライセンス決める
“from…” にsignatureを入れてもらう→ 1.1 で入れました。かわいいアイコン→ 1.2 で入れました。
ChangeLog
- 1.2 (2008.01.23) : ‘+’を含むメッセージを送るとスペースに変換されてしまう問題を解決。また、Twitterの女の子のアイコンをつかうようにしました。
- 1.1 (2007.12.22) : “from…” のとこに、”QSTwitter” を表示するようにしました。
- 1.0 (2007.12.13) : はじめのリリース。
Quicksilverのプラグインを書く方法は、うまくまとまったものが存在しないのが2007.12/13の現状です。いろいろはまったポイントがあったので、あとでそのへんのノウハウもまとめて書きます。
- Comments: 2
- Trackbacks: 2
Home > Tags > 208



























