wxWidgets 跨平台C++开发

2024-02-10 06:18
文章标签 c++ 开发 跨平台 wxwidgets

本文主要是介绍wxWidgets 跨平台C++开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文阐述了 wxWidgets 的由来,以及从 wxWidgets 的特点出发,说明了选择 wxWidgets 给我们带来的好处,并且通过一个典型的例子讨论了如何在 C++ 中使用 wxWidgets 开发跨平台的软件。

什么是 wxWidgets

wxWidgets 是一个跨平台的软件开发包。它诞生于 1992 年,最初的名子是 wxWindows ,但由于 Microsoft 的抗议,在 2004 年改名为 wxWidgets 。它最初是被设计成跨平台的 GUI 软件开发包,但后来随着越来越多的人参与进来,为 wxWidgets 加入了许多非 GUI 的功能,如多线程 (MultiThread) 、网络 (Network) 等。并且从最初的只支持 C++ 语言,逐渐发展成为支持数种语言(如 Python Perl C# Basic 等)。因此,现在的 wxWidgets 已经不再是单纯的跨平台的 GUI 软件开发包,而是一个可以支持多种操作系统平台的能够在多种语言中使用的通用跨平台软件开发包。

由于 wxWidgets 最开始是为 C++ 而设计的,因此,本文主要讨论了 wxWidgets C++ 中的使用。

为什么选择 wxWidgets

目前支持 C++ 的软件开发包非常多,比较有名的除了 wxWidgets 外,还有一些其它的软件开发包,如 MFC QT ACE 等。即然有这么多开发包,那么我们为什么要使用 wxWidgets 呢?在给出答案之前,让我们首先来看一看上述的三种软件开发包的特性。

1.       MFC

MFC Microsoft 提供的软件开发包。 MFC 虽然十分强大,但它只能运行在 Windows 下运行。而且它是收费的。

2.  QT

QT 是由 Trolltech 公司开发的一套跨平台软件开发包。它和 wxWidgets类似,但是 QT只在 linux下免费,而在 Windows Unix下使用 QT要向 Trolltech公司支付版权费。

3.  ACE

ACE 虽然是免费开源的,但是它没有提供 GUI 功能。

    从以上三个软件开发包可以看出,它们虽然有各自的优势,但是它们或多或少地都会使开发受到限制。而使用wxWidgets将不会有以上所述的问题。wxWidgets和MFC、QT、
ACE的特性对比如表1所示。

表1: wxWidgets和MFC、QT、ACE的特性对比表

    注:其中免费中的“是 / 否”代表 QT linux 平台上的 Free Edition 是免费的,而在 windows unix 下使用 QT 是收费的。而开源中的“是 / 否”代表 QT 有一个基于 GPL 的开源版本,但要进行商业开发,需要使用它的商业版本。

使用 wxWidgets 编写程序

学习一种编程语言的最好方法就是用它去编写程序,学习 wxWidgets 也不例外。由于 wxWidgets 的主要功能是实现跨平台的 GUI ,因此,本文主要从 GUI 入手,讨论 wxWidgets C++ 中如何编写跨平台的应用程序

1.  应用程序类的建立

使用 wxWidgets 建立系统需要一个类来描述整个应用程序。这个类必须从 wxApp 类继承。

class  MyApp :  public  wxApp    // 应用程序类
{
public :
    
virtual   bool  OnInit();  //  在应用程序启动时调用,如果返回false,退出应用程序
};

 

这个类只覆盖了 wxApp 的一个虚方法 OnInit 。可以用这个方法在程序启动时做一些验证,如果验证失败,可以通过返回 false 退出应用程序。当然,由于这个函数是应用程序的入口点,所以建立主窗体的工作要在这个函数中完成。

2. 建立窗体类

    wxWidgets 中关于窗体的类很多,如果要建立一般窗体的话,可以从 wxFrame 继承。

class  MyFrame :  public  wxFrame  // 窗体类
{
public :
    MyFrame(
const  wxString &  title);  //  窗体的构造函数
};

 

3. 向窗体中加入控件

    在本文中向这个窗体加入了一个菜单条 (Menu Bar) 、一个状态条、一个 Panel 和一个按钮。一般我们会在主窗体的构造函数中加入这些控件。

MyFrame::MyFrame( const  wxString &  title) : wxFrame(NULL, wxID_ANY, title)
{
    wxMenu 
* fileMenu  =   new  wxMenu;   //  建立“文件”菜单 
    wxMenu  * helpMenu  =   new  wxMenu;  //  建立“帮助”菜单
   
//  向菜单中添加子项
    helpMenu
-> Append(wxID_ABOUT, _T( " 关于 "tF1 " ), _T( " 显示关于对话框 " ));
    fileMenu
-> Append(wxID_EXIT, _T( " 退出"tAlt-X " ), _T( " 退出应用程序 " ));
    wxMenuBar 
* menuBar  =   new  wxMenuBar();   //  建立一个菜单条
    menuBar -> Append(fileMenu, _T( " 文件 " ));   // 将“文件”菜单加入到菜单条
    menuBar -> Append(helpMenu, _T( " 帮助 " ));   // 将“帮助”菜单加入到菜单条
    SetMenuBar(menuBar);   // 将菜单条放到窗体上
    wxPanel  * panel  =   new  wxPanel( this );    // 建立一个Panel
    wxButton  * button  =   new  wxButton(panel, wxID_ABOUT,  " 关于 " , wxPoint( 20 20 ), wxSize( 50 30 ));   // 建立一个Button
    CreateStatusBar( 2 );   // 建立一个两栏的状态栏
    SetStatusText(_T( " 欢迎使用wxWidgets! " ));   // 设置状态栏的文本

 

 

    在数组 sample_xpm 中描述了 sample.ico 的属性和图标本身。如 X 代表红色; o 代表黄色等。然后在源程序中通过 include “sample.xpm” 引用这个资源文件。要想从这个资源文件中装载图标。可使用 SetIcon(wxICON(sample)); wxICON 读取资源文件,而 SetIcon 将这个图标设置为 frame 的标题栏图标。要想将 ico 文件转换为这种资源文件,可使用一个免费软件 XnView进行转换。

5. 显示主窗体

    显示主窗体非常简单,只需要将上面建立的 MyFrame 类实例化,并调用 wxFrame Show 方法显示即可。这些代码可以写在 MyApp 类的 OnInit 方法中。

bool  MyApp::OnInit()
{
   
// 建立MyFrame类的实例
    MyFrame  * frame  =   new  MyFrame(_T( " 第一个wxWidgets程序 " ));
    frame
-> Show( true );   // 显示主窗体
    return   true ;   // 必须返回true,否则应用程序将退出
}

在以上代码中 Show 方法有一个参数,如果为 true ,则以模式窗口的形式显示,否则以非模式窗口的形式显示。

6. 向窗体中加入事件

到目前为止,这个程序的界面已经完成了,但还未响应任何事件,下面就详细阐述如何向这个应用程序中加入事件代码。
    对于事件来说,一般都会由两部分组成。

(1) 调用事件部分

当程序发生某个动作时,如点击按钮;选中某个控件,可能需要执行一段代码。而这段代码一般是由系统负责调用的,也就是说系统通过事件函数指针调用相应的代码。

(2) 事件函数本身

    事件函数与普通函数一样,只不过它是在发生了事件之后,由系统调用的。

wxWidgets 中是通过事件哈希表 (Event Hash Table) 来进行事件处理的,即将相应的事件函数指针保存在一个哈希表中,然后当事件发生时,从这个哈希表中找到相应的事件函数指针,然后通过函数指针调用函数。在使用事件哈希表之前,必须定义它。由于定义哈希表非常复杂,而且每个需要处理事件的类都需要同样的代码,因此, wxWidgets 为此定义了一个宏 DECLARE_EVENT_TABLE() 来定义哈希表。可将这个宏写在 MyFrame 类的任何位置。它相当于将以下语句放到了 MyFrame 类中。

     private :
       
static   const  wxEventTableEntry sm_eventTableEntries[]; 
    
protected
        
static   const  wxEventTable  sm_eventTable; 
        
virtual   const  wxEventTable *   GetEventTable()  const
        
static  wxEventHashTable  sm_eventHashTable;
        
virtual  wxEventHashTable &   GetEventHashTable()  const ;

 

其中静态数组变量 sm_eventTableEntries 保存了 MyFrame 类中的所有的事件信息。

上面的代码声明了处理事件哈希表的一些方法,即然声明了,就得实现。由于实现代码也都一样,因此, wxWidgets 也为实现这些方法定义了一组宏。实现这些方法的宏如下所示。

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MENU(wxID_EXIT,  MyFrame::OnQuit)
    EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
    EVT_BUTTON(wxID_ABOUT, MyFrame::OnAbout)
END_EVENT_TABLE()

    其中 BEGIN_EVENT_TABLE(…) 实现了上面定义的方法,以及初始化了静态变量 sm_eventTable 。后面两个 EVT_MENU 和一个 EVT_BUTTON 宏初始化了静态变量 sm_eventTableEntries ,即将这两个事件函数的指针 (button about 菜单使用一个事件函数 OnAbout) 和控件 ID 保存在 sm_eventTableEntries 中,最后的 END_EVENT_TABLE() 宏做为一个空的事件函数指针赋给了 sm_eventTableEntries ,这有些象 C 语言中处理字符串,将最后一个字符赋为 ’"0’ ,这样就可以知道哪是结尾了。
    向窗体中加入事件的最后一步是声明和实现事件函数。在本例中声明了两个事件函数。

void  OnQuit(wxCommandEvent &   event );
void  OnAbout(wxCommandEvent &   event );

可以将这两个函数声明放到 MyFrame 中的任何位置。下面是它们的实现代码。

void  MyFrame::OnQuit(wxCommandEvent &  WXUNUSED( event ))
{  
    Close(
true );
}
void  MyFrame::OnAbout(wxCommandEvent &  WXUNUSED( event ))
{
    wxString msg;
    msg.Printf( _T(
" 这是一个关于对话框的例子."n " )
                _T(
" 欢迎使用 %s " ), wxVERSION_STRING);
    wxMessageBox(msg, _T(
" ¹关于 " ), wxOK  |  wxICON_INFORMATION,  this );
}

其中 OnQuit 函数调用 Close(true) 关闭 MyFrame ,由于 MyFrame 是主窗体,因此,在 MyFrame 关闭后,应用程序也随之关闭了。 OnAbout 使用 wxMessageBox 函数弹出一个信息对话框。

7.  运行程序

到目前为止,这个程序的代码已经基本完成了,但是在前面曾说过, MyApp 中的 OnInit 方法在应用程序启动时执行,那么是谁调用了 OnInit 方法呢?答案当然是 wxWidgets wxWidgets 为了调用这个方法,提供了一个宏 IMPLEMENT_APP(…) ,这个宏有一个参数,需要将 MyApp 做为参数传入。即 IMPLEMENT_APP(MyApp) 。这个宏相当于一个 WinMain 函数(和控制台程序的 main 函数类似),即在 WinMain 函数中调用了 MyApp 中的 OnInit 函数。在加入这个宏后,就可使用一个 C++ 编译器将以上的源程序编译生成 exe 文件了。应用程序的界面如图 1 、图 2 所示。



图1 Windows下的程序界面




图2  Linux下的程序界面

注:在 windows 下使用的 wxWidgets 版本是 wxWidgets2.6.2 ,在 linux 下使用的 wxWidgets 版本号是 wxWidgets2.6.3 ,因此,在 windows linux 下的 wxVERSION_STRING 值不一样。

wxWidgets 的优势和不足

通过上面的介绍,相信读者已经对如何使用 wxWidgets 编写 GUI 程序有了一定的了解。 wxWidgets 在开发跨平台的软件上有着许多其它软件开发包不具备的优势,下面就总结一下 wxWidgets 所具有的优势。
    1.  跨平台

    wxWidgets
支持非常多的操作系统平台,如 Windows Linux Unix 等。

2.  丰富的组件

wxWidgets 拥有上百个组件可供用户选择。有了这些组件,将会给我们带来更加丰富的用户体验。

3. 支持多种语言

wxWidgets 不仅可以在 C++ 中使用,而且也可以在其它语言中使用,这些语言包括 python perl c# 等。

4.       使用本地控制

从上面给出的两个应用程序界面可以看出,在 Windows Linux 下运行这个应用程序保持了各自的风格。这是因为 wxWidgets 采用了本地的 API ,而不象其它的跨平台库去模拟它们。因此,使用 wxWidgets 开发和在 Windows 下使用 Win32 API 或在 Linux 下使用 GTK 开发没有什么区别。

5. 免费开源

这个世界上免费的开发包很多,强大的开发包也很多,当然,开源的开发包就更多了。但是要想同时满足这三点:免费、开源、强大,又同时具有本地程序一样的性能,恐怕 wxWidgets 是唯一的选择,至少是最佳的选择。

相信上面关于 wxWidgets 5 个优势已经足以成为我们选择它的理由了。也就是说,如果选择 wxWidgets ,不仅可以获得强大的功能、卓越的性能,而且您不必为此付一分钱。当然,人无完人、物无完物。 wxWidgets 也并不是没有缺点。下面就说一下 wxWidgets 的不足之处。

1. IDE 支持不够

对于 wxWidgets 来说,最大的优点也就是它最大的缺点。由于 wxWidgets 所提供的组件很多,但到现在为止还没有一个强大的 IDE 来支持它,这将给大型系统的开发带来麻烦。

2. 对双字节字符的支持不理想

wxWidgets 中的有些组件,如 xml 组件,无法识别双字节字符,如汉字会被认为是非法字符而无法装载 xml 文档。

综合上述, wxWidgets 从总体上来说还是一个非常强大的跨平台软件开发包。如果您没有足够的资金来购买商业的软件开发包,也许 wxWidgets 是最好的选择。虽然 wxWidgets 也有一些不足,但这并不能阻碍 wxWidgets 的发展。 wxWidgets 的功能还很多,由于篇幅所限,本文只能从一个简单的例子来讨论如何用 wxWidgets 来开发一个跨平台的 GUI 程序,如果读者对 wxWidgets 感性趣,可以访问 http://www.wxWidgets.org 获得更多的信息。


    要在Linux下使用Eclipse开发wxWidgets程序,请读者参阅 《快速配置Linux + Eclipse + wxWidgets开发环境

这篇关于wxWidgets 跨平台C++开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/696344

相关文章

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Python结合PyWebView库打造跨平台桌面应用

《Python结合PyWebView库打造跨平台桌面应用》随着Web技术的发展,将HTML/CSS/JavaScript与Python结合构建桌面应用成为可能,本文将系统讲解如何使用PyWebView... 目录一、技术原理与优势分析1.1 架构原理1.2 核心优势二、开发环境搭建2.1 安装依赖2.2 验

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元