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

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

Gtk#でSystem.Drawing.Graphicsクラスの描画処理を用いる

GTK+とその多くの言語バインディング(例:PyGTK)では、描画エリア(GtkDrawingArea)への描画処理を記述する場合にはGTK+の下位ライブラリのGDKもしくは高い表現力を持つCairoライブラリを用いることになる(関連記事:前半 後半)。
しかし、Mono/.NET向けのGtk#では、Monoの機能を用いてWindows Formsで行うようなSystem.Drawing.Graphicsクラスによる描画処理をGTK+上で用いることができる。動作速度はあまり速くない印象があるが、「System.Drawing.Graphicsクラスによる描画処理には慣れているが、GTK+の描画には慣れていない」「同じアプリケーションについてWindows Forms版とGtk#版と用意してOSごとに見栄えを合わせつつ、描画処理は同じように行いたい」などの場合には役に立つかもしれない。

手順

  1. Gtk.DrawingAreaもしくはこれを継承したクラスのオブジェクトを作成
  2. 1のオブジェクトのSizeAllocatedイベントのハンドラの2番目の引数(SizeAllocatedArgs)のメンバAllocation.WidthとAllocation.Heightでサイズ変更時の新しい幅と高さを取得するようにする
  3. 1のオブジェクトのExposeEventイベントのハンドラの中で2番目の引数(ExposeEventArgs)のメンバEvent.WindowをGtk.DotNet.Graphics.FromDrawable()に渡してSystem.Drawing.Graphicsオブジェクトを得る・その際に、得たオブジェクトを確実にハンドラの最後で破棄(Dispose)するために、using構文を用いる
  4. System.Drawing.Graphicsオブジェクトのメンバ関数で各種描画処理を記述


[任意]ファイル名: gtkdotnetgraphicstest.cs エンコーディング: UTF-8

/*
 * Gtk#でSystem.Drawing.Graphicsクラスによる描画を用いる
 *
 * gmcs -out:gtkdotnetgraphicstest.exe gtkdotnetgraphicstest.cs -pkg:gtk-dotnet-2.0 -reference:System.Drawing
 *
 * MonoDevelopでの参照アセンブリは
 *  gtk-dotnet
 *  System.Drawing
 */

using System.Drawing;
using System;
using Gtk;

namespace GtkDotNetGraphicsTest
{
  class MyDrawingArea : Gtk.DrawingArea
  {
    int width;
    int height;
    public MyDrawingArea ()
    {
      // サイズが変更された
      this.SizeAllocated += delegate (object o, SizeAllocatedArgs args)
      {
        this.width = args.Allocation.Width;
        this.height = args.Allocation.Height;
      };
      // DrawingAreaへの描画処理
      this.ExposeEvent += delegate (object o, ExposeEventArgs args)
      {
        using (System.Drawing.Graphics g = Gtk.DotNet.Graphics.FromDrawable (args.Event.Window))
        {
          // ここにSystem.Drawing.Graphicsの描画処理を記述
          // Windows Formsにおける描画処理と同じように記述できる
          //
          // PenやStringFormat,Fontといったオブジェクトはここですぐ不要になるので
          // using構文を用いる
          using (Pen p1 = new Pen (Color.Blue, 10.0f))
          using (Pen p2 = new Pen (Color.Green, 10.0f))
          using (StringFormat sf = new StringFormat ())
          using (Font font = new Font ("Serif", Math.Min (this.width, this.height) / 4.0f))
          {
            // 文字を中央寄せするための設定
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;
            // 線を描画
            g.DrawLine (p1, 0, 0, this.width, this.height);  // 左上 - 右下
            g.DrawLine (p2, 0, this.height, this.width, 0);  // 左下 - 右上
            // 文字列を描画
            g.DrawString ("テスト",
                          font,
                          Brushes.Red,
                          new PointF (this.width / 2, this.height / 2),  // 構造体
                          sf);
          }
        }
        // GObjectシグナルに対するハンドラで戻り値をとるものは
        // RetValというメンバにそれを代入し、この関数自体は戻り値をとらない
        args.RetVal = false;
      };
    }
  }
  class MainWindow : Gtk.Window
  {
    Gtk.AccelGroup accelgroup;
    Gtk.ImageMenuItem item_quit;
    Gtk.MenuItem item_file;
    Gtk.Menu menu_file;
    Gtk.MenuBar menubar;
    MyDrawingArea da;
    Gtk.VBox vbox;
    public MainWindow () : base (Gtk.WindowType.Toplevel)
    {
      this.accelgroup = new Gtk.AccelGroup ();
      this.AddAccelGroup (this.accelgroup);
      this.item_quit = new Gtk.ImageMenuItem (Gtk.Stock.Quit, this.accelgroup);
      this.menu_file = new Gtk.Menu ();
      this.menu_file.Add (this.item_quit);
      this.item_file = new Gtk.MenuItem ("_File");
      this.item_file.Submenu = this.menu_file;
      this.menubar = new Gtk.MenuBar ();
      this.menubar.Append (this.item_file);
      this.da = new MyDrawingArea ();
      this.vbox = new Gtk.VBox ();
      this.vbox.PackStart (this.menubar, false, false, 0);
      this.vbox.PackStart (this.da);
      this.Add (this.vbox);
      this.SetSizeRequest (200, 150);
      this.Resize (300, 200);
      // 「閉じる」ボタンが押されたときの処理
      this.DeleteEvent += delegate (object o, DeleteEventArgs args)
      {
        Application.Quit ();
        args.RetVal = false;  // trueかつApplication.Quit()を呼ばないと閉じなくできる
      };
      // メニューの「終了」が選択されたときの処理
      this.item_quit.Activated += delegate (object sender, EventArgs e)
      {
        Application.Quit ();
      };
    }
  }
  class MainClass
  {
    public static int Main (string[] args)
    {
      Application.Init ();
      MainWindow win = new MainWindow ();
      win.ShowAll ();
      Application.Run ();
      return 0;
    }
  }
}

