wxWidgets   メニュー

ホーム   C/C++チュートリアル


メニュー


この章では、アプリケーションのメニュー、ステータスバー、ツールバーを説明します。

menu.cpp


#include <wx/wx.h>
#include <wx/menu.h>

class Menu : public wxFrame
{
public:
    Menu();
    void OnQuit(wxCommandEvent &event);
    wxMenuBar *menubar;
    wxMenu    *file;
};

class App : public wxApp
{
public:
    virtual bool OnInit();
};

Menu::Menu() : wxFrame(NULL, -1, "Menu")
{
    menubar = new wxMenuBar;
    file    = new wxMenu;
    menubar->Append(file, "File");
    file->Append(wxID_EXIT, "Quit");
    SetMenuBar(menubar);
    
    Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(Menu::OnQuit));
}

void Menu::OnQuit(wxCommandEvent &event)
{
    Close(true);
}

IMPLEMENT_APP(App)

bool App::OnInit()
{
    Menu *menu = new Menu();
    menu->Show();
    return true;
}
    


windows

File メニューの Quit を選択すると、アプリケーションが終了します。


macOS

macOS では、File メニュー でけではなく、アプリケーションメニューの Quit アプリケーション名 を選択すると、アプリケーションが終了します。 キーボードで、⌘Q(command + Q)を押しても、 アプリケーションを終了できます。


コード説明

  1. #include <wx/menu.h>
    メニューを使うには、menu.hを読み込みます。
  2. menubar = new wxMenuBar;
    メニューバーを作っています。
  3. file = new wxMenu;
    メニューを作っています。
  4. menubar->Append(file, "File");
    メニューバーにメニューを追加しています。引数は次のとおりです。
    1. 第1引数に追加するメニューを指定します
    2. 第2引数にメニューに表示する文字列を指定します
  5. file->Append(wxID_EXIT, "Quit");
    file メニューにメニュー項目(メニューアイテム)を追加しています。 引数は次のとおりです。
    1. 第1引数にIDを指定します。wxID_EXIT定数には 2006 が設定されています
    2. 第2引数にメニューアイテムに表示する文字列を指定します
  6. SetMenuBar(menubar);
    フレームのメニューバーとして menubar を設定しています
  7. Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(Menu::OnQuit));
    メニュー項目が選択された時のイベントは wxEVT_COMMAND_MENU_SELECTED です。


サブメニュー

submenu.cpp


#include <wx/wx.h>
#include <wx/menu.h>

class SubMenu : public wxFrame
{
public:
    SubMenu();
    void OnQuit(wxCommandEvent & event);
    wxMenuBar  *menubar;
    wxMenu     *file;
    wxMenu     *imp;
    wxMenuItem *quit;
};

class App : public wxApp
{
public:
    virtual bool OnInit();
};

SubMenu::SubMenu() : wxFrame(NULL, -1, "SubMenu")
{
    menubar = new wxMenuBar;
    file    = new wxMenu;
    
    file->Append(wxID_NEW,  wxT("新規"));
    file->Append(wxID_OPEN, wxT("開く"));
    file->Append(wxID_SAVE, wxT("保存"));
    file->AppendSeparator();
    
    imp    = new wxMenu;
    imp->Append(-1, wxT("ブックマークをインポート"));
    imp->Append(-1, wxT("メールをインポート"));
    file->AppendSubMenu(imp, wxT("インポート"));
    
    quit   = new wxMenuItem(file, wxID_EXIT, wxT("終了\tCtrl+Q"));
    file->Append(quit);
    
    menubar->Append(file, wxT("ファイル"));
    SetMenuBar(menubar);
    
    Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(SubMenu::OnQuit));
}

void SubMenu::OnQuit(wxCommandEvent & event)
{
    Close(true);
}

IMPLEMENT_APP(App)

bool App::OnInit()
{
    SubMenu *submenu = new SubMenu();
    submenu->Show();
    return true;
}
    


Windows

Control Q もしくは、Windowsキー Q でもアプリケーションを終了できます。

