wxWidgets

wxWidgets の最小サンプル

wxWidgets 3.0 以降を使った最小の GUI プログラムを例示し、その解説をする。

コード

本格的なアプリケーションを書くならクラスごとにヘッダーファイルとソースファイルを用意するところだが、今回は簡便さのため、1つのソースファイル (app.cpp) に全て記述してしまう。

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だろう。

CMakeLists.txt
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 に指定している corebase は、アプリケーションで実際に使う(細かい)コンポーネントを表している。

add_executable に指定している WIN32 は、 Windows 環境向けに「Win32 の GUI アプリである」ということを指示している。他のプラットフォームでは単に無視されるだけである。

Mac でアプリケーションバンドルを作りたいなら、 add_executableMACOSX_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 を自分でビルドするという場合は、筆者のブログに記事を書いているので、それを参考にしてほしい。


  1. 「解説する側として」楽、という意味。 

  2. wxWidgets 用の pkg-config 的なやつ。Makefile を手書きする際にも使える。