2005.11.26

UIスレッドでのタイマー実行

ユーザインタフェースを操作する処理を一定時間ごとに繰り返し行いたい場合,スレッドを使うのが一般的である。しかし,普通に自前で生成したスレッドからはSWTのUIコンポーネントを操作することができないために,DisplayクラスのsyncExec()やasyncExec()を使用する必要がある。ある間隔を持って処理を繰り返し行う場合は,Thread.sleep(500)というようにしてスレッドの実行を停止させることが思いつく方法だが,SWTではOSのタイマーイベントを使用する方法が提供されている。

OSが持つタイマーイベントを利用するには,DisplayクラスのtimerExecメソッドを使用する。

  Runnable runnable = new Runnable() {
    public void run() {
      // 繰り返し行いたい処理
      Display display = ...;
      if (!display.isDispose())
        display.timerExec(500, this);
    }
  };
  Display display = ..;
  if (!display.isDispose())
    display.timerExec(500, runnable);

繰り返し行いたい処理は,Runnableインタフェースの実装として作成する。上記では,匿名クラスとしてRunnableインタフェースの実装オブジェクトを生成している。そして,Displayオブジェクトが破棄されているかどうかをチェックし,破棄されていなければ,DisplayオブジェクトのtimerExecメソッドに時間とRunnableオブジェクトを渡す。第1引数は,Runnableオブジェクトのrunメソッドを呼び出すまでの待ち時間を指定する。上記では,0.5秒後にrunメソッドを呼び出すように指定している。

timerExecメソッドによって,OSにタイマーの登録が行われ,指定時間後にタイマーイベントが発生し,それを契機としてrunメソッドが実行される。ただし,実行されるのは1回だけ。繰り返し処理を行いたい場合は,runメソッド内で再度timerExecメソッドを呼び出して再びrunメソッドが実行されるように登録すればよい。その際に,Displayメソッドが破棄されているかどうかをチェックする必要がある。

このrunメソッドの呼び出しはUIスレッドが行うので,SWTのUIをrunメソッド内で操作することが可能だ。

あるUIコンポーネントを定期的に操作する方法としては,原則として上記のtimerExecメソッドを使用するべきだ。例えば,下記のように自前でスレッドを生成し,その中で繰り返しsyncExecメソッドなどの呼び出しを行ってUIコンポーネントを操作した場合は,他のコンポーネントに影響が発生してしまう。

  Runnable runnable = new Runnable() {
    public void run() {
      while(true) {
        Display display = ..;
        if (!display.isDispose()) {
          display.syncExec(new Runnable() {
            public void run() {
              // 繰り返し処理
            }
          });
        }
        Thread.sleep(500);
      }
    }
  };
  (new Thread(runnable)).start();

上記のようなコードでも,繰り返し処理はちゃんと実行されるだろう。しかし,DirectoryDialogクラスを利用して実現されるディレクトリ選択ダイアログの動作が,正しく行われなくなってしまう。具体的には,サブディレクトリやアイコンの読み込みがマルチスレッド化されているのだが,そのスレッドが動作しなくなってしまい,ディレクトリの選択ができないといった現象になる。

気軽にスレッドを作ることはできるが,その結果思わぬ不具合を引き起こしてしまう可能性があるので,気をつけて欲しい。

| | コメント (16) | トラックバック (1)

2005.11.24

ステータスバーへのコンポーネント登録と削除

ステータスバーへのアイコン登録

今回は,Eclipseのワークベンチウィンドウに配置されているステータスバーへアイコン表示コンポーネントを登録する方法を紹介する。アイコンはSWTのLabelコンポーネントを使うので,実質上はSWTコンポーネントのステータスバーへの登録方法となる。

まず,普通にエディタがアクティブなときのステータスバーを見てみると,以下のような感じになっている。

status0

編集モードやキャレットの位置などが,セパレータで区切られて表示されている。もちろん,自分のプラグインの領域を確保したいので,セパレータの登録も是非やっておきたい一つだろう。

