Categories: wxWidgets

Кросс-платформенная справочная система для кросс-платформенных приложений – Часть II

Итак, продолжаем изучать процесс создания справки для приложений, разработанных с использованием wxWidgets. В этот раз я хочу рассказать о нескольких интересных методах встраивания справки в приложение.
Начнем, пожалуй, с того, что wxWidgets помимо стандартной возможности открытия справки в отдельном окне, позволяет отображать справку прямо в окне приложения без каких-либо дополнительных усилий со стороны разработчика.

Возьмем пример из статьи “Кросс-платформенная справка для кросс-платформенных приложений – Часть 1” и внесем некоторые изменения:

HelpTest.cpp

class MyFrame : public wxFrame
{
 wxHtmlHelpController     m_msHtmlHelp;
 wxHtmlHelpWindow * m_HtmlHelpWindow;
 wxNotebook * m_NavigationNotebook;
 ...
public:
 ...
};
...
MyFrame::MyFrame()
: m_msHtmlHelp(wxHF_EMBEDDED|wxHF_DEFAULT_STYLE)
{
 Create(NULL);
}
...
void MyFrame::CreateControls()
{
 ...
 wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
 SetSizer(sizer);

 m_NavigationNotebook = new wxNotebook(this, wxID_ANY);

 m_HtmlHelpWindow = new wxHtmlHelpWindow(m_NavigationNotebook, wxID_ANY);
 wxPanel * panel = new wxPanel(m_NavigationNotebook, wxID_ANY);
 m_NavigationNotebook->AddPage(panel, _("Workspace"), true);
 m_NavigationNotebook->AddPage(m_HtmlHelpWindow, _("Help"), false);

 m_msHtmlHelp.SetHelpWindow(m_HtmlHelpWindow);

 sizer->Add(m_NavigationNotebook, 1, wxEXPAND);

 CreateStatusBar();
}
...
void MyFrame::OnShowHelpContents(wxCommandEvent & event)
{
 if(m_msHtmlHelp.DisplayContents())
 {
  m_NavigationNotebook->SetSelection(1);
 }
}

void MyFrame::OnShowSection_FileMenu(wxCommandEvent & event)
{
 if(m_msHtmlHelp.DisplaySection(wxT("File")))
 {
  m_NavigationNotebook->SetSelection(1);
 }
}

void MyFrame::OnKeywordSearch(wxCommandEvent & event)
{
 wxString keyword = wxGetTextFromUser(_("Keyword Search"));
 if(!keyword.IsEmpty())
 {
  if(m_msHtmlHelp.KeywordSearch(keyword))
  {
   m_NavigationNotebook->SetSelection(1);
  }
 }
}
...

Как видно, изменений совсем немного: мы создали компонент wxNotebook и поместили на него две вкладки. Первая вкладка – рабочая, а вторая – содержит встроенное окно справки. Для того, чтобы наш контроллер справки мог работать со встроенными окнами, мы установили для него стиль wxHF_EMBEDDED, затем создали объект wxHtmlHelpWindow и выполнили его привязку к нашему контроллеру справки посредством метода wxHtmlHelpController::SetHelpWindow. В обработчиках событий меню, мы проверяем, правильно ли отработал вызов справки и в случае успешного открытия нужной страницы, делаем активным вкладку с окном справки.
После всех изменений, главное окно нашего приложения выглядит так:

Как уже было сказано ранее, существует еще возможность отображения справки с помощью утилиты helpview (utils\helpview\src), но, оказалось, сделать это уже сложнее и тому есть несколько причин.
Причина первая. Как это ни странно, но исходные коды утилиты helpview немного устарели и для того чтобы ее собрать, необходимо “доработать напильником”.
Причина вторая. Исходный код класса wxRemoteHtmlHelpController завязан на исходном коде примера, да и к тому же, написан без поддержки Unicode, что меня лично очень огорчило, ибо уже достаточно давно не пользуюсь ANSI-сборкой wxWidgets.
Причина третья. Файлы проектов для утилиты helpview и тестового примера имеют UNIX-окончания строк, что делает невозможным их использование в Visual Studio без предварительных танцев с бубном.

Но не все так страшно, если подходить к решению проблемы с умом. Проблему с окончаниями строк в файлах проектов, которая вводит в ступор многих новичкв, можно решить следующим образом:

  • Открыть файл проекта в броузере
  • Скопировать весь текст
  • Открыть файл проекта в блокноте
  • Вставить скопированный из броузера текст
  • Сохранить