コード説明

  1. file->Append(wxID_NEW, wxT("新規"));
    wxiWidgetsで日本語などの2バイト文字を使う場合はwxTマクロを使います。 ただしmacOSではwxTマクロなしでも2バイト文字が使えます。
    逆にFedoraではアルファベットなどの1バイト文字にもwxTマクロを つけなければなりません。
  2. file->AppendSeparator();
    メニューに区切り線を追加しています。
  3. file->AppendSubMenu(imp, wxT("インポート"));
    AppledSubMenu関数でサブメニューを追加できます。引数は次のとおりです。
    1. 第1引数にサブメニュとして追加するメニュを指定します
    2. 第2引数にサブメニューに表示する文字列を指定します
  4. quit = new wxMenuItem(file, wxID_EXIT, wxT("終了\tCtrl+Q"));
    メニューアイテムは、 wxMenuItemクラスから作ることもできます。引数は次のとおりです。
    1. 第1引数に追加するメニューを指定します
    2. 第2引数にIDを指定します
    3. 第3引数にメニューアイテムに表示する文字列を指定します。 \tはタブ文字に変換されるエスケープ文字です。
  5. file->Append(quit);
    個別に作ったメニューアイテムも メニュークラスのAppend関数で追加しなければいけないみたいです。
  6. なお、macOS特有のことですが、 メニューバーを作った時点でアプリケーションメニューに Quit メニューアイテムが 自動的に作られます。
    そして、メニューバーにファイルメニューを追加してから、 ファイルメニューにQuitメニューアイテムを追加すると ファイルメニュにもQuitメニューアイテムが表示されます。


定義済みメニュー

predefinedmenu.cpp


#include <wx/wx.h>
#include <wx/menu.h>

class PredefinedMenu : public wxFrame
{
public:
    PredefinedMenu();
    wxMenuBar *menubar;
    wxMenu    *filemenu;
    wxMenu    *editmenu;
    wxMenu    *helpmenu;
};

class App : public wxApp
{
public:
    virtual bool OnInit();
};

PredefinedMenu::PredefinedMenu() : wxFrame(NULL, -1, "PredefinedMenu")
{
    menubar  = new wxMenuBar();
    filemenu = new wxMenu();
    editmenu = new wxMenu();
    helpmenu = new wxMenu();
    
    filemenu->Append(wxID_ABOUT);
    filemenu->AppendSeparator();
    filemenu->Append(wxID_NEW);
    filemenu->Append(wxID_OPEN);
    filemenu->Append(wxID_SAVE);
    filemenu->AppendSeparator();
    filemenu->Append(wxID_SAVEAS);
    filemenu->Append(wxID_CLOSE);
    filemenu->AppendSeparator();
    filemenu->Append(wxID_EXIT);
    
    editmenu->Append(wxID_UNDO);
    editmenu->Append(wxID_REDO);
    editmenu->AppendSeparator();
    editmenu->Append(wxID_COPY);
    editmenu->Append(wxID_CUT);
    editmenu->Append(wxID_PASTE);
    editmenu->Append(wxID_DELETE);
    
    helpmenu->Append(wxID_HELP);
    
    menubar->Append(filemenu, "File");
    menubar->Append(editmenu, "Edit");
    menubar->Append(helpmenu, "Help");
    SetMenuBar(menubar);
}

IMPLEMENT_APP(App)

bool App::OnInit()
{
    PredefinedMenu *predefinedmenu = new PredefinedMenu();
    predefinedmenu->Show();
    return true;
}
	


Mint

Mint(Ubuntu)では定義済みメニューを使うとメニューに画像がつきます。WindowsとmacOSではつきません。
wxID_EXITを指定すると、コードを実装しなくても、WindowsではWindowsキーQというショートカットキーで、 macOSでは⌘Qというショートカットキーで、アプリケーションを終了できます。
Mint(Ubunt)ではこの機能はなくコードを実装しなければ終了できません。


コード説明

  1. filemenu->Append(wxID_NEW);
    wxID_NEWなどのwxWidgetsで定義されている定数を使うと、自動的に「NEW Ctrl+N」 というショートカットキー付きの文字列が表示されるようになります。
  2. ショートカットキーがついたメニューアイテムは、 そのショートカットキーに反応するようになります。
  3. 日本語で表示させたい場合は、"新規\tCtrl+N" とすると 日本語でショートカットキーつき文字列が表示できるようになります。 \t はタブを表すエスケープ文字です。
  4. 上記のようにプログラマーがショートカットキーを書き足した場合も、 ショートカットキーに反応するようになります。
  5. "新規" とショートカットキーを書き足さなかった場合は、
    WindowsとmacOSではメニューアイテムにショートカットキーが表示されず、 ショートカットキーにも反応しなくなります。
    Linuxではショーカットキーを書き足さなかった場合も、 メニューアイテムにショートカットキーが表示され、そのキーに反応します。
  6. ショートカットキーに反応すると言っても、 対応するイベントハンドラーを記述しなけば何の処理も行われません。
  7. ただしWindowsとmacOSでは、wxID_EXITを指定したメニューアイテムには、 自動的にアプリケーションの終了機能がつくようになります。
    Linuxには自動的に終了機能はつきません