まずはワークベンチウィンドウから,ステータスバーマネージャを取得する。この方法は,「ステータスバーへのアクセス」で紹介した手順で行えばよい。基本的には,得られたIStatusLineManagerオブジェクトのaddメソッドを使って,コンポーネントやアクションをステータスバーに登録する。例えば,アクションを登録するには以下のような感じになる。

  IStatusLineManager manager = ...; // ステータスバーオブジェクトの取得
  manager.add(new Action("アクション") {});

status1

SWTのコンポーネントをステータスバーに登録するためには,IContributionItemインタフェースを実装したクラスのオブジェクトが必要となるが,ControlContributionクラスを使用することが一般的だろう。

  manager.add(new ControlContribution("my_id") {
    protected Control createControl(Composite parent) {
      new Label(parent, SWT.SEPARATOR); // セパレータの登録
      Label l = new Label(parent, SWT.NONE); // アイコンの登録
      ImageDescriptor desc = ...;
      l.setImage(desc.createImage());
      return parent;
    }
  });

登録するIContributionItemオブジェクトには,IDが必要となる。このIDは,ステータスバーからオブジェクトを取得したり削除する際に使用できる。上記では,ControlContributionクラスのコンストラクタにIDを渡している。

セパレータは,Labelクラスのコンストラクタの第2引数にSWT.SEPARATORを渡すことで登録できる。上記の例では,その後にイメージを持つLabelオブジェクトを登録している。createControlメソッドに渡されるCompositeオブジェクトは,レイアウトとしてStatusLine$StatusLineLayoutクラスが適用されていて,コンポーネントの生成順に左から配置してくれるようになっている。

これを実行すると,以下のような感じになる。アイコンは,IP Messengerのものを使用してみた。

status2

登録したIContributionItemオブジェクトを削除するためには,以下のようにすればよい。

  IContributionItem item = manager.find("my_id");
  manager.remove(item);
  manager.update(true);

findメソッドにIDを渡してIContributionItemオブジェクトを取得し,それをremoveメソッドに渡して削除する。その後,updateメソッドを呼び出して,削除したことをステータスバーの表示に反映させる。

上記のIContributionItem系の扱いは,ツールバーやメニューバーに関しても同じなので,覚えておくと応用が利くだろう。

| | コメント (0) | トラックバック (0)

2005.11.22

ステータスバーへのアクセス

Eclipseの最下部には,いろんな情報を通知してくれるステータスバーが位置している。自作プラグインから,このステータスバーを利用したいと思うことも多いだろう。今回は,Eclipseのステータスバーへアクセスするための方法を紹介する。

statusbar

Eclipseの下部に配置されているステータスバーは,状況依存のステータスバーである。つまり,状況によって表示される内容が異なるということだ。ここで言う状況とは,ワークベンチ内で何が選択されているか,という状況である。例えばテキストエディタがアクティブになれば,編集属性やキャレットの位置が表示されるし,アウトラインビューがアクティブになれば,アウトラインに関する情報に表示が切り替わる。表示できる領域は限られているので,今どこに着目しているかによって表示が限定されるということだ。

ステータスバーを管理しているオブジェクトは,IStatusLineManagerインタフェースで表される。「ワークベンチウィンドウに張り付いているんだから,IWorkbenchWindowインタフェースに取得メソッドがあるんじゃないの?」と思ってしまうが,そんなに簡単ではない。なぜなら,状況依存という特徴があるからだ。

まずは,特定のViewに依存するステータスバーオブジェクトの取得方法から紹介しよう。ViewPartクラスのサブクラス内で,以下のようにすればステータスバーオブジェクトを得ることができる。

  IViewSite viewSite = getViewSite();
  IActionBars actionBars = viewSite.getActionBars();
  IStatusLineManager manager = actionBars.getStatusLineManager();

このmanagerオブジェクトに対してコンポーネントやアクションを登録することで,そのViewに特化したステータスバーに登録することができる。もちろん状況依存なので,managerに登録されたコンポーネントやアクションは,そのViewがアクティブになったときにしか表示されない。

