本家のオンラインマニュアル

GUIの構築と描画

 ここでは,図形や文字を描画するプログラムを例に挙げて, GUIアプリのウィンドウを構築する方法について説明します.


■「Sizer」を用いたGUIウィンドウの設計   ■描画の基礎   ■マウスイベントの扱い

■ 「Sizer」を用いたGUIウィンドウの設計 

 wxWidgetsが提供するウィンドウ構成のための基本的な方法に「Size」を用いる方法があります. このSizerとは何かを説明します.

 基本的にはSizerは,水平方向もしくは垂直方向にGUIの部品を並べるための「枠」だと考えてください.

        
垂直配列の枠
水平配列の枠

 一見すると単純すぎて,たいしたウィンドウ設計ができないような印象すらありますが, これらを組み合わせることで柔軟なウィンドウ設計が確実にできます. 例えばこれから例示するプログラムでは次のようなウィンドウを表示します.

このウィンドウは,メニューバーの下の領域を垂直に3分割し, 分割された最上位のマス目の中を更に2分割してボタンを格納しています.
(下図参照)

(1) SizerにGUIのパーツを
  組み込んでいるところ
  
 
(2) できあがり   

● GUIの構築

 では早速アプリケーション本体(下記)です.

class DrawAppClass: public wxApp {
  public:
    bool OnInit();
};

DECLARE_APP(DrawAppClass)
IMPLEMENT_APP(DrawAppClass)

// Initialization
bool DrawAppClass::OnInit()
{
    AppFrameClass* AppFrame =
        new AppFrameClass(10001, wxT("Draw1"));
    SetTopWindow(AppFrame);
    AppFrame->Show();

    return(true);
}

 今回のアプリケーションのクラスは 'DrawAppClass', ウィンドウのクラスは 'AppFrameClass' です.

 'AppFrameClass' クラスとそのコンストラクタの記述を下に示します.

class AppFrameClass: public wxFrame {
  public:
    AppFrameClass(int id, const wxString& title);
    virtual void On_mQuit(wxCommandEvent &event);
    virtual void On_btn1(wxCommandEvent &event);
    virtual void On_btn2(wxCommandEvent &event);
    
  protected:
    wxMenuBar* menubar;
    wxButton* btn1;
    wxButton* btn2;
    wxTextCtrl* tf1;
    canvasClass* canvas1;
    
    DECLARE_EVENT_TABLE();
};

AppFrameClass::AppFrameClass(int id, const wxString& title)
  :wxFrame(NULL, id, title)
{
    menubar = new wxMenuBar();
    wxMenu* mFile = new wxMenu();
    mFile->Append(10103, wxT("Quit"));
    menubar->Append(mFile, wxT("File"));
    SetMenuBar(menubar);

    btn1 = new wxButton(this, 20001, wxT("button_1"));
    btn2 = new wxButton(this, 20002, wxT("button_2"));

    tf1 = new wxTextCtrl(this, 30001);

    canvas1 = new canvasClass(this,30101);

    SetSize(wxSize(650, 550));

    tf1->SetMinSize(wxSize(320, -1));
    wxBoxSizer* sizer1 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer2 = new wxBoxSizer(wxHORIZONTAL);

    sizer2->Add(btn1, 0, 0, 0);
    sizer2->Add(btn2, 0, 0, 0);
    sizer1->Add(sizer2, 0, wxEXPAND, 0);

    sizer1->Add(tf1, 0, wxEXPAND, 0);

    sizer1->Add(canvas1, 1, wxEXPAND, 0);
    SetSizer(sizer1);
}

ボタンとテキストフィールド

 ボタンのクラスはwxButtonで,そのコンストラクタの呼び出しは次のようなものです.
  wxButton(取り付け先のウィンドウ, ボタンのwxID, ボタンのラベル)

 テキストフィールドのクラスはwxTextCtrlで,そのコンストラクタの呼び出しは次のようなものです.
  wxTextCtrl(取り付け先のウィンドウ, テキストフィールドのwxID)