Проблема с поддержкой Unicode решается использованием макросов wxT() и _() для всех строковых констант и использованием wxString::GetData() вместо wxString::c_str().

Ну и, собственно, проблема зависимости от исходного кода примера решается добавлением новых параметров в конструктор класса контроллера справки.

Исправленный исходный код класса wxRemoteHtmlHelpController, а также исходный код примера, Вы можете найти в конце статьи.

Для использования класса wxRemoteHtmlHelpController в своем приложении, необходимо включить в проект файлы remhelp.h и remhelp.cpp

Теперь внесем изменения в исходный код нашего примера:

HelpTest.cpp

#include <wx/wx.h>
#include <wx/textdlg.h>
#include <wx/aboutdlg.h>

#include <wx/cshelp.h>
#include <wx/html/helpctrl.h>
#include <wx/html/helpwnd.h>

#include "wx/filesys.h"
#include "wx/fs_zip.h"

#include "remhelp.h"

class MyApp : public wxApp
{
 wxRemoteHtmlHelpController * m_RemoteHelpController;
public:
 wxRemoteHtmlHelpController * GetRemoteHelpController()
 {
  return m_RemoteHelpController;
 }
 virtual bool OnInit();
 virtual int OnExit();
};

DECLARE_APP(MyApp);
IMPLEMENT_APP(MyApp);
...
class MyFrame : public wxFrame
{
 ...
 DECLARE_EVENT_TABLE()
 ...
 void OnShowHelpAboutRemote(wxCommandEvent & event);
};

enum
{
 ID_DIALOG_CONTEXT_HELP = 10001,
 ID_SHOW_SECTION_FILEMENU,
 ID_KEYWORD_SEARCH,
 ID_HELP_ABOUT_REMOTE
};

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
...
EVT_MENU(ID_HELP_ABOUT_REMOTE, MyFrame::OnShowHelpAboutRemote)
END_EVENT_TABLE()
...
void MyFrame::CreateControls()
{
 wxMenuBar * menuBar = new wxMenuBar;
 SetMenuBar(menuBar);
 ...
 wxMenu * helpMenu = new wxMenu;
 helpMenu->Append(ID_DIALOG_CONTEXT_HELP, _("Dialog context help"));
 helpMenu->Append(wxID_HELP_CONTENTS, _("Contents"));
 helpMenu->Append(ID_SHOW_SECTION_FILEMENU, _("Show help on \"File\" menu"));
 helpMenu->Append(ID_KEYWORD_SEARCH, _("Keyword search"));
 helpMenu->AppendSeparator();
 helpMenu->Append(wxID_ABOUT, _("About..."));
 helpMenu->Append(ID_HELP_ABOUT_REMOTE, _("About... (Remote)"));

 menuBar->Append(fileMenu, _("File"));
 menuBar->Append(helpMenu, _("Help"));
...
}
...
void MyFrame::OnShowHelpAboutRemote(wxCommandEvent & event)
{
 wxGetApp().GetRemoteHelpController()->Display(wxT("about.html"));
}
...
bool MyApp::OnInit()
{
 wxFileSystem::AddHandler(new wxZipFSHandler);

 wxHelpControllerHelpProvider* provider = new wxHelpControllerHelpProvider;
 wxHelpProvider::Set(provider);

 MyFrame * frame = new MyFrame;

 provider->SetHelpController(frame->GetHelpController());
 if( !frame->GetHelpController()->AddBook(wxFileName(wxT("HelpTest.zip"))))
 {
  wxLogError(wxT("Cannot initialize the Help system."));
 }
 m_RemoteHelpController = new wxRemoteHtmlHelpController(this);
#if defined(__WXMSW__)
 wxString server = wxT("helpview.exe");
 wxString service = wxT("generic_helpservice");
#else
 wxString server = wxT("./helpview");
 wxString service = wxT("/tmp/wxWidgets-helpconnection");
#endif
 m_RemoteHelpController->SetServer(server);
 m_RemoteHelpController->SetService(service);
 if(!m_RemoteHelpController->AddBook(wxT("HelpTest.zip")))
 {
  wxLogError(wxT("Cannot initialize the Remote Help system."));
 }
 SetTopWindow(frame);
 frame->Centre();
 frame->Show();
 return true;
}

