wxWidgets

C++用のクロスプラットフォームGUIライブラリ。各プラットフォームのネイティブAPIを使用し、OS標準の外観と操作感を実現。軽量で高速、商用利用も可能なライセンス。長期間にわたる安定したAPI提供。

デスクトップC++クロスプラットフォームGUIネイティブマルチプラットフォーム

GitHub概要

wxWidgets/wxWidgets

Cross-Platform C++ GUI Library

スター6,702
ウォッチ254
フォーク1,826
作成日:2011年5月18日
言語:C++
ライセンス:-

トピックス

c-plus-pluscocoacross-platformcross-platform-desktopdesktopgtkguigui-frameworklinuxmacosportablewin32windowswxwidgetsx11

スター履歴

wxWidgets/wxWidgets Star History
データ取得日時: 2025/7/15 22:54

デスクトップフレームワーク

wxWidgets

概要

wxWidgetsは、C++で書かれたクロスプラットフォームGUIライブラリです。ネイティブな見た目と動作を提供し、Windows、macOS、Linux(GTK)、その他多くのプラットフォームで動作します。1992年から開発されており、安定性と互換性で定評があります。

詳細

wxWidgetsは各プラットフォームのネイティブAPIを直接使用するため、OSごとに自然な見た目とユーザビリティを実現できます。WindowsではWin32 API、macOSではCocoa、LinuxでGTKを使用し、真にクロスプラットフォームなアプリケーションを構築できます。豊富なUIコントロールとレイアウト管理機能を提供し、エンタープライズレベルのアプリケーション開発に適しています。

特徴

  • ネイティブ外観: 各プラットフォームでネイティブな見た目と動作
  • 広範囲なプラットフォーム対応: Windows、macOS、Linux、FreeBSD、Solarisなど
  • 豊富なコントロール: ボタン、リスト、ツリー、グリッド、メニューなど
  • オープンソース: wxWindows Library Licenceの下で利用可能
  • 長期安定性: 30年以上の開発実績
  • Python bindings: wxPython経由でPythonからも利用可能

アーキテクチャ

  • MVC対応: Document/Viewアーキテクチャのサポート
  • イベント駆動: コールバックではなくイベントベースの設計
  • メモリ管理: 自動的なメモリ管理とリソース解放
  • 国際化: Unicode対応と多言語サポート
  • プリンティング: 高品質な印刷機能

メリット・デメリット

メリット

  • ネイティブな見た目でOSに自然に統合される
  • 非常に安定したライブラリで長期サポートが期待できる
  • 豊富なドキュメントとサンプルコード
  • 大規模なコミュニティとエコシステム
  • エンタープライズアプリケーションでの実績
  • Pythonバインディング(wxPython)も利用可能

デメリット

  • 学習コストが高く、API が複雑
  • モダンなUI/UX設計には向かない
  • C++特有の複雑性(メモリ管理、コンパイラ依存)
  • 新しいUIトレンドへの対応が遅い
  • バンドルサイズが大きくなりがち

参考ページ

書き方の例

Hello World

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    bool OnInit() override;
};

class MyFrame : public wxFrame
{
public:
    MyFrame();
    
private:
    void OnExit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);
};

wxIMPLEMENT_APP(MyApp);

bool MyApp::OnInit()
{
    MyFrame *frame = new MyFrame();
    frame->Show(true);
    return true;
}

MyFrame::MyFrame()
    : wxFrame(nullptr, wxID_ANY, "Hello wxWidgets")
{
    CreateStatusBar();
    SetStatusText("Welcome to wxWidgets!");
}

ウィンドウとコントロール