Sizer

 Sizerの基本的なクラスの1つが 'wxBoxSizer' で, コンストラクタの引数に 'wxVERTICAL' を与えると垂直配列型, 'wxHORIZONTAL' を与えると水平配列型になります.

 SizerにGUIパーツを配置(追加)するには 'Add'(下記)を使います.
  Add(GUIパーツ, 大きさの比率, 配置のしかた, ボーダーの設定)

 上の例には,ボタンを配置する水平配列のsizer2, ウィンドウ全体の配置を決める垂直配列のsizer1があります.sizer2にボタンbtn1,btn2が順次配置され, それをsizer1に追加しています.同様にテキストフィールドtf1と, 描画用オブジェクトcanvas1(後述)をsizer1に追加し,それを 'SetSizer(sizer1)' でウィンドウに設置しています.

[ページトップへ]


■ 描画の基礎

 wxWidgetsではウィンドウオブジェクトに対して描画しますが, 一般的な方法として 'wxScrolledWindow' クラスのオブジェクトに対する描画について説明します. 特に基本的な描画として四角形,線分,折れ線,文字列,画像ファイルの表示方法を取り上げます.

 今回は描画用オブジェクトのクラスとして,'wxScrolledWindow' クラスの派生クラス 'canvasClass' を定義します.(下記)

class canvasClass : public wxScrolledWindow {
  public:
    // constructor
    canvasClass(AppFrameClass *parent,wxWindowID id);
    
    // event handler
    virtual void On_paint(wxPaintEvent & event);
    
  private:
    AppFrameClass *owner;
    wxImage bmp;
    wxBitmap bitmap;
    DECLARE_EVENT_TABLE();
};

canvasClass::canvasClass(AppFrameClass *parent, wxWindowID id)
  :wxScrolledWindow(parent,id,wxDefaultPosition, wxDefaultSize,
   wxHSCROLL | wxVSCROLL | wxNO_FULL_REPAINT_ON_RESIZE) {

    owner = parent;

    // canvas background color
    SetBackgroundColour(wxColour(255,200,200));

    // reading image file
    bmp = wxImage( wxString("Draw1.bmp"), wxBITMAP_TYPE_BMP, -1);
    bitmap = wxBitmap(bmp);
}

 今回はこのクラスのインスタンス 'canvasClass' をウィンドウのSizerの中に設置して, それに対して描画します.

● 画像データの扱い

 上の例の中にあるように,画像ファイルなどの画像データは 'wxImage' クラスのオブジェクトとして扱います.このクラスのコンストラクタに読み込むファイル名, ファイルの形式を指定することで画像ファイル(上の例では'Draw1.bmp')を読み込むことができます. プログラム中で扱えるビットマップデータは 'wxBitmap' クラスのオブジェクトとして扱うので, wxImageクラスのオブジェクトはwxBitmapクラスのオブジェクトに変換する必要があり, 上の例ではwxBitmapのコンストラクタの引数にwxImageのオブジェクトを与えることで変換しています.

 
画像ファイル"Draw1.bmp"(461KB)

● 描画のタイミング

 wxWidgetsを用いたアプリが実際に描画を実行するタイミングは, 描画対象が実際にディスプレイに現れた瞬間か,もしくはウィンドウのサイズ変更などが実行された瞬間です. それらの瞬間にEVT_PAINTというイベントが発生し, そのイベントを受けたイベントハンドラが描画を実行することになります.次の例を見てください.

BEGIN_EVENT_TABLE(canvasClass,wxScrolledWindow)
    EVT_PAINT(canvasClass::On_paint)
END_EVENT_TABLE();

