20.4 实现联机帮助

虽然你应该尽可能的将你的应用程序界面设计的非常直观,以便用户根本不需要使用联机帮助,但是除了那些最最简单的应用程序外,对于大多数应用程序来说,提供联机帮助都是一个非常重要的事情.你可以提供PDF版的帮助文件或者是HTML格式的帮助文件以便用户可以使用他最习惯的方式浏览,不过如果你能够借助于某种帮助制作手段,使得你的联机帮助中的主题直接和对话框或者你的主窗口中的控件联系起来,这会让你的用户感觉更好.

wxWidgets提供了帮助控制器,你的应用程序可以使用它来加载和显示帮助文件中的主题,它主要包括下面几个类:

  • wxWinHelpController,这个类用来提供对windows平台的基于RTF格式的帮助文件(扩展名为.hlp)的支持. 这中格式现在已经不推荐使用了,新的用来代替它的类是wxCHMHelpController.
  • wxCHMHelpController, 用来提供对windows平台的MS HTML帮助文件(扩展名是.chm)的支持.
  • wxWinceHelpController,用来提供对WindowsCE上的帮助文件(扩展名是.htm)的支持.
  • wxHtmlHelpController, 用来提供对wxWidgets自定义的HTML帮助文件(扩展名是.htb)的支持.

wxHtmlHelpController类和别的帮助控制类是不同的,别的类都只是封装了那个平台上相应的本地实现,而这个类是和 wxWidgets的帮助实现机制集成在一起的,和主程序属于同一个进程.如果你想以不同进程的方式使用wxWidgets提供的帮助文件,你可以编译 wxWidgets自带的utils/src/helpview目录下的HelpView程序.其中文件remhelp.h和remhelp.cpp实现了一个远程帮助文件控制器(wxRemoteHtmlHelpController),你可以将它和你的应用程序链接在一起,以便你的应用程序可以远程控制位于别的进程的帮助文件控制器实例.

注意到目前为止,还没有实现对Mac OSX平台上的帮助文件的支持,在这个平台上,你可以使用通用的wxWidgets HTML格式的帮助文件.

下面的两副图演示了Windows平台上同时显示MS HTML格式的帮助文件和wxWidgets的HTML帮助文件的样子.它们两个的外观很相似,都是右边显示HTML帮助的内容,左边则显示主题的继承关系以及一个用来搜索主题的文本框.稍微有一点不同的是:wxWidgets格式的帮助控制器可以同时加载多个帮助文件.

使用帮助控制器

一般说来,你需要创建一个帮助文件控制器并且在整个应用程序的生命周期维护它,通常是在应用程序类中保存一个指针,在OnInit函数中初始化,在OnExit函数中释放.之所以使用指针是因为你可以自己控制什么时候释放这个指针,某些帮助控制器是依赖于某种动态链接库类的,这个类在应用程序对象被释放以后就不存在了.在创建这个帮助控制器指针以后,使用其Initialize指定一个帮助文件.你可以不用提供文件的扩展名, wxWidgets将会提供针对当前平台的扩展名,比如:

#include "wx/help.h"
#include "wx/fs_zip.h"
bool MyApp::OnInit()
{
    ...
    // wxWidgets HTML需要这个
    wxFileSystem::AddHandler(new wxZipFSHandler);
    m_helpController = new wxHelpController;
    m_helpController->Initialize(helpFilePath);
    ...
    return true;
}
int MyApp::OnExit()
{
    delete m_helpController;
    ...
    return 0;
}

注意这里我们让wxWidgets自己决定使用哪个帮助控制器类: wxHelpController在Windows平台上定义为wxCHMHelpController,而在别的平台上则定义为 wxHtmlHelpController.你可以在windows平台上也使用wxHtmlHelpController类,不过最好是尽可能使用本地平台提供的帮助控制器.

一旦帮助控制器初始化成功,你就可以使用下面的函数显示相应的帮助了:

// 显示帮助内容
m_helpController->DisplayContents();
// 显示标题为"Introduction"的主题
m_helpController->DisplaySection(wxT("Introduction"));
// 显示包含在指定文件中的帮助主题.
m_helpController->DisplaySection(wxT("wx.html"));
// 显示指定ID的主题(仅适用于WinHelp和MS HTML Help)
m_helpController->DisplaySection(500);
// 查找关键字
m_helpController->KeywordSearch(wxT("Getting started"));

通常情况下,你将在帮助菜单的事件处理函数中调用DisplayContents,也许在帮助菜单中你还会列举出其它的重要的主题,你可以在其对应的事件处理函数中使用DisplaySection函数,如果你希望通过标题使用DisplaySection函数,那么所有的标题必须是不重复的.

