クロスプラットフォーム wxWidgets でアプリを作ろう。


開発者なら誰でも夢見るクロスプラットフォーム。
wxWidgetsはそんな夢をかなえてくれるハイテンションなツールキット。
プラットフォーム(OS)毎にラッパーのように機能し、プラットフォームスイートなGUIを実現してくれる。

【参考】

、、、これからMFCを覚えるのも、、そんな感情も抱きながら、、。

0.1ダウンロード


【ダウンロード】

  1. wxWidgets のサイトを訪問
  2. 「Downloads」をクリック
  3. 「Downloads - wxWidgets」のページに移動する。
    私が訪問したとき(2008/03/09)の安定版は 2.8.7 でした。

  4. 「wxMSW」をクリック
  5. 「SourceForge.net」のページに飛んで、ダウンロードが始まります。

ちなみに私は、「(other formats: zip)」の zip版をダウンロードしてみました。

0.2インストール


ダウンロードしたファイルを解凍しライブラリをビルドする。

ちなみに私のビルド環境はVS2005である。

  1. 私は zip版をダウンロードしたので「wxMSW-2.8.7.zip」を解凍し好きなディレクトリに展開する。
  2.  解凍後のディスクに占める容量は106MB
     ファイル数 6,067
     フォルダ数 515
    と少々大きいものとなった。

  3. 「build\msw\wx.dsw」のビルド
  4. ビルド環境がVS2005であるので、wx.dswはじめ各ビルドファイルの保存形式が異なるため更新が求められる。
    すべてYESとし、環境を取り込む。

    以下のようにプロジェクトファイルが再構成された。

    元のファイル名 更新後のファイル名
    wx.dsw wx.sln
    wx_wxregex.dsp wx_wxregex.vcproj
    wx_wxzlib.dsp wx_wxzlib.vcproj
    wx_wxpng.dsp wx_wxpng.vcproj
    wx_wxjpeg.dsp wx_wxjpeg.vcproj
    wx_wxtiff.dsp wx_wxtiff.vcproj
    wx_wxexpat.dsp wx_wxexpat.vcproj
    wx_base.dsp wx_base.vcproj
    wx_net.dsp wx_net.vcproj
    wx_core.dsp wx_core.vcproj
    wx_adv.dsp wx_adv.vcproj
    wx_media.dsp wx_media.vcproj
    wx_odbc.dsp wx_odbc.vcproj
    wx_dbgrid.dsp wx_dbgrid.vcproj
    wx_html.dsp wx_html.vcproj
    wx_qa.dsp wx_qa.vcproj
    wx_xml.dsp wx_xml.vcproj
    wx_xrc.dsp wx_xrc.vcproj
    wx_aui.dsp wx_aui.vcproj
    wx_richtext.dsp wx_richtext.vcproj
    wx_gl.dsp wx_gl.vcproj
  • warning C4819
  • なお、コンパイルに当たっては以下のwarningがいくつか検出されたが、errorはなくビルドできた。

    include\wx/confbase.h : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    
    include\wx/generic/helpext.h : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    
    ..\..\src\generic\dcpsg.cpp(1835) : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    
    ..\..\src\generic\progdlgg.cpp : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    include\wx/generic/progdlgg.h : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    
    include\wx/fileconf.h : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    
    ..\..\src\msw\thread.cpp(545) : warning C4535: _set_se_translator() の呼び出しは /EHa が必要です。
    
    ..\..\src\common\string.cpp(1041) : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    
    ..\..\src\common\regex.cpp : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    include\wx/regex.h : warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
    

    1.1 wxWidgets での「Hello World!」アプリケーション

    つくし

    • 環境
    • Microsoft Visual Studio 2005(Version 8.0.50727.42)
      (MFC使えばっ、て感じかもしれませんが、、)

    • ソリューション構築
      1. 「新しいプロジェクト」のウィザード起動
      2. 「ファイル」-「新規作成」-「プロジェクト」を選択する。

      3. 「空のプロジェクト」の選択
      4. 左ペインの「プロジェクトの種類」で「Visual C++」-「全般」を選択し、右ペインの「テンプレート」の中から「空のプロジェクト」を選択する。

      5. プロジェクト名入力
      6. ダイアログ下方の「プロジェクト名」に「wxHelloWorld」と入力します。

        • 「場所」は「wxMSW-2.8.7」のあるディレクトリにして下さい。
        • 「ソリューションのディレクトリを作成」のチェックをはずしてください。
        • 好みにもよりますが、このチュートリアルでは作成しないこととします。

      7. 「OK」ボタン押下
    • ソースファイルの追加
    • 後述する「wxHelloWorld.cpp」を「wxHelloWorld」のディレクトリに作成し、ソースファイルに追加します。
      この時点で以下のようなディレクトリ構成になるようにして下さい。

      wxHelloWorld/wxHelloWorld.sln
                  /wxHelloWorld.vcproj
                  /wxHelloWorld.cpp
      wxMSW-2.8.7/include
                 /lib
                 .
                 .
      

      (※作業ごとに更新される「*.ncb」、「*.user」、「*.suo」は省略しています。)

    • プロジェクトのプロパティ
    • プロジェクトのプロパティを開き、以下の項目を設定します。

      Debug|Win32 Release|Win32
      構成プロパティ C/C++ 全般 追加のインクルードディレクトリ ..\wxMSW-2.8.7\lib\vc_lib\mswd;..\wxMSW-2.8.7\include ..\wxMSW-2.8.7\lib\vc_lib\msw;..\wxMSW-2.8.7\include
      デバッグ情報の形式 エディット コンティニュ用プログラム データベース (/ZI) 無効(そのまま)
      警告レベル レベル 4 (/W4)
      最適化 最適化 無効 (/Od) 実行速度 (/O2)(そのまま)
      コード生成 ランタイム ライブラリ マルチスレッド デバッグ DLL (/MDd) マルチスレッド DLL (/MD)
      リンカ 全般 追加のライブラリ ディレクトリ ..\wxMSW-2.8.7\lib\vc_lib
      入力 追加の依存ファイル wxbase28d.lib wxmsw28d_core.lib RpcRT4.Lib ComCtl32.Lib wxbase28.lib wxmsw28_core.lib RpcRT4.Lib ComCtl32.Lib
      デバッグ デバッグ情報の生成 はい (/DEBUG)
      (自動的に「プログラム データベース ファイルの生成」の項目に「$(TargetDir)$(TargetName).pdb」が設定されます。)
      いいえ(そのまま)
    • ビルド・実行
    • 以上でプロジェクトの設定は終了です。「wxHelloWorld.cpp」が作成できたら、ビルド、実行してみましょう。

    【補足】
    ※wxbase28d.lib/wxbase28.libのリンクで以下の参照が解決されるようです。

    • wxAppConsoleクラス
    • wxObjectクラス
    • wxEvtHandlerクラス
    • wxStringBaseクラス
    • wxStatusLineNameStr
    • wxOnAssert()
    • wxMenuBaseクラス
    • wxListBaseクラス
    • wxNodeBaseクラス
    • wxBaseArrayPtrVoidクラス
    • wxEventHashTableクラス
    • int const wxEVT_NULL

    ※wxmsw28d_core.lib/wxmsw28_core.libのリンクで以下の参照が解決されるようです。

    • wxFrameクラス
    • wxAppクラス
    • wxAppBaseクラス
    • wxEntry()
    • wxFrameBaseクラス
    • wxMenuBarクラス
    • char const * const wxStatusLineNameStr
    • char const * const wxFrameNameStr
    • wxWindowBaseクラス
    • wxTopLevelWindowBaseクラス
    • wxWindowクラス
    • wxTopLevelWindowMSWクラス
    • wxGetTopLevelParent()
    • wxMenuItemBaseクラス
    • wxMenuクラス
    • wxwxMenuItemListNodeクラス
    • wxMessageBox()
    • int const wxEVT_COMMAND_MENU_SELECTED

    1.2 HelloWorld.cpp

    さくら
    基本的に本家サイトのチュートリアル(英語)「'Hello world' in wxWidgets: A Very Short Tutorial」と同じ内容です。

    Fancyな翻訳、、+α?、という感じです。

    ファイル名を「wxHelloWorld.cpp」とし、以下のコーディングを行います。

    • インクルードファイル
    • ヘッダファイルは個別にインクルードしてもいいんですが、「wx.h」をインクルードすると一通りインクルードしてくれるようです。

      #include "wx/wx.h" 
      
    • アプリケーションクラス
    • wxApp クラスを継承し、HelloWorldアプリケーション用のサブクラス HelloWorldApp を定義する。
      特に、OnInit() (アプリケーションの初期化)をオーバーライド(再定義)します。

      class HelloWorldApp : public wxApp
      {
      	virtual bool OnInit();
      };
      
    • メインウィンドウのクラス
    • wxFrame クラスを継承し、メインウィンドウのクラス MainFrame を定義する。
      ちなみにメインウィンドウには、メニューとステータスバーを持たせることになってます。(後述)
      また、メニューからのイベント受信にイベントテーブルを使用するので、それを宣言するようです。
      DECLARE_EVENT_TABLE() マクロを記述すればいいようです。

      class MainFrame : public wxFrame
      {
      public:
      	MainFrame( const wxString& title, const wxPoint& pos, const wxSize& size);
      	void OnQuit( wxCommandEvent& event );
      	void OnAbout( wxCommandEvent& event );
      
      	DECLARE_EVENT_TABLE()
      };
      
    • イベントコードの定義
    • メニューとして「終了」と「「Hello World!」について」を用意する予定なので、メニューと各ハンドラ関数を繋ぐためのこれらのIDを定義する。

      enum
      {
      	ID_QUIT = 1,	///< 終了
      	ID_ABOUT,	///< ~について
      };
      
    • MainFrame クラスのイベントとハンドラ関数の接続
    • BEGIN_EVENT_TABLE() と END_EVENT_TABLE() のマクロによってイベントテーブルの宣言をするようである。
      また、メニューのイベント定義は EVT_MENU() マクロを使用するようである。

      BEGIN_EVENT_TABLE( MainFrame, wxFrame )
      	EVT_MENU( ID_QUIT, MainFrame::OnQuit )
      	EVT_MENU( ID_ABOUT, MainFrame::OnAbout )
      END_EVENT_TABLE()
      
    • main()関数からアプリケーションインスタンス生成の定義
    • wxWidgets では、IMPLEMENT_APP() マクロによって、main()関数からアプリケーションのインスタンスが生成され、初期化( OnInit() )が行われるらしいです。

      IMPLEMENT_APP( HelloWorldApp )
      

    続く、、、

    1.3 HelloWorld.cppの続き

    さくら
    続きです。

    • HelloWorldApp::OnInit()のインプリメント
    • OnInit() では、メインウィンドウを生成します。
      インスタンス化に成功したら、ウィンドウの表示です。
      また、アプリケーションのトップレベルウィンドウに設定します。

      bool HelloWorldApp::OnInit()
      {
      	MainFrame	*p_frame;
      	bool		result;
      
      	p_frame = new MainFrame( _T("Hello World!"), wxPoint(40,40), wxSize(400,300) );
      
      	if ( p_frame ) {
      		p_frame->Show( TRUE );
      		SetTopWindow( p_frame );
      		result = TRUE;
      	} else {
      		result = FALSE;
      	}
      	return result;
      }
      
    • MainFrame クラスのコンストラクタ
    • MainFrame のコンストラクタでは、メニューとステータスバーの各パーツを設定します。

      MainFrame::MainFrame( const wxString& title, const wxPoint& pos, const wxSize& size )
      	: wxFrame( (wxFrame *)NULL, -1, title, pos, size )
      {
      	wxStatusBar	*p_status_bar;
      	wxMenu		*p_menu_file;
      	wxMenuBar	*p_menu_bar;
      	bool		result;
      
      	result = FALSE;
      
      	p_status_bar = CreateStatusBar();
      
      	p_menu_file = new wxMenu;
      	p_menu_bar = new wxMenuBar;
      	if ( p_menu_file && p_menu_bar ) {
      		p_menu_file->Append( ID_ABOUT, _T("「Hello World!」について(&A)") );
      		p_menu_file->AppendSeparator();
      		p_menu_file->Append( ID_QUIT, _T("閉じる(&X)") );
      
      		p_menu_bar->Append( p_menu_file, _T("ファイル(&F)") );
      		SetMenuBar( p_menu_bar );
      		result = TRUE;
      	}
      
      	if ( p_status_bar ) {
      		if ( result ) {
      			SetStatusText( _T("はぁ、できました、、。") );
      		} else {
      			SetStatusText( _T("あれ?だめでした?") );
      		}
      	}
      }
      
    • 「閉じる」メニューの処理
    • Close() 関数でウィンドウを閉じます。

      void MainFrame::OnQuit( wxCommandEvent& WXUNUSED( event ) )
      {
      	Close(TRUE);
      }
      
    • 「~について」の処理
    • ここではメッセージボックスを出すだけです。
      OKボタンとインフォメーションアイコンを表示するようにしてます。

      void MainFrame::OnAbout( wxCommandEvent& WXUNUSED( event ) )
      {
      	wxMessageBox(_T("Hello World のサンプルです。"),_T("「Hello World」について"), wxOK | wxICON_INFORMATION, this);
      }
      

    wxWidgets/Tips

    さくら
    wxWidgetsを使用する上での小技集です^^

    wxWidgets でコンソールアプリケーション with Visual Studio

    さくら
    Windows向けの wxWidgets は「WinMain()」をエントリポイントとしているようで、
    そのままではコンソールアプリケーション(CUI)を作成できません。
    (wxPrintf()等、stdin/stdout の処理が無効となるようです。)

    Win32 APIの AllocConsole() 等を使用すればコンソールを作成できますが、
    AllocConsole() は wxWidgets 内に実装されておらず、直接呼ぶことになります。
    Win32 APIを直接呼ぶのでは、wxWidgets を使用する意図から外れてしまいます。
    また、コンソールから実行された場合に対応できません。(おそらく、、。)

    以下ではそれを解決します。

    • プロジェクトプロパティの【構成プロパティ】-【リンカ】-【システム】-【サブシステム】の変更
    • プロジェクトのプロパティを開き、【構成プロパティ】-【リンカ】-【システム】-【サブシステム】を以下に変更します。

      コンソール (/SUBSYSTEM:CONSOLE)
      
    • main()関数の実装
    • main()関数を以下の様に実装します。

      int main(int argc,char *argv[])
      {
      	return wxEntry( argc, argv );
      }
      

    なお、【サブシステム】を

    Windows (/SUBSYSTEM:WINDOWS)
    

    にすると、main()関数の実装は無視され、通常のGUIアプリケーションとして機能します。