次は,特定のEditorに依存するステータスバーオブジェクトの取得方法を紹介する。EditorPartクラスのサブクラス内で,以下のようにすればステータスバーオブジェクトを得ることができる。

  IEditorSite editorSite = getEditorSite();
  IActionBars actionBars = editorSite.getActionBars();
  IStatusLineManager manager = actionBars.getStatusLineManager();

このmanagerオブジェクトに対してコンポーネントやアクションを登録することで,そのEditorに特化したステータスバーに登録することができる。もちろん状況依存なので,managerに登録されたコンポーネントやアクションは,そのEditorがアクティブになったときにしか表示されない。

さて,上記の方法では,特定のViewやEditorがアクティブにならなければ,せっかく登録したコンポーネントやアクションが表示されない。時には状況に依存せずに常にコンポーネントやアクションをステータスバーに表示させておきたいこともあるだろう。状況非依存のステータスバーオブジェクトを得る方法があるのだが,Eclipse的には反則気味なやり方になる。internalパッケージのクラスを利用しなければならないからだ。

  WorkbenchWindow workbenchWindow = (WorkbenchWindow)getSite().getWorkbenchWindow();
  IActionBars actionBars = workbenchWindow.getActionBars();
  IStatusLineManager manager = actionBars.getStatusLineManager();

WorkbenchPartクラス(ViewPartやEditorPartの親)のサブクラス内で上記の処理を行うことによって,状況非依存のステータスバーのオブジェクトを得ることができる。このmanagerオブジェクトに対してコンポーネントやアクションを登録することで,ViewやEditorの選択状態に関わらず,常にコンポーネントやアクションが表示されるようになる。

ただでさえ狭い領域なので,ステータスバーにはむやみに常に表示されてしまうようなコンポーネントやアクションを登録するべきではない。internalパッケージにしか取得メソッドが存在しないのは,推奨できない方法なんだというメッセージが含まれているのだろう。よほど特殊な場合でなければ状況依存の方法を採用し,どうしてもという時は,16x16のアイコンの登録くらいに押さえておくようにすべきだろう。

| | コメント (0) | トラックバック (0)

2005.10.14

Contributing to Eclipse本のサンプルコード

「Eclipseプラグイン開発を行うなら,まずはこれを読め!」という濃い内容の「Contributing to Eclipse」本,これに掲載されているソースコードのダウンロード元をいつも忘れてしまうので,メモしておく。

Eclipseプラグイン開発(Contributing to eclipse)
http://www.asahi-net.or.jp/~yf8k-kbys/eclipse.html

上記は訳本の紹介サイトだけど,ソースコードは基本そのまんまなので有効である。

| | コメント (0) | トラックバック (0)

2005.10.01

sourceforge.jpはじめました(ipmsg4e)

IP Messenger for Eclipseの開発を進めるにあたって,やはり「オープン」であることが大事かなと思い,sourceforge.jpにてソースコードやトラッキングなどの情報を,一般に公開することにした。

[IP Messenger for Eclipse(ipmsg4e) : sourceforge.jp]
https://sourceforge.jp/projects/ipmsg4e/

もちろんこのブログ内でもエントリは続けていくが,このブログはあくまで「プラグイン開発のための情報」をエントリしていきたいので,ipmsg4eについてのことは上記の場所で行っていくことにする。

もし要望などのご意見があれば,上記のフォーラムなどでどんどん連絡してほしい。

| | コメント (5) | トラックバック (0)

2005.09.19

早速バグ発見(IPMessengerプラグイン:コア)

j2sdk1.4.2(Windows,Linux)でEclipseを動作させているときに,何らかのメッセージを受け取ると文字列解析に失敗(NoSuchMethodError)する。コアのバージョン0.0.1を公開したので,j2sdk1.4.2を使用している方はアップデートして欲しい。