チェックメニュー

checkmenu.cpp


#include <wx/wx.h>
#include <wx/menu.h>

class CheckMenu : public wxFrame
{
public:
    CheckMenu();
    void OnCheck(wxCommandEvent & event);
    wxMenuBar    *menubar;
    wxMenu       *filemenu;
    wxBoxSizer   *hbox;
    wxBoxSizer   *vbox;
    wxStaticText *statictext;
};

class App : public wxApp
{
public:
    virtual bool OnInit();
};

CheckMenu::CheckMenu() : wxFrame(NULL, -1, "CheckMenu")
{
    menubar    = new wxMenuBar();
    filemenu   = new wxMenu();
    filemenu->AppendCheckItem(1001, wxT("チェックメニュー"));
    statictext = new wxStaticText(this, -1, wxT("未選択"));
    hbox       = new wxBoxSizer(wxHORIZONTAL);
    hbox->Add(statictext, 1, wxALIGN_CENTER);
    vbox       = new wxBoxSizer(wxVERTICAL);
    vbox->Add(hbox, 1, wxLEFT, 10);
    
    menubar->Append(filemenu, "File");
    SetMenuBar(menubar);
    SetSizer(vbox);
    
    Bind(wxEVT_COMMAND_MENU_SELECTED, &CheckMenu::OnCheck, this, 1001);
    //Connect(1001, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CheckMenu::OnCheck));
}

void CheckMenu::OnCheck(wxCommandEvent & event)
{
    if (menubar->IsChecked(1001)) {
        statictext->SetLabel(wxT("チェックされました"));
    } else {
        statictext->SetLabel(wxT("未選択"));
    }
}

IMPLEMENT_APP(App)

bool App::OnInit()
{
    CheckMenu *checkmenu = new CheckMenu();
    checkmenu->Show();
    return true;
}
	

今回は、イベントとイベントハンドラー(そのイベントを処理する関数)を結びつけるために、Connect( ) 関数の代わりに Bind( ) 関数を使ってみました。Bindのほうが、Connectよりも新しい手法です。

Windows


コード説明

  1. filemenu->AppendCheckItem(1001, wxT("チェックメニュー"));
    メニュークラスのAppendCheckItem関数はメニューにチェックメニューアイテムを 追加します。引数は次のとおりです。
    1. 第1引数にIDを指定します
    2. 第2引数にチェックメニューアイテムに表示される文字列を指定します
  2. Bind(wxEVT_COMMAND_MENU_SELECTED, &CheckMenu::OnCheck, this, 1001);
    Bindはウィジェットの発するイベントと その処理をするイベントハンドラーを結びつけるwxWidget3から導入された 新しい関数です。引数は次のとおりです。
    1. 第1引数にイベントの種類を指定します
    2. 第2引数にイベントハンドラーを指定します。先頭に&をつけます
    3. 第3引数に this を指定します
    4. 第4引数にイベント発したウィジェットのIDを指定します
  3. menubar->IsChecked(1001)
    メニューバーのIsChecked関数は引数のIDのチェックメニューアイテムにチェックが ついている場合に true を返します。


コンテキストメニュー

context.cpp


#include <wx/wx.h>

class Context : public wxFrame
{
public:
    Context();
    void OnRightDown(wxCommandEvent & event);
    void OnMinimize(wxCommandEvent & event);
    void OnQuit(wxCommandEvent & event);
    wxMenu * menu;
};

class App : public wxApp
{
public:
    virtual bool OnInit();
};

Context::Context() : wxFrame(NULL, 101, "Context")
{
    menu = new wxMenu();
    menu->Append(1001, "Minimize");
    menu->Append(wxID_EXIT);
    
    Connect(101, wxEVT_RIGHT_DOWN, wxCommandEventHandler(Context::OnRightDown));
    
    Bind(wxEVT_MENU, &Context::OnMinimize, this, 1001);
    
    Bind(wxEVT_MENU, &Context::OnQuit, this, wxID_EXIT);
}

void Context::OnRightDown(wxCommandEvent & event)
{
    PopupMenu(menu);
}

void Context::OnMinimize(wxCommandEvent & event)
{
    Iconize();
}

void Context::OnQuit(wxCommandEvent & event)
{
    Close();
}

IMPLEMENT_APP(App)

bool App::OnInit()
{
    Context *context = new Context();
    context->Show();
    return true;
}
	

右クリックした場合の Bind の方法が分からず、このイベントだけ Connect を使っています。


ウィンドウ内を右クリックすると、その場所にメニューが現れます。Minimize を選択すると、ウィンドウが最小化します。

