8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

wxWidgets の最小サンプル

Last updated at Posted at 2018-04-01

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 を手書きする際にも使える。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?