外見の現象としては,ユニキャストを受信できないというものだった。開発開始当時,DatagramSocketの使い方を間違っていて,ブロードキャストされたものしか受信できないという全く同じ現象がでたので,「あれ?JDK1.5とJDK1.4.2でDatagramSocketの実装が違うの?VMのバージョン見てDatagramSocketの使い分けしないといけない?」とか疑ってしまった。

なんてことはない。JDK1.5から搭載されたAPIを使ってしまっていただけのことだった。早合点してしまった自分に反省。orz

| | コメント (0) | トラックバック (0)

2005.09.18

IPMessengerプラグイン公開開始しました

予告していたIP Messenger for Eclipseだが,アップデートサイトに配置し,公開を開始した。

update-site: http://www.eisbahn.jp/update-site/

ipmsg

今回の公開は,とりあえず「ちゃんと作ってますよ」というメッセージをこめて,バージョン0.0.0で公開することとした。つまり,使い勝手はもちろん,ニックネームの設定もできず,機能も不足していることを完全に自覚した「お試しバージョン」として捉えて欲しい。

なので,Eclipse3.1+JDK1.5.0_04(Windows, Linux)の組み合わせでしか動作確認をしていない。JDK1.4.2シリーズでもうまくいくとは思うけど,もしうまく動作しなかったらコメントを寄せて欲しい。

IP Messengerビューとして作成してあるので,[Windows]-[Show View]メニューで「Other」カテゴリからビューを表示して使用する。使い方については,前回のエントリを参考にして欲しい。

このバージョン0.0.0では,超基本的な機能として,ネットワークへの参加・脱退,メッセージの送信・受信,開封確認という機能のみの実装である。不在通知や暗号化,ファイル転送については,たぶん後回しにする。それよりも,Eclipseならではの機能の実装を優先するつもりでいる。何故かは,そのうち発表しようと思う。

IP Messengerプラグインは,コアとUIの2つのプラグインに分けて開発した。コアはIP Messengerの基本的な機能を提供するプラグインだ。ネットワークへの参加・脱退,メッセージの送受信,ネットワーク上にいるユーザの管理などが,コアにより行われる。UIのプラグインは,コアの機能を使用して,ユーザインタフェースを提供する。

コアのプラグインについては,拡張ポイントも準備している。ソース公開を後日予定しているので,興味のある方についてはもう少しお待ちくださいということで。

「動かない!」「いい感じ!」「こんな機能が欲しい!」「こんな操作感がいいのでは?」などなど,どしどし意見を寄せて欲しい。あまり否定的な意見だとモチベが下がっちゃうので,できれば前向きな意見を下さると嬉しいかも。

では,お試しくださいっ!!

| | コメント (11) | トラックバック (1)

2005.09.05

IPMessengerプラグイン作ってます!

翻訳プラグインの超マイナーバージョンアップのお知らせを久々にエントリしたこのブログ。気がつけば,10万件アクセスを突破した。着実にEclipseプラグイン開発が広がりを見せている証拠だと思う。非常に嬉しい限りだ。

さて,最近の自分といえば,Eclipseプラグインから離れていた・・・わけではなく,新プラグインの開発を虎視眈々ともくろんでいた。そしてついさっき,基本的な機能が動き出したので,ここで紹介しようと思う。

今僕は,IPMessengerのEclipseプラグイン版を開発している。その画面イメージは以下のような感じだ。

ipmsg1

左側にメンバーの一覧とメッセージの入力エリア,右側に送受信履歴とメッセージの表示エリアを配置している。[送信]ボタンを配置すると場所をとってしまうので,入力エリアで「Ctrl+Space」キーを押すことで送信できるようにしている。送信したメッセージが開封されたときは,送受信履歴一覧の未読の「○」が消えるようにしている。

送信処理のリトライ送信やメッセージの暗号化,ログファイルの出力など,本来のIPMessengerが持つ基本機能をまだ実装していないので,一般公開はもうちょっとかかりそう。まぁ,IPMessenger開発研究室からC++やJavaの参考にできるコードを入手できるので,そう時間はかからないはず。