//--- Drawing ---
void canvasClass::On_paint(wxPaintEvent & event) {
    wxPaintDC	pdc(this);

    //--- pen & brush ---
    wxPen		*pen;
    wxBrush		*brush;
    wxFont		*font;

    //--- clearing ---
    pdc.Clear();

    //--- drawing ---

    // rectangle
    pen = new wxPen( wxColor(0,0,0), 8, wxPENSTYLE_SOLID );
    pdc.SetPen(*pen);
    brush = new wxBrush( wxColor(200,255,200), wxBRUSHSTYLE_SOLID );
    pdc.SetBrush(*brush);
    pdc.DrawRectangle( wxPoint(10,10), wxSize(500,400));

    // line
    pen = new wxPen( wxColor(0,0,255), 4, wxPENSTYLE_SOLID );
    pdc.SetPen(*pen);
    pdc.DrawLine( wxPoint(20,20),wxPoint(100,100) );

    // lines
    wxPoint		p[4];
    pen = new wxPen( wxColor(255,0,0), 8, wxPENSTYLE_SOLID );
    pdc.SetPen(*pen);
    p[0].x = 20;	p[0].y = 20;
    p[1].x = 100;	p[1].y = 20;
    p[2].x = 100;	p[2].y = 100;
    p[3].x = 20;	p[3].y = 100;
    pdc.DrawLines(4,p,0,0);

    // text
    wxString	s;
    s = wxString("24 point Serif with italic bold");
    font = new wxFont(24,wxFONTFAMILY_ROMAN,
        wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
    pdc.SetFont(*font);
    pdc.DrawText(s,20,110);

    s = wxString("48 Sans-Serif bold");
    font = new wxFont(48,wxFONTFAMILY_SWISS,
        wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false);
    pdc.SetFont(*font);
    pdc.SetTextForeground(wxColor(0,255,0));
    pdc.SetTextBackground(wxColor(255,255,255));
    pdc.DrawText(s,20,140);

    // image
    pdc.DrawBitmap(bitmap,520,420,false);
}

 今回は 'On_paint' という関数がEVT_PAINTイベントを受けて描画を実行するハンドラで, canvasClassに対してイベントハンドリングを登録しています.

●「コンテキスト」:描画の対象

 今回はcanvasClassのオブジェクトに描画するわけですが, 描画対象のオブジェクトにはコンテキストという要素があり, 実際の描画はこのコンテキストに対して行います.上の例の中に
  wxPaintDC pdc(this);
という部分がありますが,「そのキャンバスのコンテキストを'pdc'として取得する」ことを意味しています. 以後,このpdcに対して色などの属性を設定しながら,pdcに対して図形や文字を描画することになります.

● 描画に必要な要素 (ペン,ブラシ,フォント)

 図形描画の基本要素は大雑把に言うと次の3つです,
  ・線の属性(線種,太さ,色など)
  ・塗りの属性(色,パターンなど)
  ・フォントの属性(書体,大きさ,装飾など)
つまり,これらの属性を設定した後で図形や文字を描画することになります.
wxWidgetsでもこれらの属性を保持するための仕組みが用意されています.

wxPen:線の属性を管理するためのしくみ(クラス)

 線画を描画する際の線の属性(太さ,線種,色など)はwxPenクラスのオブジェクトに設定して, それをコンテキストに対して適用します.上の例の中では
  wxPen *pen;
としてpenというオブジェクトを宣言し,
  pen = new wxPen( wxColor(0,0,0), 8, wxPENSTYLE_SOLID );
という部分でオブジェクト本体を生成して属性を設定しています. これは"太さ8ポイント,色は黒,線種は実践"という設定を意味しています.そして
  pdc.SetPen(*pen);
という部分でコンテキストpdcに適用しています. この後で描画する線画の線の属性はこの設定に従います.

wxBrush:塗りの属性を管理するためのしくみ(クラス)

 塗りの属性(色,パターンなど)はwxBrushクラスのオブジェクトに設定して, それをコンテキストに対して適用します.上の例の中では
  wxBrush *brush;
としてbrushというオブジェクトを宣言し,
  brush = new wxBrush( wxColor(200,255,200), wxBRUSHSTYLE_SOLID );
という部分でオブジェクト本体を生成して属性を設定しています. これは"色は薄い緑,パターンはベタ塗り"という設定を意味しています.そして
  pdc.SetBrush(*brush);
という部分でコンテキストpdcに適用しています. この後で描画する線画の塗りの属性はこの設定に従います.

wxFont:フォントに関する属性を管理するためのしくみ(クラス)

 フォントに関する属性(書体,大きさ,装飾など)はwxFontクラスのオブジェクトに設定して, それをコンテキストに対して適用します.上の例の中では
  wxFont *font;
としてfontというオブジェクトを宣言し,
  font = new wxFont(24,wxFONTFAMILY_ROMAN,
    wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
という部分でオブジェクト本体を生成して属性を設定しています. これは"書体は明朝,大きさは24ポイント,装飾は斜体で強調"という設定を意味しています.そして
  pdc.SetFont(*font);
という部分でコンテキストpdcに適用しています. この後で描画する文字の属性はこの設定に従います.

● 線画や文字の描画

 上の例では四角形,線分,折れ線,文字列を描画しています.

四角形の描画

 上の例の中に
  pdc.DrawRectangle(wxPoint(10,10), wxSize(500,400));
とありますが,これによって 「開始位置(10,10)に大きさ(500,400)の四角形」をコンテキストpdcに対して描画しています.

線分の描画

 上の例の中に
  pdc.DrawLine( wxPoint(20,20),wxPoint(100,100) );
とありますが,これによって 「始点(10,10)から終点(100,100)の線分」をコンテキストpdcに対して描画しています.

折れ線の描画

 上の例の中に
  wxPoint p[4];
そして,
  p[0].x = 20;  p[0].y = 20;
  p[1].x = 100;  p[1].y = 20;
  p[2].x = 100;  p[2].y = 100;
  p[3].x = 20;  p[3].y = 100;
  pdc.DrawLines(4,p,0,0);
とありますが,これによって 「点列の配列pが示す折れ線」をコンテキストpdcに対して描画しています.

文字の描画

 上の例の中に
  wxString s;
  s = wxString("24 point Serif with italic bold");
そして,
  pdc.DrawText(s,20,110);
とありますが,これによって 「文字列"24 point Serif with italic bold"」 をコンテキストpdcの位置(20,110)に対して描画しています.

 下にプログラム全体を示します.

プログラム全体:Draw1.cpp
#include	 <wx/wx.h>
//-------------------------------------------------------
// Class Definition : Top Window Frame
//-------------------------------------------------------
class canvasClass;

class AppFrameClass: public wxFrame {
  public:
    AppFrameClass(int id, const wxString& title);
    virtual void On_mQuit(wxCommandEvent &event);
    virtual void On_btn1(wxCommandEvent &event);
    virtual void On_btn2(wxCommandEvent &event);

  protected:
    wxMenuBar* menubar;
    wxButton* btn1;
    wxButton* btn2;
    wxTextCtrl* tf1;
    canvasClass* canvas1;
    
    DECLARE_EVENT_TABLE();
};

//-------------------------------------------------------
// Canvas Class Definition
//-------------------------------------------------------
class canvasClass : public wxScrolledWindow {
  public:
    // constructor
    canvasClass(AppFrameClass *parent,wxWindowID id);
    
    // event handler
    virtual void On_paint(wxPaintEvent & event);
    
  private:
    AppFrameClass *owner;
    wxImage bmp;
    wxBitmap bitmap;
    DECLARE_EVENT_TABLE();
};

//-------------------------------------------------------
// Constructor of Application Window Frame
//-------------------------------------------------------
AppFrameClass::AppFrameClass(int id, const wxString& title)
  :wxFrame(NULL, id, title)
{
    menubar = new wxMenuBar();
    wxMenu* mFile = new wxMenu();
    mFile->Append(10103, wxT("Quit"));
    menubar->Append(mFile, wxT("File"));
    SetMenuBar(menubar);

    btn1 = new wxButton(this, 20001, wxT("button_1"));
    btn2 = new wxButton(this, 20002, wxT("button_2"));

    tf1 = new wxTextCtrl(this, 30001);

    canvas1 = new canvasClass(this,30101);

    SetSize(wxSize(650, 550));

    tf1->SetMinSize(wxSize(320, -1));
    wxBoxSizer* sizer1 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* sizer2 = new wxBoxSizer(wxHORIZONTAL);

    sizer2->Add(btn1, 0, 0, 0);
    sizer2->Add(btn2, 0, 0, 0);
    sizer1->Add(sizer2, 0, 0, 0);

    sizer1->Add(tf1, 0, wxEXPAND, 0);

    sizer1->Add(canvas1, 1, wxEXPAND, 0);
    SetSizer(sizer1);
}

//-------------------------------------------------------
// Event Handling for Application Window Frame
//-------------------------------------------------------

// For Application Window Frame
BEGIN_EVENT_TABLE(AppFrameClass, wxFrame)
    EVT_MENU(10103, AppFrameClass::On_mQuit)
    EVT_BUTTON(20001, AppFrameClass::On_btn1)
    EVT_BUTTON(20002, AppFrameClass::On_btn2)
END_EVENT_TABLE();

void AppFrameClass::On_mQuit(wxCommandEvent &event)
{
    Close();
}

void AppFrameClass::On_btn1(wxCommandEvent &event)
{
    tf1->SetValue(wxT("button_1 is pressed."));
}

void AppFrameClass::On_btn2(wxCommandEvent &event)
{
    tf1->SetValue(wxT("button_2 が押されました."));
}

//-------------------------------------------------------
// Constructor of Paint Canvas
//-------------------------------------------------------
canvasClass::canvasClass(AppFrameClass *parent, wxWindowID id)
  :wxScrolledWindow(parent,id,wxDefaultPosition, wxDefaultSize,
   wxHSCROLL | wxVSCROLL | wxNO_FULL_REPAINT_ON_RESIZE) {

    owner = parent;

    // canvas background color
    SetBackgroundColour(wxColour(255,200,200));

    // reading image file
    bmp = wxImage( wxString("Draw1.bmp"), wxBITMAP_TYPE_BMP, -1);
    bitmap = wxBitmap(bmp);
}

//-------------------------------------------------------
// Event Handling for Application Window Frame
//-------------------------------------------------------
BEGIN_EVENT_TABLE(canvasClass,wxScrolledWindow)
    EVT_PAINT(canvasClass::On_paint)
END_EVENT_TABLE();

//--- Drawing ---
void canvasClass::On_paint(wxPaintEvent & event) {
    wxPaintDC	pdc(this);

    //--- pen & brush ---
    wxPen		*pen;
    wxBrush		*brush;
    wxFont		*font;

    //--- clearing ---
    pdc.Clear();

    //--- drawing ---

    // rectangle
    pen = new wxPen( wxColor(0,0,0), 8, wxPENSTYLE_SOLID );
    pdc.SetPen(*pen);
    brush = new wxBrush( wxColor(200,255,200), wxBRUSHSTYLE_SOLID );
    pdc.SetBrush(*brush);
    pdc.DrawRectangle( wxPoint(10,10), wxSize(500,400));

    // line
    pen = new wxPen( wxColor(0,0,255), 4, wxPENSTYLE_SOLID );
    pdc.SetPen(*pen);
    pdc.DrawLine( wxPoint(20,20),wxPoint(100,100) );

    // lines
    wxPoint		p[4];
    pen = new wxPen( wxColor(255,0,0), 8, wxPENSTYLE_SOLID );
    pdc.SetPen(*pen);
    p[0].x = 20;	p[0].y = 20;
    p[1].x = 100;	p[1].y = 20;
    p[2].x = 100;	p[2].y = 100;
    p[3].x = 20;	p[3].y = 100;
    pdc.DrawLines(4,p,0,0);

    // text
    wxString	s;
    s = wxString("24 point Serif with italic bold");
    font = new wxFont(24,wxFONTFAMILY_ROMAN,
        wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false);
    pdc.SetFont(*font);
    pdc.DrawText(s,20,110);

    s = wxString("48 Sans-Serif bold");
    font = new wxFont(48,wxFONTFAMILY_SWISS,
        wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false);
    pdc.SetFont(*font);
    pdc.SetTextForeground(wxColor(0,255,0));
    pdc.SetTextBackground(wxColor(255,255,255));
    pdc.DrawText(s,20,140);

    // image
    pdc.DrawBitmap(bitmap,520,420,false);
}

//-------------------------------------------------------
// Application Construction
//-------------------------------------------------------

// Application Class
class DrawAppClass: public wxApp {
  public:
    bool OnInit();
};

DECLARE_APP(DrawAppClass)
IMPLEMENT_APP(DrawAppClass)

// Initialization
bool DrawAppClass::OnInit()
{
    AppFrameClass* AppFrame =
        new AppFrameClass(10001, wxT("Draw1"));
    SetTopWindow(AppFrame);
    AppFrame->Show();

    return(true);
}

 これを実行した様子を下に示します.

         
Macintoshで実行した様子
Windowsで実行した様子

(参考:様々なコンテキスト)

 wxWidgetsでは,描画処理はコンテキストに対して行うというのが基本的な考え方です. このコンテキストですが,今回はwxPaintDCというクラスのものを使いましたが,この他にも, ビットマップ画像オブジェクト(wxBitmapなど)に対して描画処理を施すためのwxMemoryDCや, プリンターデバイスに対して描画処理(印刷処理)をするためのwxMemoryDC, wxPostScriptDCといったものもあります.詳しくは折を見て説明することにします.

[ページトップへ]


■ マウスイベントの扱い

 マウスのドラッグでウィンドウに絵を書くプログラムを例に挙げ, マウスイベントの取り扱いについて説明します.

今回のプログラム:

 描画用のキャンバスとなるウィンドウオブジェクト(canvasClass *canvas) をアプリのメインウィンドウに埋め込んで,その上のマウスがドラッグしたら,軌跡を描きます.ただし, 軌跡はcanvasに直接描くのではなく,ビットマップオブジェクト(wxBitmap bitmap)に描いたものを, canvasに描く手順にしています.

 マウスの移動やボタン操作のイベントハンドリングは,canvasに登録する形にします.

● ビットマップオブジェクトの生成

 ビットマップ画像は 'wxBitmap' クラスのオブジェクトとして扱います.例えば

    wxBitmap bitmap;
    bitmap = wxBitmap(600, 500, wxBITMAP_SCREEN_DEPTH);

とすると600✕500のサイズのビットマップが作成されます.

● ビットマップオブジェクトへの描画

 ビットマップオブジェクトへの描画もやはりコンテキストを使用します. この場合は 'wxMemoryDC' というコンテキストをビットマップオブジェクトから取得します.

 では早速プログラムを見てみましょう.(下記:'Draw2.cpp')

プログラム全体:Draw2.cpp
#include <wx/wx.h>

class appFrameClass;
//---------------------------------------------------------------
// Class for Drawing Canvas
//---------------------------------------------------------------
class canvasClass : public wxScrolledWindow {
  public:
    // constructor
    canvasClass(wxWindow *parent, wxWindowID id);

    // for paint
    virtual void rePaint();
    virtual void traceLine(int x1,int y1,int x2,int y2);

    // event handler
    virtual void On_paint(wxPaintEvent & event);
    virtual void On_mouseUp(wxMouseEvent & event);
    virtual void On_mouseDown(wxMouseEvent & event);
    virtual void On_mouseMove(wxMouseEvent & event);

  private:
    // parent window
    wxWindow *owner;

    // for mouse
    int mx, my;
    bool mouseLeft;

    // bitmap object for drawing
    wxBitmap bitmap;

    DECLARE_EVENT_TABLE();
};

// constructor
canvasClass::canvasClass(wxWindow *parent, wxWindowID id)
  :wxScrolledWindow(parent,id,wxDefaultPosition, wxDefaultSize,
   wxHSCROLL | wxVSCROLL | wxNO_FULL_REPAINT_ON_RESIZE) {

    owner = parent;

    // generate bitmap object
    bitmap = wxBitmap(600, 500, wxBITMAP_SCREEN_DEPTH);

    wxMemoryDC	dc(bitmap);
    dc.Clear();

    // mouse button
    mouseLeft = false;
}


//---------------------------------------------------------------
// Class for Application Window Frame
//---------------------------------------------------------------
class appFrameClass: public wxFrame {
  public:
    appFrameClass(int id, const wxString& title);

    virtual void On_mQuit(wxCommandEvent &event);
    
  private:
    wxMenuBar* menubar;
    canvasClass* canvas;
    
    DECLARE_EVENT_TABLE();
};


appFrameClass::appFrameClass(int id, const wxString& title)
  :wxFrame(NULL, id, title) {
    menubar = new wxMenuBar();
    wxMenu* mFile = new wxMenu();
    mFile->Append(10101, wxT("Quit"));
    menubar->Append(mFile, wxT("File"));
    SetMenuBar(menubar);
    canvas = new canvasClass(this, 20001);

    SetTitle(title);
    SetSize(wxSize(605, 520));
    canvas->SetScrollRate(10, 10);
    wxBoxSizer* sizer1 = new wxBoxSizer(wxHORIZONTAL);
    sizer1->Add(canvas, 1, wxEXPAND, 0);
    SetSizer(sizer1);
    Layout();
}


//---------------------------------------------------------------
// Event Handling for Application Window Frame
//---------------------------------------------------------------
BEGIN_EVENT_TABLE(appFrameClass, wxFrame)
    EVT_MENU(10101, appFrameClass::On_mQuit)
END_EVENT_TABLE();

void appFrameClass::On_mQuit(wxCommandEvent &event)
{
    event.Skip();
    Close();
}


//---------------------------------------------------------------
// Event Handling for canvasClass
//---------------------------------------------------------------
BEGIN_EVENT_TABLE(canvasClass,wxScrolledWindow)
    EVT_PAINT(canvasClass::On_paint)
    EVT_LEFT_DOWN(canvasClass::On_mouseDown)
    EVT_LEFT_UP(canvasClass::On_mouseUp)
    EVT_MOTION(canvasClass::On_mouseMove)
END_EVENT_TABLE();


//--- Mouse Action ---
void canvasClass::On_mouseMove(wxMouseEvent & event) {
    int		x, y;

    x = event.GetX();
    y = event.GetY();
    if ( mouseLeft ) {
        traceLine(mx,my,x,y);
        rePaint();
    }
    mx = x;
    my = y;

}

void canvasClass::traceLine(int x1, int y1, int x2, int y2) {
    wxMemoryDC	dc(this->bitmap);
    dc.DrawLine(wxPoint(x1,y1),wxPoint(x2,y2));
}

void canvasClass::On_mouseDown(wxMouseEvent & event) {
    event.Skip();

    mouseLeft = true;
    mx = event.GetX();
    my = event.GetY();
}

void canvasClass::On_mouseUp(wxMouseEvent & event) {
    event.Skip();

    mouseLeft = false;
    mx = event.GetX();
    my = event.GetY();
}


//--- Drawing ---
void canvasClass::On_paint(wxPaintEvent & event) {
    event.Skip();

    rePaint();
}

void canvasClass::rePaint() {
    wxClientDC	cdc(this);

    cdc.Clear();
    cdc.DrawBitmap(bitmap,0,0,false);
}

//---------------------------------------------------------------
// Building Application
//---------------------------------------------------------------
class appClass: public wxApp {
  public:
    appFrameClass* appFrame;
    bool OnInit();
};

IMPLEMENT_APP(appClass)

bool appClass::OnInit() {
    appFrame = new appFrameClass(10001,wxT("Draw2"));
    SetTopWindow(appFrame);
    appFrame->Show();
    return true;
}

マウスのためのイベントハンドリング

 このプログラム中の下記の部分がマウスイベントを登録している部分です.

BEGIN_EVENT_TABLE(canvasClass,wxScrolledWindow)
    EVT_PAINT(canvasClass::On_paint)
    EVT_LEFT_DOWN(canvasClass::On_mouseDown)
    EVT_LEFT_UP(canvasClass::On_mouseUp)
    EVT_MOTION(canvasClass::On_mouseMove)
END_EVENT_TABLE();

・ポイント
 ビットマップオブジェクトのコンテキストdcを使って軌跡を描き, 今度はアプリのウィンドウのコンテキストcdcを使って出来上がったビットマップを描画しています.
 この描画処理は,マウスが動く度,ウィンドウがリサイズする度に実行されます.

 wxWidgetsの公式サンプルプログラムでは, ウィンドウのコンテキストはwxClientDCのオブジェクトとして取得することがよくあるようですので, このプログラムもそれにならいました.

 実行した例を下に示します.

         
Macintoshで実行した様子
Windowsで実行した様子


補足:

 表示が素早く切り替わるようなケース(例えばゲームなど)には, ここで紹介した描画方法は向きません.(表示の遅れやチラツキが起こります)
リアルタイム性のある描画処理に関してはまた改めて説明したいと思います.

 このページで紹介した内容は,wxWidgetsが提供するGUIのうち最も初歩的なものです. wxWidgetsは更に高機能で多彩なGUI構築機能を提供しており,全てを紹介しつくすに至っておりません. 当サイトでは時間をかけて「ぼちぼち」と紹介してゆこうと考えています.

[ページトップへ]


2014/08/25