Сегодня я хочу рассказать об одной из интересных функциональных возможностей wxWidgets, а именно, о работе со справочной системой. Библиотека wxWidgets позволяет организовать работу со справочной системой, прилагая минимум усилий. Обеспечение работы со справкой с помощью wxWidgets занимает куда меньше времени, чем написание самой справки.
Также интересным является тот факт, что wxWidgets использует распостраненный формат описания структуры справочной системы, а именно те файлы, которые можно сгенерировать с помощью утилиты HTML Help Workshop. К слову сказать, это достаточно распространенная практика… так, например, инструментарий документирования исходного кода Doxygen также позволяет создавать файлы проектов HTML Help Workshop, с помощью которых можно сгенерировать справку в формате .chm
wxWidgets позволяет организовать работу со справочной системой для приложений, работающих под управлением любой из поддерживаемых операционных систем. А о том, как это всё работает, рассказано ниже.
Для начала создадим простенькое приложение:
HelpTest.cpp
#include <wx/wx.h> class MyFrame : public wxFrame { void CreateControls(); public: MyFrame(); bool Create(wxWindow * parent); DECLARE_EVENT_TABLE() void OnExit(wxCommandEvent & event); }; BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(wxID_EXIT, MyFrame::OnExit) END_EVENT_TABLE() MyFrame::MyFrame() { Create(NULL); } bool MyFrame::Create(wxWindow * parent) { bool res = wxFrame::Create(parent, wxID_ANY, wxT("Help System Test")); if(res) { CreateControls(); } return res; } void MyFrame::CreateControls() { wxMenuBar * menuBar = new wxMenuBar; SetMenuBar(menuBar); wxMenu * fileMenu = new wxMenu; fileMenu->Append(wxID_OPEN, _("Open\tCtrl+O")); fileMenu->Append(wxID_SAVE, _("Save\tCtrl+S")); fileMenu->AppendSeparator(); fileMenu->Append(wxID_EXIT, _("Exit\tAlt+F4")); menuBar->Append(fileMenu, _("File")); wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL); SetSizer(sizer); CreateStatusBar(); } void MyFrame::OnExit(wxCommandEvent & event) { Close(); } class MyApp : public wxApp { virtual bool OnInit() { MyFrame * frame = new MyFrame; SetTopWindow(frame); frame->Centre(); frame->Show(); return true; } }; IMPLEMENT_APP(MyApp);
Начнем с самого простого. А проще всего реализовать контекстную справку для диалоговых окон. Для этого внесем некоторые изменения в наш код:
HelpTest.cpp
... #include <wx/cshelp.h> #include <wx/html/helpctrl.h> class MyDialog : public wxDialog { public: MyDialog(wxWindow * parent) : wxDialog(parent, wxID_ANY, _("Modal dialog")) { SetExtraStyle(wxDIALOG_EX_CONTEXTHELP); SetSizer(new wxBoxSizer(wxVERTICAL)); wxTextCtrl * text = new wxTextCtrl(this, wxNewId(), wxT("Some text"), wxDefaultPosition, wxSize(300, 100), wxTE_MULTILINE); text->SetHelpText(_("You can type some tex here")); wxBoxSizer * buttonSizer = new wxBoxSizer(wxHORIZONTAL); wxButton* btnOK = new wxButton(this, wxID_OK, _T("&OK")); btnOK->SetHelpText(_("The OK button confirms the dialog choices.")); wxButton* btnCancel = new wxButton(this, wxID_CANCEL, _T("&Cancel")); btnCancel->SetHelpText(_("The Cancel button cancels the dialog.")); buttonSizer->Add(btnOK, 0, wxALIGN_CENTER | wxALL, 5); buttonSizer->Add(btnCancel, 0, wxALIGN_CENTER | wxALL, 5); // Add explicit context-sensitive help button for non-MSW #ifndef __WXMSW__ buttonSizer->Add(new wxContextHelpButton(this), 0, wxALIGN_CENTER|wxALL, 5); #endif GetSizer()->Add(text, 1, wxEXPAND|wxALL, 5); GetSizer()->Add(buttonSizer, 0, wxALIGN_RIGHT, 5); GetSizer()->Fit(this); Centre(); } }; class MyFrame : public wxFrame { ... DECLARE_EVENT_TABLE() void OnDialogContextHelp(wxCommandEvent & event); ... }; enum { ID_DIALOG_CONTEXT_HELP = 10001 }; BEGIN_EVENT_TABLE(MyFrame, wxFrame) ... EVT_MENU(ID_DIALOG_CONTEXT_HELP, MyFrame::OnDialogContextHelp) 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")); ... menuBar->Append(fileMenu, _("File")); ... } ... void MyFrame::OnDialogContextHelp(wxCommandEvent & event) { MyDialog dlg(this); dlg.ShowModal(); } ... class MyApp : public wxApp { virtual bool OnInit() { wxHelpControllerHelpProvider* provider = new wxHelpControllerHelpProvider; wxHelpProvider::Set(provider); ... return true; } };
Как видно, мы создали класс диалогового окна, производного от wxDialog
и поместили на диалоговое окно текстовое поле и кнопки.
Для обеспечения поддержки контекстной справки для элементов диалога, нам необходимо установить нашему диалоговому окну стиль wxDIALOG_EX_CONTEXTHELP
. Этот стиль работает только для ОС семейства Windows, для всех остальных операционных систем нам необходимо создавать кнопку для вызова контекстной справки. Для этой цели используем класс wxContextHelpButton
. Далее необходимо, также, установить тектст справочного сообщения для каждого элемента управления с помощью wxWindow::SetHelpText
.
Поддержка контекстной справки обеспечивается в wxWidgets посредством использования класса wxHelpControllerHelpProvider
. Для того, чтобы всё заработало, мы при инициализации приложения создаем новый провайдер контекстной справки и делаем его активным при помощи метода wxHelpProvider::Set
.
Теперь собираем наш пример и смотрим, что получилось.
Отлично. Оно даже заработало.
Теперь можно приступить к более сложной части
Создадим справку для нашего приложения в формате HTML.
Для создания CHM-файла будем использовать HTML Help Workshop
Создадим новый проект
Назовем его HelpTest
После этого нам необходимо указать, что проект создается на основе уже существующих файлов
Теперь нам необходимо добавить в проект уже созданные HTML-файлы
Вот так должно выглядеть окно настроек проекта
Теперь нам необходимо создать структуру проекта справки. Переходим на вкладку “Contents” и указываем, что необходимо создать новый файл с описанием структуры проекта
Теперь создаем иерархию справочной системы. Для этого пользуемся кнопками на панели инструментов слева. Для добавления страницы справки используем кнопку “Insert a page”.
В диалоговом окне “Table of Contents Entry” необходимо указать отображаемое название страницы и указать соответствующий HTML-файл, нажав кнопку “Add…”.
В диалоговом окне “Path or URL” нужно выбрать файл из списка и нажать OK
Для создания группы пользуемся кнопкой “Insert a heading”
После того, как мы добавили все необходимые страницы, окно структуры проекта должно выглядеть примерно вот так:
Теперь нам необходимо создать индекс справки. Для этого переходим на вкладку Index и указываем, что нужно создать новый файл индекса.
Для добавления нового ключевого слова пользуемся кнопкой “Insert a keyword”
В окне “Index Entry” необходимо указать ключевое слово и выбрать соответствующий HTML-файл, нажав кнопку “Add…”
В окне “Path or URL” не обходимо выбрать файл из списка и нажать “OK”
После того, как мы добавили все ключевые слова, вкладка индекса должна иметь примерно вот такой вид.
На вкладке “Project” нажимеам кнопку “Project Options”, указываем название проекта и выбираем файл, отображаемый по умолчанию при открытии окна справки.
Теперь нам нужно собрать CHM-файл. Для этого жмем “Compile HTML file” и ждем завершения компиляции.
После того, как все успешно собралось, в журнале компиляции должна появиться статистика по проекту: количество тем, внешних и внутренних ссылок, рисунков.
Теперь можно запустить наш CHM-файл и посмотреть, что получилось.
После того, как мы сохранили наш проект справки, в каталоге с файлами должны появиться .hpp, .hhk, .hhc и .chm файлы
Отлично. CHM-файл у нас готов, теперь можно приступать к внесению изменений в исходный код приложения. Но сначала немного теории.
Для создания справочной системы в wxWidgets используется концепция контроллеров справки, которые позволяют загружать файлы справки и отображать их содержимое.
wxWinHelpController
– используется для работы с файлами справки в формате .hlp Этот формат справки считается устаревшим, поэтому данный контроллер спавки желательно не использоватьwxCHMHelpController
– для работы с файлами справки в формате .chm Этот контроллер справки доступен для использования только под управлением ОС Windows.wxHtmlHelpController
– доступен для всех платформ. Использует файлы .zipВсе контроллеры справки кроме wxHtmlHelpController
являются классами-обёртками над “родной” справочной системой для конкретной ОС и используют внешние средства просмотра файлов справки. В случае же wxHtmlHelpController
, просмотр справки осуществляется внутри процесса, и все окна справки будут закрыты по завершению приложения их вызвавшего. Если есть необходимость использовать внешний просмотрищик, то нужно воспользоваться утилитой HelpView ( utils/src/helpview ) и классом wxRemoteHtmlHelpController
, описанным в файлах remhelp.h и remhelp.cpp, который позволяет управлять утилитой HelpView.
Итак, давайте посмотрим, как это всё работает на примере wxCHMHelpController (предварительно скопировав созданный ранее файл HelpTest.chm в папку с программой).
HelpTest.cpp
#include <wx/wx.h> #include <wx/textdlg.h> #include <wx/aboutdlg.h> #include <wx/cshelp.h> #include <wx/html/helpctrl.h> class MyDialog : public wxDialog { public: MyDialog(wxWindow * parent) : wxDialog(parent, wxID_ANY, _("Modal dialog")) { SetExtraStyle(wxDIALOG_EX_CONTEXTHELP); SetSizer(new wxBoxSizer(wxVERTICAL)); wxTextCtrl * text = new wxTextCtrl(this, wxNewId(), wxT("Some text"), wxDefaultPosition, wxSize(300, 100), wxTE_MULTILINE); text->SetHelpText(_("You can type some tex here")); wxBoxSizer * buttonSizer = new wxBoxSizer(wxHORIZONTAL); wxButton* btnOK = new wxButton(this, wxID_OK, _T("&OK")); btnOK->SetHelpText(_("The OK button confirms the dialog choices.")); wxButton* btnCancel = new wxButton(this, wxID_CANCEL, _T("&Cancel")); btnCancel->SetHelpText(_("The Cancel button cancels the dialog.")); buttonSizer->Add(btnOK, 0, wxALIGN_CENTER | wxALL, 5); buttonSizer->Add(btnCancel, 0, wxALIGN_CENTER | wxALL, 5); #ifndef __WXMSW__ buttonSizer->Add(new wxContextHelpButton(this), 0, wxALIGN_CENTER | wxALL, 5); #endif GetSizer()->Add(text, 1, wxEXPAND|wxALL, 5); GetSizer()->Add(buttonSizer, 0, wxALIGN_RIGHT, 5); GetSizer()->Fit(this); Centre(); } }; class MyFrame : public wxFrame { void CreateControls(); #if wxUSE_MS_HTML_HELP && !defined(__WXUNIVERSAL__) wxCHMHelpController m_msHtmlHelp; #endif public: MyFrame(); bool Create(wxWindow * parent); wxHelpController * GetHelpController() { return &m_msHtmlHelp; } DECLARE_EVENT_TABLE() void OnDialogContextHelp(wxCommandEvent & event); void OnAbout(wxCommandEvent & event); void OnExit(wxCommandEvent & event); void OnShowHelpContents(wxCommandEvent & event); void OnShowSection_FileMenu(wxCommandEvent & event); void OnKeywordSearch(wxCommandEvent & event); }; enum { ID_DIALOG_CONTEXT_HELP = 10001, ID_SHOW_SECTION_FILEMENU, ID_KEYWORD_SEARCH }; BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(wxID_EXIT, MyFrame::OnExit) EVT_MENU(wxID_ABOUT, MyFrame::OnAbout) EVT_MENU(wxID_HELP_CONTENTS, MyFrame::OnShowHelpContents) EVT_MENU(ID_SHOW_SECTION_FILEMENU, MyFrame::OnShowSection_FileMenu) EVT_MENU(ID_DIALOG_CONTEXT_HELP, MyFrame::OnDialogContextHelp) EVT_MENU(ID_KEYWORD_SEARCH, MyFrame::OnKeywordSearch) END_EVENT_TABLE() MyFrame::MyFrame() { Create(NULL); } bool MyFrame::Create(wxWindow * parent) { bool res = wxFrame::Create(parent, wxID_ANY, wxT("Help System Test")); if(res) { CreateControls(); } return res; } void MyFrame::CreateControls() { wxMenuBar * menuBar = new wxMenuBar; SetMenuBar(menuBar); wxMenu * fileMenu = new wxMenu; fileMenu->Append(wxID_OPEN, _("Open\tCtrl+O")); fileMenu->Append(wxID_SAVE, _("Save\tCtrl+S")); fileMenu->AppendSeparator(); fileMenu->Append(wxID_EXIT, _("Exit\tAlt+F4")); 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...")); menuBar->Append(fileMenu, _("File")); menuBar->Append(helpMenu, _("Help")); wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL); SetSizer(sizer); CreateStatusBar(); } void MyFrame::OnShowHelpContents(wxCommandEvent & event) { m_msHtmlHelp.DisplayContents(); } void MyFrame::OnShowSection_FileMenu(wxCommandEvent & event) { m_msHtmlHelp.DisplaySection(wxT("File")); } void MyFrame::OnKeywordSearch(wxCommandEvent & event) { wxString keyword = wxGetTextFromUser(_("Keyword Search")); if(!keyword.IsEmpty()) { m_msHtmlHelp.KeywordSearch(keyword); } } void MyFrame::OnDialogContextHelp(wxCommandEvent & event) { MyDialog dlg(this); dlg.ShowModal(); } void MyFrame::OnAbout(wxCommandEvent & event) { wxAboutDialogInfo info; info.SetName(_("HelpTest")); info.SetVersion(_("0.0.1")); info.SetDescription(_("This program does something great.")); info.SetCopyright(_T("(C) 2007 Volodymir (T-Rex) Tryapichko https://wxwidgets.info")); wxAboutBox(info); } void MyFrame::OnExit(wxCommandEvent & event) { Close(); } class MyApp : public wxApp { virtual bool OnInit() { wxHelpControllerHelpProvider* provider = new wxHelpControllerHelpProvider; wxHelpProvider::Set(provider); MyFrame * frame = new MyFrame; #if wxUSE_MS_HTML_HELP && !defined(__WXUNIVERSAL__) provider->SetHelpController(frame->GetHelpController()); if( !frame->GetHelpController()->Initialize(_T("HelpTest")) ) { wxLogError(wxT("Cannot initialize the MS HTML Help system.")); } #endif SetTopWindow(frame); frame->Centre(); frame->Show(); return true; } }; IMPLEMENT_APP(MyApp);
Что же делает наш пример:
Help -> Contents
– вызывает окно справки и отображает список темHelp -> Show help on “File” menu
– вызывает окно справки и отображает тему "File"Help -> Keyword search
– позволяет ввести ключевое слово или фразу, а затем вызывает окно справки и выполняет поиск по введенной фразе.Отображение списка тем производится с помощью метода wxHelpController::DisplayContents
.
Ваше приложение может, также, иметь другие важные страницы справки. Для отображения конкретной страницы справки используется метод wxHelpController::DisplaySection
, который может принимать в качестве параметра название страницы, числовой идентификатор страницы или имя HTML-файла.
Для выполнения поиска по ключевому слову используется метод wxHelpController::KeywordSearch, который принимает в качестве параметра ключевое слово или фразу.
Теперь можно попробовать создать кросс-платформенный вариант справочной системы. Для этого нам не обходимо все HTML-файлы, рисунки, и файлы созданные HTML Help Workshop запаковать в ZIP-архив и перенести этот архив в папку с программой, а также внести некоторые изменения в исходный код нашого приложения, которое теперь будет использовать класс wxHtmlHelpController и пересобрать его.
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/filesys.h" #include "wx/fs_zip.h" class MyDialog : public wxDialog { public: MyDialog(wxWindow * parent) : wxDialog(parent, wxID_ANY, _("Modal dialog")) { SetExtraStyle(wxDIALOG_EX_CONTEXTHELP); SetSizer(new wxBoxSizer(wxVERTICAL)); wxTextCtrl * text = new wxTextCtrl(this, wxNewId(), wxT("Some text"), wxDefaultPosition, wxSize(300, 100), wxTE_MULTILINE); text->SetHelpText(_("You can type some tex here")); wxBoxSizer * buttonSizer = new wxBoxSizer(wxHORIZONTAL); wxButton* btnOK = new wxButton(this, wxID_OK, _T("&OK")); btnOK->SetHelpText(_("The OK button confirms the dialog choices.")); wxButton* btnCancel = new wxButton(this, wxID_CANCEL, _T("&Cancel")); btnCancel->SetHelpText(_("The Cancel button cancels the dialog.")); buttonSizer->Add(btnOK, 0, wxALIGN_CENTER | wxALL, 5); buttonSizer->Add(btnCancel, 0, wxALIGN_CENTER | wxALL, 5); #ifndef __WXMSW__ buttonSizer->Add(new wxContextHelpButton(this), 0, wxALIGN_CENTER | wxALL, 5); #endif GetSizer()->Add(text, 1, wxEXPAND|wxALL, 5); GetSizer()->Add(buttonSizer, 0, wxALIGN_RIGHT, 5); GetSizer()->Fit(this); Centre(); } }; class MyFrame : public wxFrame { wxHtmlHelpController m_msHtmlHelp; void CreateControls(); public: ... wxHtmlHelpController * GetHelpController() { return &m_msHtmlHelp; } ... }; ... class MyApp : public wxApp { virtual bool 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.")); } SetTopWindow(frame); frame->Centre(); frame->Show(); return true; } }; IMPLEMENT_APP(MyApp);
После всех проделанных изменений, окно справки под управлением ОС Windows будет выглядеть подобным образом:
А вот так оно будет выглядеть при работе в ОС Linux:
Ну вот, пока всё. Продолжение темы будет позже ;)
Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень…
I can see that there is still a lot of topics at wxWidgets forums related…
I've just published the source code of wxToolBox component and a couple of sample apps at…
Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are…
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to…
Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job…