wxWidgets 3.0 以降を使った最小の GUI プログラムを例示し、その解説をする。
コード
本格的なアプリケーションを書くならクラスごとにヘッダーファイルとソースファイルを用意するところだが、今回は簡便さのため、1つのソースファイル (app.cpp
) に全て記述してしまう。
#include <wx/wx.h>
// アプリケーションについての挙動を管理するシングルトンクラス。
// 具体的には、初期化処理、メインループの管理など。
class MyApp : public wxApp
{
virtual bool OnInit() override;
};
// wxGetApp 関数を宣言する(後述)
wxDECLARE_APP(MyApp);
// メインウィンドウを表すクラス。
class MyFrame : public wxFrame
{
public:
MyFrame();
virtual ~MyFrame();
void OnClose(wxCloseEvent& event);
};
// アプリケーションの初期化処理。
// 実質的な main 関数の代わりと思って良い。
// true を返した場合はメインループに突入し、 false を返した場合はそのまま終了する。
bool MyApp::OnInit()
{
// コマンドライン引数を処理する。
if(!wxApp::OnInit())
return false;
MyFrame* frame = new MyFrame;
frame->Show();
return true;
}
MyFrame::MyFrame()
: wxFrame(nullptr, wxID_ANY, "Minimal App")
{
// メインメニューの作成
auto menuBar = new wxMenuBar;
auto menuFile = new wxMenu;
menuFile->Append(wxID_EXIT, "Quit"); // wxID_EXIT のような定義済みIDはプラットフォームによって扱いが変わる。例えば、 Mac ではアプリケーションメニューの Quit と統合される。
menuBar->Append(menuFile, "File");
SetMenuBar(menuBar);
// イベントハンドラーの登録
// 自身のメンバー関数を登録する場合:
Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this);
// 無名関数で直接記述する場合:
Bind(wxEVT_MENU, [this](wxCommandEvent&) { Close(true); }, wxID_EXIT);
}
MyFrame::~MyFrame()
{
}
// wxEVT_CLOSE_WINDOW はウィンドウが閉じられようとしていた場合に呼ばれる。
// 実装しなかった場合は単に Destroy() が呼ばれる。
void MyFrame::OnClose(wxCloseEvent& event)
{
Destroy();
}
// アプリケーションの main 関数がここで定義される(後述)
wxIMPLEMENT_APP(MyApp);
ポイント:
-
wxApp
を継承したクラスを作ってOnInit
関数をオーバーライドする。 -
wxDECLARE_APP
/wxIMPLEMENT_APP
マクロを使って、wxGetApp
関数の宣言・定義とエントリーポイント(main
関数)の定義を行う。(後述) - イベントハンドラーの登録には
Bind
関数を使う。Bind
関数へはメンバー関数のポインタを渡したり、無名関数を直接渡すことができる。- 昔は「イベントテーブル」とか言ってマクロを多用していたが、新規に書くコードでイベントテーブルを使う必要はないだろう。
- プログラム中で
MyFrame
,wxMenuBar
,wxMenu
等のオブジェクトをnew
したまま解放していない。-
MyFrame
はウィンドウがDestroy()
によって破棄された時に自動でdelete
される。 -
wxMenuBar
,wxMenu
のインスタンスは所属先であるMyFrame
が破棄されたタイミングで自動でdelete
される。
-
wxDECLARE_APP マクロ
このマクロでは wxGetApp()
関数の宣言が行われる。wxGetApp()
関数はマクロの引数に渡されたクラス(コード例では MyApp
クラス)の唯一のインスタンスを返す。
つまり、 wxDECLARE_APP(MyApp);
は
MyApp& wxGetApp();
と等価である。
wxIMPLEMENT_APP マクロ
このマクロには、 wxGetApp()
関数の実装や、アプリケーションの main
関数が含まれる。
wxIMPLEMENT_APP
中で定義される main
関数の中身は、大雑把には
int main(int argc, char *argv[]) {
return wxEntry(argc, argv);
}
である。
wxEntry
関数は諸々の初期化処理をした後、 wxIMPLEMENT_APP
に指定されたクラスの OnInit
関数を呼び、メインループに入る。
main
関数をマクロで隠蔽するのは正直言ってキモいが、隠蔽する理由としては、プラットフォームによって適切なエントリーポイントが異なるということが挙げられる。
具体的には、 Windows では GUI アプリは main
関数ではなく (w
)WinMain
関数から始まる。コンソールアプリであっても、 Unicode なコマンドライン引数を適切に扱うには main(int, char **)
ではダメで wmain(int, wchar_t **)
を使う必要がある。
ビルド
ビルドには CMake を使うのが楽1だろう。
cmake_minimum_required(VERSION 3.1)
project(firstapp)
find_package(wxWidgets COMPONENTS core base)
include(${wxWidgets_USE_FILE})
add_executable(FirstApp WIN32
app.cpp
)
target_compile_features(FirstApp PUBLIC cxx_std_14)
target_link_libraries(FirstApp ${wxWidgets_LIBRARIES})
wxWidgets を使う上では、 find_package
, include
, target_link_libraries
の3行がポイントである。
wxWidgets は、いくつかのコンポーネントに分割されている。例えば、 GUI に関係ないもの(文字列クラスなど)は wxBase に、基本的な GUI 機能は wxCore というコンポーネントに、それぞれ含まれている(詳しくは 公式ドキュメント を参照)。
find_package
に指定している core
と base
は、アプリケーションで実際に使う(細かい)コンポーネントを表している。
add_executable
に指定している WIN32
は、 Windows 環境向けに「Win32 の GUI アプリである」ということを指示している。他のプラットフォームでは単に無視されるだけである。
Mac でアプリケーションバンドルを作りたいなら、 add_executable
に MACOSX_BUNDLE
を指定すると良いだろう。
インストール済みの wxWidgets の指定方法
運が良ければ自動でシステムにインストールされた wxWidgets を見つけてくれるかもしれないが、そうでない場合は自分で wxWidgets のインストール先を指定してやる必要がある。
Unix系の場合は、 wx-config
というスクリプト2の位置を指定する。
CMake の実行例:
$ cmake -G "Unix Makefiles" -DwxWidgets_CONFIG_EXECUTABLE=/path/to/wx-config .
あとは make を叩けば実行ファイルができる。
Windows の場合は、公式のバイナリ配布を使うという人もいるだろう。ここでは仮に、Release wxWidgets 3.1.1 から Visual Studio 2017 用のバイナリー (wxMSW-3.1.1_vc141_x64_Dev.7z
) とヘッダーファイル (wxWidgets-3.1.1-headers.7z
) を取ってきて、 D:\wxWidgets-3.1.1
以下に
D:\wxWidgets-3.1.1\include\wx\(たくさんのヘッダファイル)
D:\wxWidgets-3.1.1\include\msvc\wx\setup.h
D:\wxWidgets-3.1.1\lib\vc141_x64_dll\(たくさんの .lib と .dll)
と展開したとする。
この時、 wxWidgets のインストール先である D:\wxWidgets-3.1.1
を指定するには、 CMake の変数 wxWidgets_ROOT_DIR
を使う:
> cmake -G "Visual Studio 15 2017 Win64" -DwxWidgets_ROOT_DIR=D:\wxWidgets-3.1.1 .
あとは Visual Studio 向けのソリューションが出来上がるので、それを使って適当にビルドする。
MSYS2 を使う場合は、 Unix 系と同様に wx-config
を使うのだが、generatorの名前が "MSYS Makefiles"
になる。
$ cmake -G "MSYS Makefiles" -DwxWidgets_CONFIG_EXECUTABLE=/path/to/wx-config .
MSYS2 公式パッケージではなくて独自ビルドの wx-config
を指定した場合、ライブラリーが見つからないとか言って失敗するかもしれない。(TODO: 対処法を書く)
その他
この記事では wxWidgets 自体のビルド・インストールは詳しく扱わなかった。もし wxWidgets を自分でビルドするという場合は、筆者のブログに記事を書いているので、それを参考にしてほしい。