それよりも,Eclipseプラグインだからこその機能をいろいろと盛り込んでみたい。今思っているのは,
  (1) 「プロジェクト/ファイル/位置」をメッセージに付加して,相手側のエディタで表示させる機能。
  (2) あるコードを変更したことを,他ユーザに通知する機能
って感じだ。共同作業の助けとなる機能が中心になるだろう。もちろん,相手が普通のIPMessengerでも,読んで意味のわかる記述にする必要がある。

とにかく,元のIPMessengerがダイアログベースなものなため,ユーザインタフェースについては,まだまだかなり改善しないといけない。エディタとの連携というか,エディタ上にいろいろ噴出し的な表示をするようにしても面白いかも。

もし「こうして欲しい」みたいな意見などがあれば,是非コメントを寄せて欲しい。もちろん,基本機能を最優先に搭載して,さっさと一般公開するようにするつもりだが,さくっと組み込めるものであれば一緒に組み込むことも可能だと思うので,どしどし意見をくれると嬉しい。

| | コメント (12) | トラックバック (2)

2005.07.31

翻訳ビュープラグインVer. 1.1.1公開!

翻訳ビュープラグインを久しぶりにバージョンアップした。といっても,だいぶ前に修正したものなんだけど。。。

修正点は,Linuxでもちゃんと動くようにした,という点だけ。文字化けしてしまうという報告を受けていたので,その対処を施した。

それと,アップデートサイトの場所を変更した。今まではso-netの自分の領域だったが,自ドメインを取得したので,以下の場所に変更した。

update-site: http://www.eisbahn.jp/update-site/

Windows以外でEclipseをお使いの方は,是非試してみて欲しい。

| | コメント (1) | トラックバック (1)

2005.04.09

Eclipse起動時のプラグイン活性化

Eclipseは,その起動にかかる時間をできる限り一定にするために,いろいろな工夫が施されている。プラグインの集合がEclipseを形成しているのだが,プラグインの数は半端じゃないし,起動時に活性化しなくても良いプラグインも多く存在する。すべてのプラグインを活性化していてはEclipseの起動時間は大変なことになるので,最低限起動に必要なプラグインのみが活性化されるようになっている。

さて,プラグインによっては,Eclipse起動時に何かを行わなければならない処理もあるだろう。例えば,ワークスペースに対する何らかの変更を監視するリスナーを追加したり,Eclipseを何らかのドメインに特化した開発環境にする,などが考えられる。そもそもEclipseの起動時にのみ何かを行いたいだけのプラグインもあるかもしれない(思いつかないけど)。

久々の今回は,Eclipseの起動時にプラグインを活性化させるための方法について紹介する。起動時に活性化するプラグインは,org.eclipse.ui.startup拡張ポイントを使用する

  <plugin id="..." ...>
    ...
    <extension point="org.eclipse.ui.startup"/>
    ...
  </plugin>

さらに,そのプラグインのPluginクラスに対して,IStartupインタフェースを実装しておく。

  public class MyPlugin extends AbstractUIPlugin implements IStartup {
    public void start(BundleContext context) throws Exception {
      ...
    }
    public void earlyStartup() {
      ...
    }
  }

これにより,Eclipse起動時にプラグインが活性化され,上記のMyPluginクラスのインスタンスが生成される。それと同時に,startメソッドとearlyStartupメソッドが順に呼び出される

上記のような場合は,startメソッドとearlyStartupメソッドを呼び出すスレッドは同一のようなので,どちらに処理を記述しても問題ないだろう。まぁ気分的には,startメソッドに書くのが自然だと思う。なお,活性化時点で他のIStartup実装オブジェクトを実行したい場合は,org.eclipse.ui.startup拡張ポイントのclass属性にIStartup実装クラスを記述しておくことで実現できる。

もちろん,IStartup実装クラス内には,いつまでたってもEclipseが起動してこないのはよろしくないので,比較的短時間で終わる処理を記述することが好ましい。

| | コメント (0) | トラックバック (0)

より以前の記事一覧