コード説明

  1. menu = new wxMenu();
    コンテキストメニューを作るには、 メニューバーに所属しないメニューを作っておきます。
  2. Connect(101, wxEVT_RIGHT_DOWN, wxCommandEventHandler(Context::OnRightDown));
    ウィンドウを右クリックした時のイベントは wxEVT_RIGHT_DOWN です。
  3. PopupMenu(menu);
    PopupMenu関数で引数のメニューを表示するコンテキストメニューが表示されます。
  4. Iconize();
    Iconize関数で、ウィンドウを最小化します。


ステータスバー

statusbar.cpp


#include <wx/wx.h>
#include <wx/menu.h>

class StatusBar : public wxFrame
{
public:
    StatusBar();
    void ShowStatusBar(wxCommandEvent & event);
    void ShowWxWidgets(wxCommandEvent & event);
    wxMenuBar *menubar;
    wxMenu    *view;
};

class App : public wxApp
{
public:
    virtual bool OnInit();
};

StatusBar::StatusBar() : wxFrame(NULL, -1, "StatusBar")
{
    menubar = new wxMenuBar;
    view    = new wxMenu;
    view->AppendCheckItem(1001, "Show StatusBar");
    view->AppendCheckItem(1002, "Show wxWidgets");
    view->Check(1001, true);
    menubar->Append(view, "View");
    SetMenuBar(menubar);
    CreateStatusBar();
    
    Bind(wxEVT_COMMAND_MENU_SELECTED, &StatusBar::ShowStatusBar, this, 1001);
    
    Bind(wxEVT_COMMAND_MENU_SELECTED, &StatusBar::ShowWxWidgets, this, 1002);
}

void StatusBar::ShowStatusBar(wxCommandEvent & event)
{
    if (menubar->IsChecked(1001)) {
        GetStatusBar()->Show();
    } else {
        GetStatusBar()->Hide(); 
    }
}

void StatusBar::ShowWxWidgets(wxCommandEvent & event)
{
    if (menubar->IsChecked(1002)) {
        SetStatusText("wxWidgets");
    } else {
        SetStatusText("");
    }
}


IMPLEMENT_APP(App)

bool App::OnInit()
{
    StatusBar *statusbar = new StatusBar();
    statusbar->Show();
    return true;
}
	


Windows

Mint

コード説明

  1. view->Check(1001, true);
    メニュークラスのCheck関数でチェックアイテムにチェックを付けられます。
  2. CreateStatusBar();
    CreateStatusBar関数でステータスバーが作られます。
  3. GetStatusBar()->Show();
    GetStatusBar関数で現在のステータスバーが得られます。そしてステータスバーの Show関数でステータスバーが表示されます。
  4. GetStatusBar()->Hide();
    ステータスバーのHide関数でステータスバーが非表示になります。
  5. SetStatusText("wxWidgets");
    SetStatusText関数は、引数の文字列をステータスバーに表示します。
  6. SetStatusText("");
    ステータスバーの文字列を空文字にするとによって、 ステータスバーの文字列を消しています。


ツールバー

次の toolbar.cpp で使われている std::to_string() という関数は、g++ のバージョン 7 以上が必要です。Mint 18 と Ubuntu 16.04 のデフォルトの g++ は、バージョン 4 か 5 です。ターミナルで次のコマンドを実行して バージョン 9 をインストールしてください。この作業は、Mint 18 と Ubuntu 16.04 以外では必要ありません。

  1. sudo add-apt-repository ppa:ubuntu-toolchain-r/test
  2. sudo apt update
  3. sudo apt install -y g++-9

コンパイルする時は次のようにします。
g++-9 toolbar.cpp `wx-config --cppflags --libs` -o ToolBar
デフォルトの g++ を使う場合は、今までとおりに次のようにします。
g++ ~~~~.cpp `wx-config -cppflags --libs` -o ~~~~

toolbar.cpp


#include <wx/wx.h>
#include <wx/artprov.h>

class ToolBar : public wxFrame
{
public:
    ToolBar();
    wxStaticText *text;
    wxToolBar    *toolbar;
    void OnUndo(wxCommandEvent & event);
    void OnRedo(wxCommandEvent & event);
    void OnQuit(wxCommandEvent & event);
};

class App : public wxApp
{
public:
    virtual bool OnInit();
};

