2001 2 01

1.   要运用的平台

因为 wxWindows 随时可以使用本机控件,所以它采取与大多数其它多平台 GUI 库不同的方式来对待小窗口部件。可以仿效那些不可用的控件,例如 Unix 下的树控件。这将为应用程序的用户提供类似的外观和感觉。wxWindows 库当前支持以下平台:

  • Windows 3.1Windows 95/98Windows NT

  • 带有 Motif/Lesstif 的大多数 UNIX 版本

  • 带有 GTK+ 的大多数 UNIX 版本

  • Mac

仅带非 GUI 类的 wxBase 库也可以在 UNIX/Win32 BeOS 下构建(有一些限制)。即使您不是将 wxWindows 作为 DLL 编译,也可以获得非常小的可执行文件。例如,使用 Windows 平台的 Microsoft Visual C++ 所编译的最小的样本应用程序小于 400 KB。因为 wxWindows 的可执行程序很小,所以通常可以避免所谓的“DLL 灾难

2.   独立于体系结构的类型

为避免体系结构的依赖性,该库提供了各种独立于体系结构的类型和宏,能够根据应用程序的尾数来处理位交换。它们包括:

  • wxInt3232 位有符号整数)

  • wxInt1616 位有符号整数)

  • wxInt88 位有符号整数)

  • wxUint3232 位无符号整数)

  • wxUint16 = wxWord16 位无符号整数)

  • wxUint8 = wxByte8 位无符号整数)

位交换宏可用于整数和无符号整数(其中的 xx 代表 16 32BE 代表大尾数法,LE 代表小尾数法。)

  • wxINTxx_SWAP_ON_BE()

  • wxUINTxx_SWAP_ON_BE()

  • wxINTxx_SWAP_ON_LE()

  • wxUINTxx_SWAP_ON_LE()

  • wxINTxx_SWAP_ALWAYS()

  • wxUINTxx_SWAP_ALWAYS()

这里的用法很直接,如下例所示:

32 位带符号整数变量的字节交换

 

wxInt32 old_var = 0xF1F2F3F4;

wxInt32 new_var = wxINT32_SWAP_ALWAYS( old_var )

 

 

除了这些宏以外,wxWindows 还提供了 #define 来定义机器(库在其上编译)的当前尾数。以下是它的一例:

 

if ( wxBYTE_ORDER == wxLITTLE_ENDIAN ){

    // Do stuff for little endian machine...

}else{

    // Do stuff for big endian machine...

}

 

3.   文件处理

要为具有不同文件存储器概念的不同平台进行编写总是很困难的。要克服这一问题,wxWindows 有一些函数用于多平台文件处理。首先,让我们看一些用于基本文件操作(例如复制、删除和重命名)的函数。

 

wxString old_report = "smithers_00.doc"

wxString new_report = "my_smithers.doc";

if ( wxCopyFile( old_report, "smithers_00.bak" ) == true ){

    if ( wxRemoveFile( old_report ) == true ){

        if ( wxRenameFile( new_report, old_report ) == false ){

            // Doh!

        }

    }

}

 

 

另一个为不同平台进行编写所带来的严重问题是目录分隔字符,但使用 wxPathList 类的话就可以完全避免这一问题。 wxPathList 包含了搜索文件所用的目录列表。如果希望查找某一文件,只需要将文件名传递给 wxPathList 类,它就会搜索预先定义的目录。

 

wxPathList path_list;

 

// Add current working directory

path_list.Add( "." );

 

// Add one directory above current working directory

path_list.Add( ".." );

 

// Add directories from environment variable PATH to the list

path_list.AddEnvList( "PATH" );

wxString path = path_list.FindValidPath( "homer.bmp" );

 

 

wxWindows 中还有两个有用的函数: wxFileNameFromPath() ,用于将文件名从完整路径中剥离,以及 wxPathOnly() ,用于将路径从完整路径中剥离。

4.   HTML

Vaclav Slavik wxHTML 库对基本 HTML 进行语法分析并产生 HTML。它并没有完全实现 HTML 标准,但它的功能对于处理联机帮助已经足够了,它还可以使用标记处理程序进行扩展。要显示 HTML,需要创建一个类为 wxHtmlWindow 的对象。然后调用它的方法来设置相关的框架和相关的状态栏,前者用来实际地显示 HTML,后者用来显示由 HTML 语法分析器所生成的消息。

 