下はペンなどの再利用可能なオブジェクトを描画エリアのクラスのメンバとして再利用するようにしたもの。
[任意]ファイル名: gtkdotnetgraphicstest2.cs エンコーディング: UTF-8

/*
 * Gtk#でSystem.Drawing.Graphicsクラスによる描画を用いる
 *
 * gmcs -out:gtkdotnetgraphicstest2.exe gtkdotnetgraphicstest2.cs -pkg:gtk-dotnet-2.0 -reference:System.Drawing
 *
 * MonoDevelopでの参照アセンブリは
 *  gtk-dotnet
 *  System.Drawing
 */

using System.Drawing;
using System;
using Gtk;

namespace GtkDotNetGraphicsTest
{
  class MyDrawingArea : Gtk.DrawingArea
  {
    int width;
    int height;
    Pen p1;
    Pen p2;
    StringFormat sf;
    public MyDrawingArea ()
    {
      this.p1 = new Pen (Color.Blue, 10.0f);
      this.p2 = new Pen (Color.Green, 10.0f);
      // 文字を中央寄せするための設定
      this.sf = new StringFormat ();
      this.sf.Alignment = StringAlignment.Center;
      this.sf.LineAlignment = StringAlignment.Center;
      // サイズが変更された
      this.SizeAllocated += delegate (object o, SizeAllocatedArgs args)
      {
        this.width = args.Allocation.Width;
        this.height = args.Allocation.Height;
      };
      // DrawingAreaへの描画処理
      this.ExposeEvent += delegate (object o, ExposeEventArgs args)
      {
        using (System.Drawing.Graphics g = Gtk.DotNet.Graphics.FromDrawable (args.Event.Window))
        {
          // ここにSystem.Drawing.Graphicsの描画処理を記述
          // Windows Formsにおける描画処理と同じように記述できる
          //
          // サイズによって調整する部分でusing構文を用いる
          using (Font font = new Font ("Serif", Math.Min (this.width, this.height) / 4.0f))
          {
            // 線を描画
            g.DrawLine (this.p1, 0, 0, this.width, this.height);  // 左上 - 右下
            g.DrawLine (this.p2, 0, this.height, this.width, 0);  // 左下 - 右上
            // 文字列を描画
            g.DrawString ("テスト",
                          font,
                          Brushes.Red,
                          new PointF (this.width / 2, this.height / 2),  // 構造体
                          this.sf);
          }
        }
        // GObjectシグナルに対するハンドラで戻り値をとるものは
        // RetValというメンバにそれを代入し、この関数自体は戻り値をとらない
        args.RetVal = false;
      };
    }
  }
  class MainWindow : Gtk.Window
  {
    Gtk.AccelGroup accelgroup;
    Gtk.ImageMenuItem item_quit;
    Gtk.MenuItem item_file;
    Gtk.Menu menu_file;
    Gtk.MenuBar menubar;
    MyDrawingArea da;
    Gtk.VBox vbox;
    public MainWindow () : base (Gtk.WindowType.Toplevel)
    {
      this.accelgroup = new Gtk.AccelGroup ();
      this.AddAccelGroup (this.accelgroup);
      this.item_quit = new Gtk.ImageMenuItem (Gtk.Stock.Quit, this.accelgroup);
      this.menu_file = new Gtk.Menu ();
      this.menu_file.Add (this.item_quit);
      this.item_file = new Gtk.MenuItem ("_File");
      this.item_file.Submenu = this.menu_file;
      this.menubar = new Gtk.MenuBar ();
      this.menubar.Append (this.item_file);
      this.da = new MyDrawingArea ();
      this.vbox = new Gtk.VBox ();
      this.vbox.PackStart (this.menubar, false, false, 0);
      this.vbox.PackStart (this.da);
      this.Add (this.vbox);
      this.SetSizeRequest (200, 150);
      this.Resize (300, 200);
      // 「閉じる」ボタンが押されたときの処理
      this.DeleteEvent += delegate (object o, DeleteEventArgs args)
      {
        Application.Quit ();
        args.RetVal = false;  // trueかつApplication.Quit()を呼ばないと閉じなくできる
      };
      // メニューの「終了」が選択されたときの処理
      this.item_quit.Activated += delegate (object sender, EventArgs e)
      {
        Application.Quit ();
      };
    }
  }
  class MainClass
  {
    public static int Main (string[] args)
    {
      Application.Init ();
      MainWindow win = new MainWindow ();
      win.ShowAll ();
      Application.Run ();
      return 0;
    }
  }
}

関連記事:

使用したバージョン:

  • Gtk# 2.12.9, 2.12.10
  • Mono 2.4.2.3, 2.6.4