試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

GTK+のシステムトレイのテストのコードをVala言語に移植

Gtk#のコードをもとにしてValaのコードを作成

Gtk#でシステムトレイを使用してメニューを表示する」のコードをもとにしてGTK+のシステムトレイのテストのコードをValaに移植してみた。

using Gtk;

namespace ValaSystrayTest
{
  /*
   * 継承の記述はC#と同様で、多重継承ができないのも同じ
   * また、同様にインターフェースが使用可能となっている
   */
  class TrayMenu : Gtk.Menu  // Gtk.Menuを継承
  {
    Gtk.ImageMenuItem item1;
    Gtk.ImageMenuItem item2;
    Gtk.ImageMenuItem item3;
    Gtk.ImageMenuItem item_quit;
    Gtk.StatusIcon trayicon;
    public TrayMenu (Gtk.StatusIcon trayicon)
    {
      /*
       * ストックアイコン付きメニュー項目を作成する流れ
       * メンバ変数imageには
       * ストックアイコンとサイズを指定したGtk.Imageオブジェクトを指定
       * 「with_xxx」「from_xxx」などで生成するときにも「new」が必要
       * (これらはコンストラクタとなっている)
       */
      this.trayicon = trayicon;
      this.item1 = new Gtk.ImageMenuItem.with_label ("item1");
      this.item1.image = new Gtk.Image.from_stock (Gtk.STOCK_PREFERENCES, Gtk.IconSize.MENU);
      this.item2 = new Gtk.ImageMenuItem.with_label ("item2");
      this.item2.image = new Gtk.Image.from_stock (Gtk.STOCK_OPEN, Gtk.IconSize.MENU);
      this.item2.sensitive = false;  // 選択できない状態にする
      this.item3 = new Gtk.ImageMenuItem.with_label ("item3");
      this.item3.image = new Gtk.Image.from_stock (Gtk.STOCK_CLOSE, Gtk.IconSize.MENU);
      this.item_quit = new Gtk.ImageMenuItem.from_stock (Gtk.STOCK_QUIT, null);
      /*
       * 下の形式ではGtk.AccelGroupオブジェクトを作成後
       * 2番目の引数に指定するとストックアイコンと「Ctrl+Q」のような文字列が付く
       */
      //Gtk.AccelGroup group = new AccelGroup ();
      //this.item_quit = new Gtk.ImageMenuItem.from_stock (Gtk.STOCK_QUIT, group);  // 閉じようとすると落ちる?
      /* 項目を追加していく */
      this.append (this.item1);  // 本クラスのメンバ関数(継承したappend())を呼んでいる
      this.append (this.item2);
      this.append (this.item3);
      this.append (new Gtk.SeparatorMenuItem ());  // 分割線
      this.append (this.item_quit);
      /* 表示可能にする */
      this.show_all ();
      /*
       * シグナルの設定はC#に近いが右辺にはハンドラ名のみを指定
       */
      /* トレイアイコンの通常クリック時にon_item1_activated()を呼ぶ */
      this.trayicon.activate += this.on_item1_activated;
      /* 各項目が選択されたときのハンドラを設定 */
      this.item1.activate += this.on_item1_activated;
      this.item2.activate += this.on_item2_activated;
      this.item3.activate += this.on_item3_activated;
      this.item_quit.activate += this.on_item_quit_activated;
    }
    void on_item1_activated ()
    {
      print ("item1 activated\n");
    }
    void on_item2_activated ()
    {
      this.item2.sensitive = false;   // item2を選択できなくする
      this.item3.sensitive = true;    // item3を選択できるようにする
      this.trayicon.blinking = false;  // 点滅状態を解除
      print ("item2 activated\n");
    }
    void on_item3_activated ()
    {
      this.item2.sensitive = true;
      this.item3.sensitive = false;
      this.trayicon.blinking = true;  // 点滅開始
      print ("item3 activated\n");
    }
    void on_item_quit_activated ()
    {
      Gtk.main_quit ();  // メインループ終了
    }
  }