wxHtmlWindow html_window = new wxHtmlWindow( this );

html_window->SetRelatedFrame( this, "HTML : %%s" );

html_window->SetRelatedStatusBar( 0 );

 

 

然后,可以通过使用以下函数来装入 HTML 页面:

 

html_window->LoadPage( "burns.htm" );

 

 

也可以通过以下函数来显示 HTML 代码

 

html_window->SetPage( "<html><body>Hello, Monty!</body></html>" );

 

5.   图像

wxImage 类使用图像格式处理程序来装入各种不同的图像文件格式。可以通过实现自己的图像格式处理程序,扩展 wxImage 来装入新的图像格式。现有的图像格式处理程序使用众所周知的库,例如 Sam Leffler libTIFF 库或独立 JPEG 小组的 JPEG 库。有一些用于 BMPPNGJPEGGIFPCXPNM TIFF 的处理程序。可以通过使用应用程序启动代码中 wxImage 类的静态方法 AddHandler() 来激活每个图像格式处理程序。

 

bool MyApp::OnInit()

{

    wxImage::AddHandler( new wxPNGHandler );

    // more ...

}

 

 

要使用所有现有的图像格式处理程序,只需要调用函数 wxInitAllImageHandlers() 而不是上面显示的 AddHandler() 方法。

在其它平台上使用应用程序中的工具栏位图时要特别小心。在 Windows 上,您将看到 Windows 位图格式,但 Linux 位图通常是 pixmaps,在这种情况下,就不能完全避免有条件的编译。让我们看一些样本代码。

 

#if defined(__WXGTK__) || defined(__WXMOTIF__)

    #include "maggie.xpm"

#endif

 

// more ...

void MyApp::UseBitmap()

{

    wxBitmap bitmap( wxBITMAP( maggie ));

    // more ...

}

 

 

理解了吗?所有效果都是由 wxBITMAP() 宏实现的。对于 Windows OS/2,它将使用应用程序资源中名为 'maggie' 的位图来创建 wxBitmap 对象。对于所有其它平台,它将使用称为 'maggie_xpm' pixmap 来创建 wxBitmap 对象。

当可以使用 wxDC::DrawBitmap() 在设备上下文中绘制位图时,必须为图像操作使用 wxImage 对象,如下所示。

 

wxImage* p_p_w_picpath = new wxImage( bitmap );

// Have some fun

if ( p_p_w_picpath->Ok() ){

    if ( p_p_w_picpath->GetHeight() > 50 && p_p_w_picpath->GetWidth() > 50 ){

        unsigned char red   = p_p_w_picpath->GetRed( 50, 50 );

        unsigned char green = p_p_w_picpath->GetGreen( 50, 50 );

        unsigned char blue  = p_p_w_picpath->GetBlue( 50, 50 );

        // Secure but might be slow

        p_p_w_picpath->SetRGB( 50, 50, red, green, blue );

        // If you want fast action use a pointer...

        unsigned char* data = p_p_w_picpath->GetData();

        // Manipulate the data...

    }

}

6.   Unicode 和国际化 (i18n)

开发适用于国际市场的软件时,不能指望每个人都能阅读以英文表示的应用程序消息。但广泛使用的 ANSI 代码又不能处理所有语言符号。(例如,它不能处理中文。)而同时编写 ANSI Unicode 又有些复杂,您可以从下例中领略到这一点:

ANSI Unicode

 

#ifdef USE__UNICODE

 

    wchar_t wide_char = L'h';

    wchar_t const* wide_string = L"Hello, World!";

int length = wcslen( wide_string );

 

#else

 

    char ansi_char = 'h';

    char const* ansi_string = "Hello, World!";

int length = strlen( ansi_string );

 

#endif

 

而使用 wxWindows Unicode 能力就只需要编写以下代码:

wxT() 宏以及 wxChar wxString

 

wxChar wx_char = wxT( '*' );

wxString wx_string = wxT( "Hello, World!" );

int length = wx_string.Len();

 

