2025/02/16

wxWidgets

wxWidgets 是一個開放原始碼而且跨平台的 C++ widget toolkit, 是由 Julian Smart 於 1992 年在愛丁堡大學首先開發。 wxWidgets 的授權授權條款是經過開放原始碼促進會認證,其本質等同於 GNU 較寬鬆公共許可證(LGPL)。 然而一個例外是 wxWidgets 授權允許修改者以自己的授權條款發佈。

一般而言,跨平台的 widget toolkit 有二個策略可以使用:

  • 以一組 API 包裝各個平台的 GUI library 所提供的 widgets,優點是提供 native look and feel, 缺點是移植到不同的平台以及客製化會比較麻煩,使用這個策略的為 wxWidgets
  • 使用不同平台提供的繪圖功能或者是使用跨平台的繪圖函式庫,而在這個基礎上建立自己的 widgets。 這個策略的優點是移植到不同的平台以及客製化相對較為容易,缺點需要使用佈景主題 (theme) 才能夠提供 native look and feel, 使用這個策略的為 FLTK, Qt 與 GTK

下面是在 openSUSE 安裝的指令:

sudo zypper in wxWidgets-3_2-nostl-devel

下面是 Hello World Example

#include <wx/wx.h>

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

wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame {
public:
    MyFrame();

private:
    void OnHello(wxCommandEvent &event);
    void OnExit(wxCommandEvent &event);
    void OnAbout(wxCommandEvent &event);
};

enum { ID_Hello = 1 };

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

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, "Hello World") {
    wxMenu *menuFile = new wxMenu;
    menuFile->Append(ID_Hello, "&Hello...\tCtrl-H",
                     "Help string shown in status bar for this menu item");
    menuFile->AppendSeparator();
    menuFile->Append(wxID_EXIT);

    wxMenu *menuHelp = new wxMenu;
    menuHelp->Append(wxID_ABOUT);

    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append(menuFile, "&File");
    menuBar->Append(menuHelp, "&Help");

    SetMenuBar(menuBar);

    CreateStatusBar();
    SetStatusText("Welcome to wxWidgets!");

    Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello);
    Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT);
    Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT);
}

void MyFrame::OnExit(wxCommandEvent &event) { Close(true); }

void MyFrame::OnAbout(wxCommandEvent &event) {
    wxMessageBox("This is a wxWidgets Hello World example", "About Hello World",
                 wxOK | wxICON_INFORMATION);
}

void MyFrame::OnHello(wxCommandEvent &event) {
    wxLogMessage("Hello world from wxWidgets!");
}

使用下列的指令編譯:

g++ hello.cpp `wx-config --cflags --libs` -o hello

注意 wxIMPLEMENT_APP() 這個巨集,這是一定要定義的巨集, 這樣 wxWdigets 才會正確定義在所支援平台的適當入口函數 (entry function)。 這個巨集採用單一參數,即應用程式類別的名稱。該類別必須繼承自 wxApp,並且至少重寫 wxApp::OnInit() 虛擬函數, 因為會被 wxWidgets 呼叫用來初始化應用程式。

典型應用程式的主視窗是一個 wxFrame 物件。雖然可以直接使用 wxFrame 類別,但是繼承並且自訂類別通常更方便, 因為這樣可以在此類別的方法中儲存附加資料並處理事件(例如滑鼠點擊、來自選單或按鈕的訊息)。

wxWidgets 有三種不同處理 event 的方法。

  • Event table
  • Bind<>()
  • Connect()

其中 Connect() 因為與 Bind<>() 功用相同但是彈性更少,因此不建議使用。


接下來使用 CMake 編譯程式。 首先建立一個 CMakeLists.txt 檔案。

cmake_minimum_required(VERSION 3.14)

# set the project name and version
project(hello)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(wxWidgets COMPONENTS core base)
if(wxWidgets_FOUND)
  include(${wxWidgets_USE_FILE})
endif()

add_subdirectory(src)

建立 src 目錄,並且將原始碼搬移到此目錄下。然後再建立一個 CMakeLists.txt 檔案。

set(SRC_FILES
    hello.cpp
)

add_executable(${PROJECT_NAME} ${SRC_FILES})

target_link_libraries(${PROJECT_NAME} ${wxWidgets_LIBRARIES})

Event table

下面則是使用 event handler 的例子。

main.cpp

#include "main.h"
#include "frame.h"

wxIMPLEMENT_APP(MyApp);

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

main.h

#include <wx/wx.h>

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

frame.cpp

#include "frame.h"

enum { ID_Hello = 1 };

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MENU(ID_Hello, MyFrame::OnHello)
    EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
    EVT_MENU(wxID_EXIT, MyFrame::OnExit)
wxEND_EVENT_TABLE()

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, "Hello World") {
    wxMenu *menuFile = new wxMenu;
    menuFile->Append(ID_Hello, "&Hello...\tCtrl-H",
                     "Help string shown in status bar for this menu item");
    menuFile->AppendSeparator();
    menuFile->Append(wxID_EXIT);

    wxMenu *menuHelp = new wxMenu;
    menuHelp->Append(wxID_ABOUT);

    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append(menuFile, "&File");
    menuBar->Append(menuHelp, "&Help");

    SetMenuBar(menuBar);

    CreateStatusBar();
    SetStatusText("Welcome to wxWidgets!");
}

void MyFrame::OnExit(wxCommandEvent &event) { Close(true); }

void MyFrame::OnAbout(wxCommandEvent &event) {
    wxMessageBox("This is a wxWidgets Hello World example", "About Hello World",
                 wxOK | wxICON_INFORMATION);
}

void MyFrame::OnHello(wxCommandEvent &event) {
    wxLogMessage("Hello world from wxWidgets!");
}

frame.h

#include <wx/wx.h>

class MyFrame : public wxFrame {
public:
    MyFrame();

private:
    void OnHello(wxCommandEvent &event);
    void OnExit(wxCommandEvent &event);
    void OnAbout(wxCommandEvent &event);

    wxDECLARE_EVENT_TABLE();
};

使用 event table,wxDECLARE_EVENT_TABLE() 這個巨集是一定要宣告的。 而後使用 wxBEGIN_EVENT_TABLE()wxEND_EVENT_TABLE(),並且建立 event table。

Sizer

wxWidgets 提供了各種 sizer 來幫忙設計 GUI 的佈局。wxWidgets 中的 sizer 使用的佈局演算法基於這樣的理念: 各個子視窗會報告它們所需的最小尺寸以及當父視窗的尺寸發生變化時,子視窗的延展能力。

下面是一個簡單的 wxBoxSizer 使用例子。

main.cpp

#include "main.h"
#include "frame.h"

wxIMPLEMENT_APP(MyApp);

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

main.h

#include <wx/wx.h>

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

frame.cpp

#include "frame.h"

MyFrame::MyFrame(const wxString &title) : wxFrame(nullptr, wxID_ANY, title) {
    wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
    wxStaticText *text =
        new wxStaticText(this, wxID_STATIC, wxT("A static text box"),
                         wxDefaultPosition, wxSize(200, 100));
    topSizer->Add(text);
    SetSizer(topSizer);
    topSizer->Fit(this);
}

frame.h

#include <wx/wx.h>

class MyFrame : public wxFrame {
public:
    MyFrame(const wxString &title);
};

相關連結

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。