ToolBar::ToolBar() : wxFrame(NULL, -1, "ToolBar")
{
    wxImage::AddHandler(new wxPNGHandler);
    wxBitmap undo = wxArtProvider::GetBitmap(wxART_UNDO);
    wxBitmap redo = wxArtProvider::GetBitmap(wxART_REDO);
    wxBitmap exit = wxArtProvider::GetBitmap(wxART_QUIT);
    
    toolbar = CreateToolBar();
    toolbar->AddTool(wxID_UNDO, "Undo", undo);
    toolbar->AddTool(wxID_REDO, "Redo", redo);
    toolbar->AddSeparator();
    toolbar->AddTool(wxID_EXIT, "Quit", exit);
    toolbar->Realize();
    toolbar->EnableTool(wxID_REDO, false);
    
    text = new wxStaticText(this,101, "5");
    wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
    hbox->Add(text, 1, wxALIGN_CENTER);
    vbox->Add(hbox, 1, wxALIGN_CENTER);
    SetSizer(vbox);
    
    Bind(wxEVT_COMMAND_TOOL_CLICKED, &ToolBar::OnUndo, this, wxID_UNDO);
    
    Bind(wxEVT_COMMAND_TOOL_CLICKED, &ToolBar::OnRedo, this, wxID_REDO);
    
    Bind(wxEVT_COMMAND_TOOL_CLICKED, &ToolBar::OnQuit, this, wxID_EXIT);
}

void ToolBar::OnUndo(wxCommandEvent & event)
{
    int num = atoi(text->GetLabel());
    num--;
    switch (num)
    {
        case 0:
            toolbar->EnableTool(wxID_UNDO, false);
            break;
        case 4:
            toolbar->EnableTool(wxID_REDO, true);
            break;
    }
    text->SetLabel(std::to_string(num));
}

void ToolBar::OnRedo(wxCommandEvent & event)
{
    int num = atoi(text->GetLabel());
    num++;
    switch (num)
    {
        case 1:
            toolbar->EnableTool(wxID_UNDO, true);
            break;
        case 5:
            toolbar->EnableTool(wxID_REDO, false);
            break;
    }
    text->SetLabel(std::to_string(num));
}

void ToolBar::OnQuit(wxCommandEvent & event)
{
    Close();
}

IMPLEMENT_APP(App)

bool App::OnInit()
{
    ToolBar *toolbar = new ToolBar();
    toolbar->Show();
    return true;
}
	

今回は、wxArtProvider で提供されているビットマップイメージを使っています。

Windows

macOS

Mint

コード説明

  1. #include <wx/artprov.h>
  2. wxImage::AddHandler(new wxPNGHandler);
    wxImageクラスのAddHandler関数を呼び出すことによって引数のイメージが アプリケーションで使えるようになります。ここではPNGが使えるようになります。
  3. wxBitmap undo = wxArtProvider::GetBitmap(wxART_UNDO);
    wxArtProviderが用意しているsxART_UNDOを取得しています。
  4. toolbar = CreateToolBar();
    ツールバーを作っています。
  5. toolbar->AddTool(wxID_UNDO, "Undo", undo);
    ツールバーにツール(ボタン)を追加しています。引数は次のとおりです。
    1. 第1引数にIDを指定します
    2. 第2引数にツールに表示される文字列を指定しまが、 ツールはデフォルトでアイコン表示になっていますので、 通常文字列が表示されることはありません
    3. 第3引数にアイコン画像を指定します
  6. toolbar->AddSeparator();
    ツールバーに区切り線を追加しています
  7. toolbar->Realize();
    ツールバーのRealize関数を呼び出すことによって ツールバーが表示さっることになります。
  8. toolbar->EnableTool(wxID_REDO, false);
    ツールーバーのEnableTool関数でツール(ぼたん)の使用・不使用を設定します。
    1. 第1引数にツール(ボタン)を指定します
    2. 第2引数に false を指定するとそのツールは使えなくなります
  9. Bind(wxEVT_COMMAND_TOOL_CLICKED, &ToolBar::OnUndo, this, wxID_UNDO);
    ツールバーのツールがクリックされた時のイベントは wxEVT_COMMAND_TOOL_CLICKED です。
  10. int num = atoi(text->GetLabel());
    1. StaticTextのGetLabel関数でStaticTextの文字列を取得できます
    2. Cの標準関数のatoiは、引数の文字列を数値に変換した値を返します
  11. toolbar->EnableTool(wxID_REDO, true);
    EnableToolの第2引数に true を指定すると、そのツールは使用可能になります。
  12. text->SetLabel(std::to_string(num));
    C++の標準関数std::to_stringは引数の数値を文字列変換した値を返します。



3587 visits
Posted: Nov. 27, 2019
Update: Dec. 29, 2019

ホーム   C/C++チュートリアル   目次