wxT() 宏以及 wxChar wxString 类将处理所有问题。该库在内部对所有消息使用这种方法。当前有捷克语、丹麦语、德语、法语、意大利语和俄语译文可用,但您可以在 wxLocale 类的帮助下编译库的本地化版本后使用它。

7.   调试

库提供了各种不同的类、函数和宏来帮助您调试应用程序。如果以调试方式来编译库,用于类 wxObject wxWindows 中大多数类的基类)的 new delete 操作符就已经重新定义,可以存储有关在堆上分配的对象的额外信息。使用这些信息,可以用类 wxDebugContext 来获得有关对象分配、内存泄露、覆盖和正在写入的详细信息。

 

// Start logging for Dump() call

wxDebugContext::SetCheckpoint();

wxString *thing = new wxString;

wxDate* date = new wxDate;

 

// non-object allocation

char *ordinaryNonObject = new char[1000];

 

// more ...

// Print number of object and non-object allocations

 

wxDebugContext::Dump();

 

// Print allocation statistics

wxDebugContext::PrintStatistics();

 

 

wxDebugContext::Dump() 的调用将弹出一个包含分配列表的窗口。您可以在下面看到这样一个列表,这是我用 wxWindows 提供的 memcheck 样本创建的。

 

13:32:45: ----- Memory dump of memcheck at Tue Dec 26 13:32:45 2000 -----

13:32:45: ..\..\..\samples\memcheck\memcheck.cpp(88):

   non-object data at $DD3DC0, size 4

13:32:45: ..\..\..\samples\memcheck\memcheck.cpp(89):

   wxDate at $DD40D0, size 24

13:32:45: ..\..\..\samples\memcheck\memcheck.cpp(92):

   non-object data at $DD4118, size 1000

 

wxDebugContext::PrintStatistics() 的调用将提供一些统计信息,如下所示。

 

13:32:45: ----- Memory statistics of memcheck at Tue Dec 26 13:32:45 2000 -----

13:32:45: 1 objects of class wxDate, total size 24

13:32:45: 5 objects of class nonobject, total size 4256

13:32:45:

13:32:45: Number of object items: 1

13:32:45: Number of non-object items: 5

13:32:45: Total allocated size: 4280

 

8.   MFC 应用程序移植到 Linux

wxWindows 库也可以将 MFC 应用程序移植到 Linux 和其它操作系统。您将在下面的代码摘录中看到,wxWindows 字符串类 wxString MFC 字符串类 CString 有某些相似性。

 

wxString s1 = "Hello, World!";

wxString s2 = "Hello";

if ( s1.IsEmpty() == false ){

    s2.Empty();

    s2 = s1.Left( 5 );

    int pos = s1.Find( ',' );

    s2 += s1.Mid( pos, 2 );

    s2 += s1.Right( 6 );

}

 

wxWindows 的事件系统也与 MFC 非常相似,消息映射表都是将事件处理程序方法映射到事件系统。在 wxWindows 中,这些称为事件表。事件表宏与 MFC 的消息映射只有一点差别。在下面的源代码中显示了主要差异。

 

class CButtonCtrl : public COleControl

{

// Implementation

protected:

    LRESULT OnOcmCommand( WPARAM wParam, LPARAM lParam );

    DECLARE_MESSAGE_MAP()

};

 

 

 

BEGIN_MESSAGE_MAP( CButtonCtrl, COleControl )

    //{{AFX_MSG_MAP( CButtonCtrl )

    ON_MESSAGE( OCM_COMMAND, OnOcmCommand )

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

 

 

class MyButton : public wxButton

{

    void OnButton( wxMouseEvent& event )

private:

    DECLARE_EVENT_TABLE()

};

 

 

 

BEGIN_EVENT_TABLE( MyButton, wxButton )

    EVT_BUTTON( -1, MyButton::OnButton )

END_EVENT_TABLE()

 

如您所见,并没有什么不同。在邮件列表中已经有相当一部分人成功地将他们现有的 MFC 应用程序移植到 wxWindows(我们也非常愿意帮助您:)

9.   标准 C++