class MainFrame : public wxFrame
{
public:
    MainFrame() : wxFrame(nullptr, wxID_ANY, "wxWidgets App")
    {
        // メニューバー
        wxMenuBar* menuBar = new wxMenuBar();
        wxMenu* fileMenu = new wxMenu();
        fileMenu->Append(wxID_EXIT, "&Exit");
        menuBar->Append(fileMenu, "&File");
        SetMenuBar(menuBar);
        
        // メインパネル
        wxPanel* panel = new wxPanel(this);
        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
        
        // コントロール
        wxStaticText* label = new wxStaticText(panel, wxID_ANY, "Hello World!");
        wxTextCtrl* textCtrl = new wxTextCtrl(panel, wxID_ANY, "");
        wxButton* button = new wxButton(panel, wxID_ANY, "Click Me");
        
        // レイアウト
        sizer->Add(label, 0, wxALL, 5);
        sizer->Add(textCtrl, 0, wxALL | wxEXPAND, 5);
        sizer->Add(button, 0, wxALL, 5);
        
        panel->SetSizer(sizer);
        
        // イベントハンドラー
        button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, 
                    [this](wxCommandEvent&) {
                        wxMessageBox("Button clicked!", "Info");
                    });
    }
};

イベント処理とダイアログ

class EventFrame : public wxFrame
{
public:
    EventFrame() : wxFrame(nullptr, wxID_ANY, "Event Handling")
    {
        wxPanel* panel = new wxPanel(this);
        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
        
        wxButton* dialogBtn = new wxButton(panel, wxID_ANY, "Show Dialog");
        wxButton* fileBtn = new wxButton(panel, wxID_ANY, "Open File");
        wxButton* exitBtn = new wxButton(panel, wxID_ANY, "Exit");
        
        sizer->Add(dialogBtn, 0, wxALL, 5);
        sizer->Add(fileBtn, 0, wxALL, 5);
        sizer->Add(exitBtn, 0, wxALL, 5);
        
        panel->SetSizer(sizer);
        
        // イベントバインド
        dialogBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &EventFrame::OnDialog, this);
        fileBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &EventFrame::OnOpenFile, this);
        exitBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &EventFrame::OnExit, this);
    }
    
private:
    void OnDialog(wxCommandEvent& event)
    {
        wxMessageDialog dlg(this, "This is a message dialog", "Information", 
                           wxOK | wxICON_INFORMATION);
        dlg.ShowModal();
    }
    
    void OnOpenFile(wxCommandEvent& event)
    {
        wxFileDialog dlg(this, "Choose a file", "", "", 
                        "Text files (*.txt)|*.txt|All files (*.*)|*.*",
                        wxFD_OPEN | wxFD_FILE_MUST_EXIST);
                        
        if (dlg.ShowModal() == wxID_OK)
        {
            wxString path = dlg.GetPath();
            wxMessageBox("Selected: " + path, "File Selected");
        }
    }
    
    void OnExit(wxCommandEvent& event)
    {
        Close(true);
    }
};

リストとグリッド

class ListFrame : public wxFrame
{
public:
    ListFrame() : wxFrame(nullptr, wxID_ANY, "List and Grid")
    {
        wxPanel* panel = new wxPanel(this);
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
        
        // リストコントロール
        wxListCtrl* listCtrl = new wxListCtrl(panel, wxID_ANY, 
                                             wxDefaultPosition, wxDefaultSize,
                                             wxLC_REPORT | wxLC_SINGLE_SEL);
        
        // カラム追加
        listCtrl->AppendColumn("Name", wxLIST_FORMAT_LEFT, 100);
        listCtrl->AppendColumn("Age", wxLIST_FORMAT_RIGHT, 60);
        listCtrl->AppendColumn("City", wxLIST_FORMAT_LEFT, 120);
        
        // データ追加
        long index = listCtrl->InsertItem(0, "John Doe");
        listCtrl->SetItem(index, 1, "30");
        listCtrl->SetItem(index, 2, "New York");
        
        index = listCtrl->InsertItem(1, "Jane Smith");
        listCtrl->SetItem(index, 1, "25");
        listCtrl->SetItem(index, 2, "Los Angeles");
        
        // グリッド(要wxGrid include)
        wxGrid* grid = new wxGrid(panel, wxID_ANY);
        grid->CreateGrid(5, 3);
        grid->SetColLabelValue(0, "Product");
        grid->SetColLabelValue(1, "Price");
        grid->SetColLabelValue(2, "Stock");
        
        // グリッドデータ
        grid->SetCellValue(0, 0, "Laptop");
        grid->SetCellValue(0, 1, "$999");
        grid->SetCellValue(0, 2, "10");
        
        sizer->Add(listCtrl, 1, wxALL | wxEXPAND, 5);
        sizer->Add(grid, 1, wxALL | wxEXPAND, 5);
        
        panel->SetSizer(sizer);
    }
};