可能你还打算在所有的自定义对话框上增加一个帮助按钮,以便用来显示针对这个对话框的帮助主题.然而,这里有一个需要注意的事情:不是所有的平台都支持从一个模式对话框的事件处理函数中显示帮助.当帮助文件是通过一个外部程序显示的时候(比如Windows平台的 wxCHMHelpController),你可以放心的在模式对话框中调用帮助控件,但是如果帮助控制器只是在本进程中通过一个非模式对话框显示帮助文件时(比如wxHtmlHelpController),你必须小心,因为通常我们不可以在一个模式对话框中创建一个非模式的frame窗口.模式对话框不允许你切换到另外一个非模式的对话框中.在wxGTK平台上,这种行为仅对于wxHtmlHelpController来说是可以的,但是在Mac OS X平台上,这种行为是不允许的,这时你可以使用前面介绍的使用自己编译的HelpView程序等外部程序来显示帮助文件,或者使用模式对话框来显示帮助文件,后一种方法我们稍后会进一步描述.

扩展wxWidgets HTML帮助

wxWidgets的HTML帮助系统确实是很不错的,不过它有两个问题:首先,它只能在自己的frame窗口中显示帮助,因此你不可以在你的主窗口的某个TAB控件中显示帮助文件,另外一个缺陷是前面提到过的在模式对话框中使用的问题.

为了解决这两个问题而对wxWidgets HTML帮助系统进行的一个扩展将wxWidgets的帮助显示在一个自定义的窗口中,这个窗口可以是任何类型窗口的子窗口.你可以从光盘的 examples/chap20/htmlctrlex目录中或者ftp: //biolpc22.york.ac.uk/pub/contrib/helpctrlex获取它的源代码.

如果你将它集成进你的应用程序,你可以将wxHtmlHelpWindowEx窗口集成进你的主窗口,然后使用wxHtmlHelpControllerEx类来对它进行和别的控制器一样的控制来显示帮助内容,下面是一个例子:

#include "helpwinex.h"
#include "helpctrlex.h"
bool MyApp::OnInit()
{
    ...
    m_embeddedHelpController = new wxHtmlHelpControllerEx;
    m_embeddedHelpWindow = new wxHtmlHelpWindowEx;
    m_embeddedHelpWindow->SetController(m_embeddedHelpController);
    m_embeddedHelpController->SetHelpWindow(m_embeddedHelpWindow);
    m_embeddedHelpController->UseConfig(config, wxT("EmbeddedHelp"));
    m_embeddedHelpWindow->Create(parentWindow,
        wxID_ANY, wxDefaultPosition, wxSize(200, 100),
        wxTAB_TRAVERSAL|wxNO_BORDER, wxHF_DEFAULT_STYLE);
    m_embeddedHelpController->AddBook(wxT("book1.htb"));
    m_embeddedHelpController->AddBook(wxT("book2.htb"));
    return true;
}
int MyApp::OnExit(void)
{
    if (m_embeddedHelpController)
    {
        m_embeddedHelpController->SetHelpWindow(NULL);
        delete m_embeddedHelpController;
    }
    ...
    return 0;
}

而为了解决模式对话框的问题,你可以使用wxModalHelp类来在模式对话框中显示某个帮助主题.当用户看完帮助以后,需要使用关闭按钮关闭这个帮助对话框,然后焦点才会重新返回上一个模式对话框中去.下面的代码就是你所需要作的全部:

wxModalHelp help(parentWindow, helpFile, topic);

在同一个程序使用两种不同的方法来显示帮助有时候是很不方便的,这时候你可以使用下面的函数来节约一些代码:

// 如果modalParent不为空,则使用模式帮助显示方法,否则就使用普通的方法.
void MyApp::ShowHelp(const wxString& topic, wxWindow* modalParent)
{
#if USE_MODAL_HELP
    if (modalParent)
    {
        wxString helpFile(wxGetApp().GetFullAppPath(wxT("myapp")));
        wxModalHelp help(modalParent, helpFile, topic);
    }
    else
#endif
    {
        if (topic.IsEmpty())
            m_helpController->DisplayContents();
        else
            m_helpController->DisplaySection(topic);
    }
}

宏USE_MODAL_HELP应该在那些使用wxHtmlHelpController控件的平台上被定义.当你在模式对话框中显示帮助的时候,将这个对话框的指针作为ShowHelp函数的第二个参数,这样,如果需要,帮助就会显示在一个模式的对话框中,而当你不是在模式对话框中显示帮助的时候,只需要传递NULL作为ShowHelp的第二个参数.

帮助文件中的声明

大多数现代的帮助文件系统都是基于HTML格式的.为了让跨平台的帮助文件制作更容易一些,wxWidgets的HTML帮助文件使用了和MS的HTML帮助文件同样的工程,内容以及关键字文件输入格式.这可以让你在制作多平台的帮助文件时,只需要考虑一套文件就可以了.下面列举出为了创建帮助文件所需要的所有的文件:

  • 一套HTML文件,每个主题是一个文件.
  • 一个内容文件(扩展名是hhc)以XML的格式描述了主题的继承关系.
  • 一个可选的关键字文件(扩展名是hhk)用来将关键字映射到帮助主题.
  • 一个工程文件(扩展名是hhp)用来描述工程中所有的其它文件以及整个工程的各个选项.