wxWindows 还没有使用标准 C++ 技术(例如 std::string STL 或名称空间),因为这样会大量减少 wxWindows 可在其上进行编译的平台数。(只有很少一些编译器完全支持标准 C++ 的最新特性。)但随着标准 C++ 越来越受到普遍支持,wxWindows 开发小组会将对标准 C++ 的支持集成到它的库中。让我们看看他们都做了哪些工作。

 

class A {

    virtual void foo() {};

};

class B : public A {};

A* p_A = new B();

B* p_B = dynamic_cast( p_A );

     

如果有任何错误, p_B 将包含 0 指针。在 wxWindows 中,您将发现一个基于宏的系统用于运行时类型信息,但对于上述 C++ 代码,必须按以下方法使用 wxDynamicCast() 宏:

 

B* p_B = wxDynamicCast( p_A, B );

 

如果类型转换失败, p_B 将包含 0 指针。对于支持新类型转换语法的实现,宏扩展为 dynamic_cast<> 。但不幸的是,宏只对指针起作用,而不对引用起作用。除了 wxDynamicCast() 宏以外,还有 wxStaticCast() wxConstCast() 宏。

wxWindows 字符串类 wxString 提供了 90% 的标准字符串类方法。这里有些示例:

 

wxString s1 = "Hello, World!";

wxString s2 = "Hello";

s2.erase();

for ( size_t i = 0; i < s1.length(); ++i )

    s2 += s1[i];

if ( s1.compare( s2 ) == 0 ) {

    // Strings are equal

}

 

请注意,如果在这里将 wxString typedef std::string ,就可以同时为 wxWindows 和标准 C++ 编写。

10.            文档

wxWindows 文档就目前来说一点也不出色。虽然一些比较陈旧的类(例如 wxString )文档很齐全,但对最近实现的类(例如 wxGrid )或更模糊的类(例如 wxGLCanvas )的描述本应该做得更好。主要文档提供了对库及其概念的快速介绍、按照字母顺序排列的类引用、编程策略、主题概述,以及对 wxHTML wxPython 的某些注释,提供了 HTMLWinHelpMS HTML Help PDF 格式。

如果您刚刚接触 wxWindows,应该从主题概述开始。它们提供了许多有关公共主题(例如调试、事件处理、打印等等)的基本信息和代码示例。主要文档还包含了一些技术说明和教程,它们提供的主题信息范围从有关了解 wxWindows 的一些非常公共的问题到特定于编译器的问题。您还将得到大量纯文本文件,包含用于所支持平台的安装和发行说明。wxWindows 发行版还提供了随文档一起的大约 50 个样本应用程序。

11.            支持

如果您在使用库时遇到问题,在文档中又找不到答案该怎么办?不用担心。通常您有两种支持可选:使用邮件列表的免费支持,或商业支持。如果您不幸需要快速解答,可能最好使用商业支持。虽然核心开发者总是会关注邮件列表,但他们往往很忙碌,无法立即做出响应。但您通常可以在一两天内得到答案。(如果提的问题不太寻常或比较复杂,则在再次提出问题之前应该至少等上两天。)可以从 Sourceforge上的 CVS 源代码数据库访问最新的源码。


现在您对 wxWindows 究竟是什么,它为多平台开发提供了哪些产品和服务应该有了一定认识。但当然,这些不是全部。拿当前的 wxStudio 项目为例,它使用 wxWindows 开发类似于 IDE Microsoft Visual Studio。或者 wxCVS 项目,它将是用于 CVS 系统的多平台图形界面。或者 wxDesigner,它是由 Robert Roebling 开发的一种 RAD 工具,用于构建 wxWindows 对话框。您应该会发现,wxWindows 社区正在不断成长,因此在下一次将要开发多平台项目时请关注一下它。

所有提到的产品名都是其相应拥有者的 商标或注册商标。



关于作者


Markus Neifer 最初在 LOGO 龟标的帮助下开始编程,在此之后他使用过各种版本的 BASIC。在研究 地理信息期间,他学习了一段时间的 C,但随后很快转向 C++ Java,因为它们具有面向对象的性质。 他曾在 R&D 部门工作,在那期间, 他发表了有关面向对象的科学软件开发的文章。 现在,他是地理信息系统领域中的软件工程师。可以通过 [email]markusneifer@my-deja.com[/email] 与他联系。