  class ValaSystrayTest
  {
    TrayMenu menu;
    Gtk.StatusIcon icon;
    public void main (string[] args)
    {
      Gtk.init (ref args);  // 初期化
      this.icon = new Gtk.StatusIcon.from_stock (Gtk.STOCK_DIALOG_INFO);
      /* tooltip_text,tooltip_markupはGTK+ 2.16以上が必要・tooltipは利用不可 */
//      this.icon.tooltip_text = "test";  // マウスポインタを少し乗せたときに出る文字列

      //this.icon.tooltip = "test";  // .vapiファイルを修正すれば使用可能
      this.menu = new TrayMenu (this.icon);
      this.icon.popup_menu += this.on_popup;  // ポップアップイベント
      Gtk.main ();  // メインループ開始
    }
    void on_popup (uint button, uint activate_time)
    {
      /*
       * システムトレイアイコンの位置に合わせてメニューをポップアップする
       */
      this.menu.popup (null, null, icon.position_menu, button, activate_time);
    }
  }

  class MainClass
  {
    public static void main (string[] args)
    {
      ValaSystrayTest app = new ValaSystrayTest ();
      app.main (args);
    }
  }
}

(2009/5/10)Gtk.ImageMenuItem.with_label()などのコンストラクタに関する一部コメントの微調整
(2009/5/22)「this」が使用できるところで全て「this」を記述、メッセージ出力関数の微調整

シグナル接続

シグナルの接続に関する操作はGtk#のときに近い書式で「+=」の演算子を用いてハンドラを指定(追加)する。ただし、C#とは違って「new FooHandler ([ハンドラ])」のように指定するのではない点に注意。

/* トレイアイコンの通常クリック時にon_item1_activated()を呼ぶ */
this.trayicon.activate += this.on_item1_activated;
/* 各項目が選択されたときのハンドラを設定 */
this.item1.activate += this.on_item1_activated;
this.item2.activate += this.on_item2_activated;
this.item3.activate += this.on_item3_activated;
this.item_quit.activate += this.on_item_quit_activated;

問題点

システムトレイアイコンのツールチップ指定

Gtk.StatusIconオブジェクトのメンバ変数tooltipを

icon.tooltip = "test";

のように変更しようとしても

error: The name `tooltip' does not exist in the context of `Gtk.StatusIcon'
      icon.tooltip = "test";
      ^^^^^^^^^^^^
Compilation failed: 1 error(s), 0 warning(s)

というエラーになる。調べると代わりに「tooltip_text」あるいは「tooltip_markup」を用いることができることが分かったのだが、これらはバージョン2.16系以上のGTK+を要求してしまう。
これを回避するには、古い名前の(非推奨な)関数での呼び出しができるようにするという方法がある。Valaコード側で

icon.tooltip = "test";

のように記述しておいて.vapiファイルのGtk.StatusIconクラスの中に
ファイル名: vala-0.7.1/vapi/gtk+-2.0.vapi

		public void set_tooltip (string text);
		public string tooltip { get; set; }

を記述するとビルドも通り動作もする。ただ、GTK+ 2.16以上が主流になる頃にはこのようにはせずに「_text」「_markup」付きのバージョンを使用するようにしたほうがよい。

ストックアイコン付きのメニュー項目でGtk.AccelGroupを使用すると選択時に落ちる?

システムトレイのメニューでは意味がないが、「閉じる」のメニュー項目について「Ctrl+Q」を出すためにGtk#のときと同様に

Gtk.AccelGroup group = new AccelGroup ();
this.item_quit = new Gtk.ImageMenuItem.from_stock (Gtk.STOCK_QUIT, group);  // 閉じようとすると落ちる?

と書いたところ、メニューには「Ctrl+Q」が表示されるようになったのだが、これを選択すると落ちてしまった。対処方法は不明なのでこの件に付いては保留とする。

関連記事:

使用したバージョン:

  • Vala 0.7.1
  • GTK+ 2.14.3