然后,你就可以将它们编译为MS HTML帮助文件格式(扩展名为chm)或者wxWidgets的HTML帮助文件格式(扩展名是htb).对于前者,你需要使用微软的HTML帮助制作软件(Microsoft's HTML Help Workshop),它既可以从命令行调用也可以从GUI界面中被调用,而对于后者来说,你只需要使用任何你喜欢的zip压缩工具将所有的这些文件打包成一个文件,将扩展名修改为.htb就可以了.

当然你可以手动制作你的帮助文件,但是使用一个工具可以节省你的时间.有很多MS HTML帮助文件的制作工具,但是它们有时候会输出不兼容于wxWidgets(不能被wxWidgets解析)的HTML标记.Anthemion Software公司的HelpBlocks软件是目前唯一的可以同时支持MS HTML格式和wxWidgets HTML格式的软件,可以用来帮助你制作帮助文件和分析关键字.

要了解好的帮助文件的结构,最好是看看别人是怎么作的.你可以考虑增加下面的这些主题:内容,欢迎,联系信息,安装信息,注册信息,发布信息,教程,菜单使用帮助,工具条使用帮助,对话框使用帮助(将所有针对对话框的主题作为子标题),快捷键,命令行参数以及故障解决等内容.

记住,应用程序中各个帮助主题的风格最好设计成各自独立的.比如教程主题,最好采用一种比较现代的风格.

其它提供帮助的手段

你也可以考虑使用别的方法给你的用户提供帮助,比如使用wxWidgets的HTML类等.Anthemion Software公司的Writer's Caf�软件使用一个模式对话框来实现一些初始选项,它是用wxHtmlWindow实现的,这些选项包括显示一个快速教程来通过一系列的HTML文件演示这个程序的用途(如下图所示).这样作的好处是不言而喻的,对于您软件的使用新手来说,这样做可以给你的用户提供一个更狭长的学习路径以便你的用户不至于一开始就被浩如烟海的帮助给淹没,从而把自己至于迷失的状态.

另外一个常用的方法是提供每日一学之类的启动提示对话框,这种方法把应用程序的功能分成一个一个的小片,然后每天学习一点点,这很符合某些人的口味.我们在第8章,"使用标准对话框"中已经介绍了怎样使用wxShowTip函数来显示这种帮助,它的参数包括一个父窗口,一个 wxTipProvider指针以便告诉wxWidgets到哪里去寻找那些帮助信息,以及一个boolean变量以便指定除此显示这个对话框的时候,用于给用户选择是否显示这种帮助的复选框的初始选项.如下所示:

#include "wx/tipdlg.h"
...
m_tipProvider = wxCreateFileTipProvider(tipFilename, currentTip);
wxShowTip(parent, m_tipProvider, showAtStart);

上下文敏感帮助和工具提示

应用程序应该尽可能的提供上下文敏感帮助和工具提示.所谓工具提示是指一个很小的提示窗口,这个窗口是当鼠标在某个控件上提留一小段时间的时候探出来的.上下文敏感帮助也和它类似,不过它是由用户先点击某个帮助按钮或者是工具条上的系统按钮,然后再点击他感兴趣的控件来加以显示的.第9 章,"创建定制的对话框"中,对这些内容有比较详细的介绍,在那里我们用来介绍怎样给对话框提供帮助信息,然而这些方法不仅仅只能应用于对话框,它可以应用于任何一种窗口类型.比如用户可以在工具条或者帮助菜单中提供一个"这是什么?"的选项,以便在用户选择这个菜单或者点击这个工具按钮以后,对用户随后点击的窗口控件提供工具提示.你也不必拘泥于系统默认提供的帮助显示方法,你可以自己重载wxHelpProvider类来实现你自己的ShowHelp 函数.

一些控件支持使用更本地化的方法来提供上下文敏感帮助,如果你正在使用wxCHMHelpController控件,你可以使用它来提供上下文敏感的帮助,比如:

#include "wx/cshelp.h"
m_helpController = new wxCHMHelpController;
wxHelpProvider::Set(
      new wxHelpControllerHelpProvider(m_helpController));

wxHelpControllerHelpProvider类的实例将使用其m_helpController成员的DisplayTextPopup函数来提供上下文敏感帮助.

注意,提供上下文敏感帮助和Mac OSX的风格是格格不入的,因此在这个平台你应该忽略这种帮助.

菜单项提示

当你在菜单中加入菜单项的时候,你可以提供一个帮助信息字符串.如果这个菜单是菜单条的一部分,而这个菜单条所在的frame窗口拥有一个状态条,那么当用户鼠标在这个菜单项上划过的时候,这个帮助信息将显示在状态栏上.你可以通过wxFrame::SetStatusBarPane函数来指定显示这个帮助信息的状态条方格(如果设置为-1则将禁止显示帮助信息).这个行为是在wxFrame的默认菜单项事件 EVT_MENU_HIGHLIGHT_ALL的处理函数中实现的,因此你可以拦截这个事件来提供你自己的显示方式,比如将这个帮助信息显示在另外的一个窗口上.