マルチスレッドとタイマー

class TimerFrame : public wxFrame
{
public:
    TimerFrame() : wxFrame(nullptr, wxID_ANY, "Timer Example"),
                   timer(this, wxID_ANY)
    {
        wxPanel* panel = new wxPanel(this);
        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
        
        timeLabel = new wxStaticText(panel, wxID_ANY, "00:00:00");
        wxButton* startBtn = new wxButton(panel, wxID_ANY, "Start Timer");
        wxButton* stopBtn = new wxButton(panel, wxID_ANY, "Stop Timer");
        
        sizer->Add(timeLabel, 0, wxALL | wxCENTER, 10);
        sizer->Add(startBtn, 0, wxALL, 5);
        sizer->Add(stopBtn, 0, wxALL, 5);
        
        panel->SetSizer(sizer);
        
        // イベントバインド
        startBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &TimerFrame::OnStart, this);
        stopBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &TimerFrame::OnStop, this);
        timer.Bind(wxEVT_TIMER, &TimerFrame::OnTimer, this);
        
        startTime = wxDateTime::Now();
    }
    
private:
    wxTimer timer;
    wxStaticText* timeLabel;
    wxDateTime startTime;
    
    void OnStart(wxCommandEvent& event)
    {
        startTime = wxDateTime::Now();
        timer.Start(1000); // 1秒間隔
    }
    
    void OnStop(wxCommandEvent& event)
    {
        timer.Stop();
    }
    
    void OnTimer(wxTimerEvent& event)
    {
        wxDateTime now = wxDateTime::Now();
        wxTimeSpan diff = now - startTime;
        timeLabel->SetLabel(diff.Format("%H:%M:%S"));
    }
};

カスタム描画

class DrawingPanel : public wxPanel
{
public:
    DrawingPanel(wxFrame* parent) : wxPanel(parent)
    {
        Bind(wxEVT_PAINT, &DrawingPanel::OnPaint, this);
        Bind(wxEVT_LEFT_DOWN, &DrawingPanel::OnMouseDown, this);
    }
    
private:
    std::vector<wxPoint> points;
    
    void OnPaint(wxPaintEvent& event)
    {
        wxPaintDC dc(this);
        
        // 背景をクリア
        dc.Clear();
        
        // グラデーション
        wxRect rect = GetClientRect();
        dc.GradientFillLinear(rect, wxColour(200, 200, 255), 
                             wxColour(100, 100, 200), wxSOUTH);
        
        // 円と線を描画
        dc.SetPen(wxPen(wxColour(255, 0, 0), 3));
        dc.SetBrush(wxBrush(wxColour(0, 255, 0, 128)));
        
        for (size_t i = 0; i < points.size(); ++i)
        {
            dc.DrawCircle(points[i], 20);
            if (i > 0)
            {
                dc.DrawLine(points[i-1], points[i]);
            }
        }
        
        // テキスト描画
        dc.SetTextForeground(wxColour(0, 0, 0));
        dc.DrawText(wxString::Format("Points: %zu", points.size()), 10, 10);
    }
    
    void OnMouseDown(wxMouseEvent& event)
    {
        points.push_back(event.GetPosition());
        Refresh(); // 再描画をトリガー
    }
};