int MyApp::OnExit()
{
 wxDELETE(m_RemoteHelpController);
 return wxApp::OnExit();
}

Теперь смотрим, какие же мы внесли изменения:
Мы добавили в класс приложения новую переменную типа wxRemoteHtmlHelpController. Произвели настройку соединения с утилитой helpview с помощью методов wxRemoteHtmlHelpController::SetServer и wxRemoteHtmlHelpController::SetService. Добавили новый пункт меню "About... (Remote)" и создали для него обработчик, в котором с помощью метода wxRemoteHtmlHelpController::Display отображаем страницу с правки с помощью утилиты helpview.

Еще одним популярным методом донесения справочной информации до конечного пользователя является использование диалога "Tip of the Day", с помощью которого можно, например, рассказать о ключевых особенностях программного продукта. Использование этого метода в wxWidgets также не занимает много усилий. С помощью функции wxShowTip, которая принимает в качестве параметров указатель на родительское окно и указатель на объект wxTipProvider, можно буквально несколькими строками кода организовать подобный функционал в своем приложении. Ну вот, пора снова править наш пример:

HelpTest.cpp

...
#include <wx/tipdlg.h>
#include <wx/config.h>
...
class MyApp : public wxApp
{
...
 void ShowTip(wxWindow * parent, bool bShowTip, wxConfigBase * config = NULL);
};
...
class MyFrame : public wxFrame
{
 ...
 void OnShowTip(wxCommandEvent & event);
};

enum
{
 ID_DIALOG_CONTEXT_HELP = 10001,
 ID_SHOW_SECTION_FILEMENU,
 ID_KEYWORD_SEARCH,
 ID_HELP_ABOUT_REMOTE,
 ID_SHOW_TIP
};

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
...
EVT_MENU(ID_SHOW_TIP, MyFrame::OnShowTip)
END_EVENT_TABLE()
...
void MyFrame::CreateControls()
{
 ...
 helpMenu->AppendSeparator();
 helpMenu->Append(ID_SHOW_TIP, _("Tip of the Day"));
 helpMenu->AppendSeparator();
 helpMenu->Append(wxID_ABOUT, _("About..."));
 ...
}

void MyFrame::OnShowTip(wxCommandEvent & event)
{
 wxConfig * config = new wxConfig(wxGetApp().GetAppName());
 wxGetApp().ShowTip(this, false, config);
 wxDELETE(config);
}
...
bool MyApp::OnInit()
{
 ...
 wxConfig * config = new wxConfig(GetAppName());
 if(config)
 {
  bool bShowTip(false);
  if(!config->Read(wxT("ShowTip"), &bShowTip))
  {
   bShowTip = true;
  }
  if(bShowTip)
  {
   ShowTip(frame, bShowTip, config);
  }
 }
 wxDELETE(config);
 return true;
}

void MyApp::ShowTip(wxWindow * parent, bool bShowTip, wxConfigBase * config)
{
 wxString tipPath = GetAppName()+wxT(".txt");
 wxTipProvider * provider = wxCreateFileTipProvider(tipPath, 0);
 bShowTip = wxShowTip(parent, provider, bShowTip);
 wxDELETE(provider);
 if(config)
 {
  config->Write(wxT("ShowTip"), bShowTip);
  config->Flush();
 }
}

Помимо модификации исходного кода приложения, нам необходимо создать текстовый файл, в котором будет храниться текст диалога "Tip of the Day". Каждая строка в этом текстовом файле соответствует странице справки для нашего диалога. Еще интересной возможностью является использование макросов wxT() и _() внутри текстового файла. Так, строки, заключенные внутрь макроса _() будут автоматически переведены при смене языка.

Ну вот, все изменения произведены. Смотрим, что у нас получилось.

Скачать исходный код примера

T-Rex

Share
Published by
T-Rex

Recent Posts

Разработка кроссплатформенных модульных приложений на C++ с библиотекой wxWidgets

Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень…

10 years ago

wxWidgets App With Plugins (Windows/Linux/Mac) – Sample Source Code

I can see that there is still a lot of topics at wxWidgets forums related…

11 years ago

wxToolBox is Now Open-Source!

I've just published the source code of wxToolBox component and a couple of sample apps at…

11 years ago

Microsoft Kinect Helper Library and Sample for wxWidgets

Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are…

13 years ago

wxJSON 1.1.0 Released

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to…

14 years ago

wxRuby. Оно даже работает!

Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job…

15 years ago