Ю.В.Земсков. Программирование на C++ с использованием библиотеки Qt4

Программирование на C++ с использованием библиотеки wxWidgets

Титульный лист
Основные сведения
Простейшее приложение wxWidgets
Установка wxWidgets
Компиляция приложений wxWidgets
Меню и строка состояния
Обработка событий в приложениях wxWidgets
Разработка GUI в программе DialogBlocks
Дата и время в строке состояния
Отладка приложений wxWidgets
Основные элементы интерфейса
Диалог подключения к информационной системе
Менеджеры размещения
Конфигурационный файл приложения


Юрий Владимирович Земсков

z1-soft@yandex.ru

Программирование на C++

с использованием библиотеки wxWidgets

г.Волгоград

2007 г.


Ю.В.Земсков. Программирование на C++ с использованием библиотеки wxWidgets

Введение

Эта книга посвящена разработке программ, которые могут работать на разных платформах и в различных операционных системах: MS Windows, Unix/Lunux/FreeBSD, Mac OS, мобильных устройствах и т. п. В качестве языка разработки используется C++. Для реализации графического интерфейса программ, приведённых в этой книге, за основу взяты библиотеки wxWidgets и Qt 4.

Библиотека wxWidgets для C++ и её реализации для десятка других языков программирования (Python, Ruby, Java, BASIC, C#, PHP и др.) -- это профессиональный инструмент создания межплатформенных приложений, о чём свидельствует возросшее количество программных продуктов (в том числе коммерческих), появившихся на мировом рынке за последние несколько лет. Но в настоящее время отсутствуют какие бы то ни было источники информации по этим библиотекам на русском языке, да и на английском соответствующая литература начала издаваться только недавно: первая английская книга по wxWidgets вышла в 2005 году (Smart J. и др.).

Что касается Qt, то читатель, знакомый с миром Linux, вероятно, знает, что эта библиотека является основой для популярной графической среды пользователя KDE (которая соперничает с не менее известной Gnome). Но кроме решения системных задач, Qt идеально подходит для разработки межплатформенных приложений, поскольку использует свой собственный, не зависимый от системы, набор элементов графического интерфейса пользователя. Поэтому приложение Qt на любой из поддерживаемых платформ выглядит и работает одинаково.

Особое внимание уделено вопросам разработки приложений баз данных и автоматизации бухгалтерского и управленческого учёта, а также разработке программ, конфигурируемых и расширяемых с помощью макроязыка, подобного языку Visual Basic для продуктов MS Office или встроенному языку комплекса программ 1С:Предприятие.

Книга рассчитана на читателей, имеющих хотя бы минимальный опыт разработки программ на языке C++ для Windows или Linux, также будет полезна студентам-программистам и всем, кого интересуют вопросы межплатформенной разработки приложений баз данных.

Основные сведения о библиотеке wxWidgets

wxWigdets -- библиотека с открытым исходным кодом для разработки межплатформенных программ (в том числе и коммерческих). Работает в системах X Window, Mac OS, MS Windows, OS/2. Там, где это возможно, использует родные (нативные) элементы интерфейса целевой платформы. Поэтому одна и та же программа, скомпилированная для разных систем, выглядит по-разному, как будто каждая её версия разрабатывалась отдельно, с использованием "родных" для этой системы библиотек. Развивается также проект wxUniversal для платформ, не имеющих своего API с поддержкой элементов графического интерфейса. Разумеется, его можно применять и там, где где такая поддержка есть, но по каким-либо причинам её использование нежелательно.

wxWidgets построена на C++: практически все функции, предназначенные для создания графического интерфейса, оформлены как методы соответствующих классов. Пользовательская реакция на события также должна быть методом класса. В то же время, для передачи событий, используются числовые коды (как в Windows). Оба подхода примиряются за счёт широкого использования макросов, скрывающих от пользователя истинную кухню работы с событиями. Вообще, программировать с помощью библиотеки wxWidgets будет достаточно комфортно тем, кто знаком с MFC (Microsoft Foundation Classes), т.к. оба инструментария достаточно похожи, только wxWidgets позволяет разрабатывать программы для различных операционных систем и, в отличие от MFC, в настоящее время активно развивается.

Что требуется для создания приложений на основе библиотеки wxWidgets

В операционной системе Microsoft Windows:

  • 32- или 64-разрядный компьютер с установленной операционной системой MS Windows 98/2000/XP.
  • Любой из перечисленных компиляторов: MS Visual C++, Borland C++, Watcom C++, Cygwin, MinGW, Metrowerks CodeWarrior, Digital Mars C++.

В системах Unix/Linux/FreeBSD:

  • Любой компилятор C++, в том числе GNU C++.
  • Практически любой компьютер с установленной системой Unix/Linux/FreeBSD.
  • Любой из перечисленных графических менеджеров: GTK+ 1.2, GTK+ 2.0, Motif 1.2 или выше, Lesstif.

В системе Mac OS/Mac OS X:

  • Компьютер PowerPC Mac с установленной системой Mac OS 8.6/9.x или Mac OS X 10.x.
  • Компилятор CodeWarrior 5.3, 6 или 7 для Mac OS или Apple Developer Tools (например, GNU C++), CodeWarrior 7 или выше для Mac OS X.
Во всех случаях необходимо по крайней мере 100 Мбайт на жёстком диске.

Кроме того, для начинающих можно порекомендовать простую среду разработки программ Anthemion DialogBlocks, доступную для всех перечисленных выше систем (чуть позже мы рассмотрим эту программу подробнее).

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

Обзор классов библиотеки wxWidgets

Чтобы читатель имел хотя бы общее представление о возможностях библиотеки wxWidgets, перечислим её основные классы:
  • Окна, которые могут управляться оконным менеджером операционной системы:
    • wxTopLevelWindow -- окно верхнего уровня (диалог или фрейм);
    • wxDialog -- диалог (может быть модальным);
    • wxFrame -- фрейм (всегда немодальный);
    • wxMDIChildFrame -- дочерний фрейм интерфейса MDI;
    • wxMDIParentFrame -- родительский фрейм интерфейса MDI;
    • wxMiniFrame -- минифрейм (с узкой строкой заголовка);
    • wxSplashScreen -- заставка (обычно выводится при старте приложения);
    • wxPropertySheetDialog -- диалог для редактирования свойств объекта;
    • wxTipWindow -- небольшое окно с подсказкой;
    • wxWizard, wxWizardPage и wxWizardPageSimple -- мастер и страницы мастера.
  • Контейнеры для элементов (наследники класса wxWindow):
    • wxPanel -- панель;
    • wxScrolledWindow -- окно с полосами прокрутки;
    • wxGrid -- таблица;
    • wxSplitterWindow -- окно, которое может быть разделено по вертикали или горизонтали;
    • wxStatusBar -- строка состояния;
    • wxToolBar -- панель инструментов;
    • wxNotebook -- многостраничный интерфейс;
    • wxListbook -- как wxNotebook, но для переключения страниц используется wxListCtrl;
    • wxChoicebook -- как wxNotebook, но для переключения страниц используется wxChoice;
    • wxSashWindow -- окно, имеющее подвижный край;
    • wxSashLayoutWindow -- то же, что и wxSashWindow, но с пользовательским алгоритмом размещения;
    • wxVScrolledWindow -- как wxScrolledWindow, но прокручиваемом окне могут использоваться строки различной высоты.
  • Стандартные диалоги (наследники класса wxDialog):
    • wxColourDialog -- диалог для выбора цвета;
    • wxDirDialog -- диалог для выбора каталога;
    • wxFileDialog -- диалог для выбора файла;
    • wxFindReplaceDialog -- диалог для поиска/замены текста;
    • wxMultiChoiceDialog -- диалог для выбора одного или нескольких элементов списка;
    • wxSingleChoiceDialog -- диалог для выбора одного элемента списка;
    • wxTextEntryDialog -- диалог для ввода строки;
    • wxPasswordEntryDialog -- диалог для ввода пароля;
    • wxFontDialog -- диалог для выбора шрифта;
    • wxPageSetupDialog -- диалог для установки параметров страницы перед печатью;
    • wxPrintDialog -- диалог для печати документа;
    • wxProgressDialog -- диалог для отображения выполнения длительного процесса;
    • wxMessageDialog -- диалог для вывода сообщения.
  • Элементы управления:
    • wxWindow -- базовый класс для всех визуальных элементов библиотеки wxWidgets;
    • wxControl -- базовый класс для всех элементов управления;
    • wxButton -- кнопка с текстовой надписью;
    • wxBitmapButton -- кнопка с изображением вместо текста;
    • wxToggleButton -- кнопка с двумя устойчивыми состояниями (нажато/отжато).
    • wxCalendarCtrl -- календарь (для показа и выбора даты);
    • wxCheckBox -- пометка, чекбокс (выбрано/не выбрано);
    • wxControlWithItems -- виртуальный класс, на основе которого построены элементы wxListBox, wxCheckListBox, wxChoice и wxComboBox;
    • wxCheckListBox -- список, строки которого можно помечать;
    • wxChoice -- список для выбора (без поля ввода);
    • wxComboBox -- поле ввода с раскрывающимся (или всё время отображающимся) списком;
    • wxDatePickerCtrl -- небольшой элемент управления для выбора даты;
    • wxGauge -- для отображения ходя выполнения (процента завершённости) длительного процесса;
    • wxGenericDirCtrl -- дерево каталогов;
    • wxHtmlListBox -- список, элементы которого допускают форматирование с помощью тегов HTML;
    • wxStaticBox -- рамка с надписью вокруг группы элементов;
    • wxListBox -- список строк для выбора одной или нескольких из них;
    • wxListCtrl -- список строк и/или иконок с различными режимами отображения (в том числе со многими столбцами и возможностью редактирования пунктов);
    • wxListView -- упрощённый интерфейс для wxListCtrl;
    • wxTextCtrl -- текстовое поле ввода (одно- или многострочное);
    • wxTreeCtrl -- дерево (иерархическая структура объектов);
    • wxScrollBar -- полоса прокрутки;
    • wxSpinButton -- двойная кнопка (вверх-вниз, больше-меньше);
    • wxSpinCtrl -- комбинация wxTextCtrl и wxSpinCtrl для ввода чисел;
    • wxStaticText -- одно- или многострочное поле для отображения нередактируемого текста;
    • wxStaticBitmap -- растровая картинка;
    • wxRadioBox -- группа связанных пунктов, допускающих выбор только одного из них (радиокнопки);
    • wxRadioButton -- одиночная радиокнопка;
    • wxSlider -- подвижный ползунок, положением которого можно управлять с помощью мыши;
    • wxVListBox -- то же, что wxListBox, но высота пунктов может быть различной и сам список является виртуальным (его элементы не хранятся в памяти, а вычисляются с помощью пользовательской функции);
    • wxColourPickerCtrl -- элемент для выбора цвета;
    • wxDirPickerCtrl -- элемент для выбора каталога;
    • wxFilePickerCtrl -- элемент для выбора файла;
    • wxFontPickerCtrl -- элемент для выбора параметров шрифта.
  • Валидаторы (для фильтрации и проверки информации, вводимой с помощью элементов управления):
    • wxValidator -- базовый класс для всех валидаторов;
    • wxTextValidator -- валидатор для элемента wxTextCtrl;
    • wxGenericValidator -- универсальный валидатор.
  • Меню:
    • wxMenu -- группа пунктов меню;
    • wxMenuBar -- строка меню в верхней части фрейма;
    • wxMenuItem -- пункт меню.
  • Менеджеры размещения:
    • wxSizer -- абстрактный базовый класс;
    • wxBoxSizer -- размещает элементы в одной горизонтальной строке или одной вертикальной колонке;
    • wxStaticBoxSizer -- то же, что wxBoxSizer, но может управлять размерами рамки wxStaticBox, отображаемой вокруг его элементов;
    • wxGridSizer -- размещает элементы в ячейках воображаемой таблицы (все строки таблицы имеют одинаковую высоту, а столбцы -- одинаковую ширину);
    • wxFlexGridSizer -- то же, что wxGridSizer, но строки и столбцы могут иметь настраиваемый размер;
    • wxGridBagSizer -- то же, что wxFlexGridSizer, но положение элементов (номер строки и столбца) указывается явно, кроме того, элементы могут занимать несколько соседних ячеек по вертикали и/или горизонтали.
    • wxLayoutConstraints, wxIndividualLayoutConstraint -- альтернативный метод управления положением и размерами визуальных элементов.
  • Графический контекст устройств (канва для рисования):
    • wxDC -- базовый класс для всех контекстов устройств;
    • wxBufferedDC, wxBufferedPaintDC -- контекст устройства с двойным буферированием;
    • wxPaintDC -- для рисования из метода OnPaint;
    • wxClientDC -- для рисования вне метода OnPaint;
    • wxWindowDC -- доступ к области окна;
    • wxScreenDC -- доступ ко всему экрану;
    • wxMemoryDC -- для работы с растровыми картинками;
    • wxMirrorDC -- работа с "зеркальным" отображением картинки;
    • wxMetafileDC -- доступа к метафайлу;
    • wxPostScriptDC -- доступ к файлу PostScript;
    • wxPrinterDC -- доступ к принтеру.
  • Графика:
    • wxColour -- цвет, заданный в формате RGB (красный, зелёный, синий);
    • wxDCClipper -- работа со стеком перекрывающихся областей;
    • wxBitmap -- растровая картинка;
    • wxBrush, wxBrushList -- "кисти" для заливки областей изображения;
    • wxCursor -- растровое изображение курсора;
    • wxFont, wxFontList -- шрифты;
    • wxIcon -- иконка (небольшое растровое изображение);
    • wxImage -- графическое изображение (не зависит от платформы);
    • wxImageList -- список изображений;
    • wxMask -- битовая маска;
    • wxPen, wxPenList -- перо для рисования;
    • wxPalette -- палитра (набор RGB-цветов);
    • wxRegion -- область на плоскости;
    • wxRendererNative -- набор примитивов для рисования элементов графического интерфейса пользователя;
  • Обработка событий:
    • wxEvent -- базовый класс для всех событий;
    • wxActivateEvent -- активация элемента или приложения;
    • wxCalendarEvent -- используется с элементом wxCalendarCtrl;
    • wxCalculateLayoutEvent -- запрос на (пере)размещение элементов;
    • wxClipboardTextEvent -- событие буфера обмена;
    • wxCloseEvent -- при закрытии окна;
    • wxCommandEvent -- событие, генерируемое стандартными элементами управления;
    • wxContextMenuEvent -- при вызове контекстного меню;
    • wxDateEvent -- событие элемента wxDatePickerCtrl;
    • wxDialUpEvent -- событие, генерируемое элементом wxDialUpManager;
    • wxDropFilesEvent -- при перетаскивании файла;
    • wxEraseEvent -- при стирании фона элемента;
    • wxFindDialogEvent -- используется диалогом wxFindReplaceDialog;
    • wxFocusEvent -- при получении фокуса ввода;
    • wxKeyEvent -- при нажатии клавиши;
    • wxIconizeEvent -- при сворачивании и восстановлении окна;
    • wxIdleEvent -- состояние ожидания: это событие генерируется, когда нет других событий для обработки;
    • wxInitDialogEvent -- событие, происходящее при инициализации диалога;
    • wxJoystickEvent -- событие джойстика;
    • wxListEvent -- событие для элемента wxListCtrl;
    • wxMaximizeEvent -- окно распахивается на весь экран;
    • wxMenuEvent -- событие меню;
    • wxMouseEvent -- событие мыши;
    • wxMoveEvent -- событие, происходящее при перемещении элемента;
    • wxNotebookEvent -- событие элемента wxNotebook;
    • wxPaintEvent -- при необходимости прорисовки элемента на экране;
    • wxProcessEvent -- при завершении процесса wxProcess;
    • wxQueryLayoutInfoEvent -- запрос на получение информации о размещении элемента (размер, ориентация, режим выравнивания);
    • wxRichTextEvent -- событие элемента редактирования текста в формате RTF;
    • wxScrollEvent -- генерируется элементом wxScrollBar и wxSlider;
    • wxScrollWinEvent -- при прокрутке окна;
    • wxSizeEvent -- событие, происходящее при изменении размеров элемента;
    • wxSocketEvent -- событие сокета;
    • wxSpinEvent -- генерируется элементом wxSpinButton;
    • wxSplitterEvent -- генерируется элементом wxSplitterWindow;
    • wxTimerEvent -- срабатывание таймера;
    • wxTreebookEvent -- генерируется элементом wxTreebook;
    • wxTreeEvent -- генерируется элементом wxTreeCtrl;
    • wxUpdateUIEvent -- запрос на обновление элементов интерфейса пользователя;
    • wxWindowCreateEvent -- событие при создании элемента;
    • wxWindowDestroyEvent -- событие при уничтожении элемента;
    • wxWizardEvent -- генерируется элементом wxWizard.
  • Структуры данных:
    • wxObject -- базовый класс для большинства классов wxWidgets;
    • wxCmdLineParser -- парсер командной строки;
    • wxDateSpan, wxTimeSpan -- интервал времени;
    • wxDateTime -- дата/время;
    • wxArray -- динамический массив;
    • wxArrayString -- массив строк wxString;
    • wxHashMap -- ассоциативный массив, элементы которого имеют строковую или целочисленную метку);
    • wxHashSet -- ассоциативное множество;
    • wxHashTable -- устаревший класс (вместо него рекомендуется использовать wxHashMap);
    • wxList -- связный список (каждый элемент может иметь номер или строковую метку, но последнюю возможность использовать не рекомендуется: для списка с именованными элементами следует применять wxHashMap);
    • wxLongLong -- платформо-независимое 64-битное целое число;
    • wxNode -- узел списка wxList;
    • wxPathList -- список путей файловой системы;
    • wxPoint -- точка на плоскости с целочисленными координатами;
    • wxRect -- прямоугольник;
    • wxRegEx -- для работы с регулярными выражениями;
    • wxRegion -- область сложной формы на плоскости;
    • wxString -- строка;
    • wxStringTokenizer -- представление строки в виде списка токенов (слов);
    • wxRealPoint -- точка на плоскости с вещественными координатами;
    • wxSize -- размер прямоугольной области;
    • wxURI -- универсальный идентификатор ресурса (Uniform Resource Identifier);
    • wxVariant -- данные, тип которых может изменять во время выполнения программы.
  • Информация о типах во время выполнения программы:
    • wxClassInfo, wxObject, макросы RTTI.
  • Ведение протоколов (логов):
    • wxLog, wxLogStderr, wxLogStream, wxLogTextCtrl, wxLogWindow, wxLogGui, wxLogNull, wxLogChain, wxLogPassThrough,wxStreamToTextRedirector;
    • функции, объявленные в файле wx/log.h: wxDebugMsg, wxError, wxFatalError, wxLogError, wxLogFatalError, wxLogWarning, wxLogMessage и т. д.
  • Отладка:
    • wxDebugContext, WXDEBUG_NEW, wxDebugReport, wxDebugReportCompress, wxDebugReportUpload, wxDebugReportPreview, wxDebugReportPreviewStd и макросы, определённые в файле wx/debug.h.
  • Работа с сетью:
    • wxDialUpManager -- выполнение соединения и проверка его состояния;
    • wxIPV4address, wxIPaddress -- адрес Интернет;
    • wxSocketBase -- базовый класс для реализации сокетов;
    • wxSocketClient -- сокет клиента;
    • wxSocketServer -- сокет сервера;
    • wxSocketEvent -- события сокетов;
    • wxFTP -- работа с протоколом передачи файлов FTP;
    • wxHTTP -- работа с протоколом передачи гипертекста HTTP;
    • wxURL -- Universal Resource Locator.
  • Взаимодействие между процессами:
    • wxClient, wxDDEClient -- клиент;
    • wxConnection, wxDDEConnection -- соединение между клиентом и сервером;
    • wxServer, wxDDEServer -- сервер.
  • Архитектура документ/вид:
    • wxDocument -- документ;
    • wxView -- внешний вид документа;
    • wxDocTemplate -- связь между документом и его видом;
    • wxDocManager -- управление документами и видами в приложении;
    • wxDocChildFrame -- дочерний фрейм для отображения вида документа;
    • wxDocParentFrame -- родительский фрейм.
  • Печать:
    • wxPreviewFrame -- фрейм для предварительного просмотра перед печатью;
    • wxPreviewCanvas -- канва для отображения результата предварительного просмотра;
    • wxPreviewControlBar -- стандартная панель с элементами управления просмотром перед печатью;
    • wxPrintDialog -- стандартный диалог выбора принтера и опций печати;
    • wxPageSetupDialog -- стандартный диалог установки параметров страницы;
    • wxPrinter -- работа с принтером;
    • wxPrinterDC -- контекст устройства принтера;
    • wxPrintout -- распечатка (класс, представляющий распечатываемый документ);
    • wxPrintPreview -- результат предпросмотра перед печатью;
    • wxPrintData -- информация о документе, предназначенном для вывода на печать;
    • wxPrintDialogData -- данные диалога wxPrintDialog;
    • wxPageSetupDialogData -- данные диалога wxPageSetupDialog.
  • "Drag and drop" (технология "перетащи и оставь") и буфер обмена:
    • wxDataObject -- объект данных;
    • wxDataFormat -- формат данных;
    • wxTextDataObject -- текстовый объект данных;
    • wxFileDataObject -- файловый объект данных;
    • wxBitmapDataObject -- графический объект данных;
    • wxURLDataObject -- объект данных URL;
    • wxCustomDataObject -- пользовательский объект данных;
    • wxClipboard -- буфер обмена;
    • wxDropTarget -- приёмник данных;
    • wxFileDropTarget -- приёмник файлового объекта данных;
    • wxTextDropTarget -- приёмник текстового объект данных;
    • wxDropSource -- источник данных.
  • Работа с файлами:
    • wxFileName -- работа с именами файлов и атрибутами;
    • wxDir -- работа с файлами и каталогами;
    • wxDirTraverser -- для рекурсивного перебора файлов и подкаталогов;
    • wxFile -- файловый ввод-вывод на нижнем уровне;
    • wxFFile -- буферизованный файловый ввод-вывод на нижнем уровне;
    • wxTempFile -- временный файл;
    • wxTextFile -- текстовый файл как массив строк;
    • wxStandardPaths -- стандартные пути к конфигурационным файлам системы, данным пользователя и приложения.
  • Потоки ввода-вывода:
    • wxStreamBase -- базовый класс для потоков ввода-вывода;
    • wxStreamBuffer -- буфер потока;
    • wxInputStream -- поток ввода;
    • wxOutputStream -- поток вывода;
    • wxCountingOutputStream -- класс для вычисления объёма данных в потоке вывода;
    • wxFilterInputStream -- фильтр для потока ввода (например, для разархивирования или декодирования);
    • wxFilterOutputStream -- фильтр для потока вывода;
    • wxBufferedInputStream -- буферизованный поток ввода;
    • wxBufferedOutputStream -- буферизованный поток вывода;
    • wxMemoryInputStream -- поток ввода, использующий буфер в памяти;
    • wxMemoryOutputStream -- поток вывода, использующий буфер в памяти;
    • wxDataInputStream -- платформо-независимый поток ввода двоичных данных;
    • wxDataOutputStream -- платформо-независимый поток вывода двоичных данных;
    • wxTextInputStream -- платформо-независимый поток ввода текстовых данных;
    • wxTextOutputStream -- платформо-независимый поток вывода текстовых данных;
    • wxFileInputStream, wxFFileInputStream -- файловые потоки ввода;
    • wxFileOutputStream, wxFFileOutputStream -- файловые потоки вывода;
    • wxTempFileOutputStream -- поток вывода во временный файл;
    • wxStringInputStream -- поток ввода строк;
    • wxStringOutputStream -- поток вывода строк;
    • wxZlibInputStream -- поток ввода со сжатием данных;
    • wxZlibOutputStream -- поток вывода со сжатием данных;
    • wxZipInputStream -- для чтения из архива zip;
    • wxZipOutputStream -- для записи в архив zip;
    • wxSocketInputStream -- чтение из сокета;
    • wxSocketOutputStream -- запись в сокет.
  • Многопоточное программирование:
    • wxThread, wxThreadHelper -- класс потока;
    • wxMutex, wxMutexLocker -- мьютекс;
    • wxCriticalSection, wxCriticalSectionLocker -- критическая секция;
    • wxCondition -- условие возобновления потока;
    • wxSemaphore -- семафор.
  • Работа с HTML:
    • wxHtmlHelpController -- управление системой HTML-помощи приложения;
    • wxHtmlWindow -- окно, отображающее HTML-страницу;
    • wxHtmlEasyPrinting, wxHtmlPrintout -- печать HTML-страницы на принтере;
    • wxHtmlParser, wxHtmlTagHandler -- парсер для HTML-страниц и обработчик тегов;
    • wxHtmlWinParser, wxHtmlWinTagHandler -- парсер и обработчик тегов для wxHtmlWindow;
  • Виртуальная файловая система:
    • wxFSFile -- представляет один файл виртуальной файловой системы;
    • wxFileSystem -- основной интерфейс виртуальной файловой системы;
    • wxFileSystemHandler -- обработка доступа к файлам различного типа.
  • Работа с файлами XML:
    • wxXmlDocument -- документ (файл) XML;
    • wxXmlNode -- узел документа XML;
    • wxXmlProperty -- атрибуты узла документа XML.
  • Работа с ресурсами в формате XML:
    • wxXmlResource, wxXmlResourceHandler.
  • Система помощи:
    • wxHelpController, wxHtmlHelpController, wxContextHelp, wxContextHelpButton, wxHelpProvider, wxSimpleHelpProvider, wxHelpControllerHelpProvider, wxToolTip.
  • Работа с базами данных ODBC:
    • wxDb -- соединение с базой данных;
    • wxDbTable -- таблица базы данных;
    • wxDbInf, wxDbTableInf, wxDbColDef, wxDbColInf, wxDbColDataPtr, wxDbColFor, wxDbConnectInf, wxDbIdxDef -- служебные классы для работы с базой данных.
  • Разное:
    • wxApp -- приложение;
    • wxCaret -- курсор;
    • wxCmdLineParser -- парсер командной строки;
    • wxConfigBase, wxFileConfig, wxRegConfig -- работа с конфигурационным файлом и реестром;
    • wxDllLoader -- работа с динамическими библиотеками;
    • wxGLCanvas -- канва для работы с библиотекой OpenGL.
    • wxGLContext -- состояние (текущий набор параметров) OpenGL.
    • wxLayoutAlgorithm -- алгоритм размещения элементов в окне;
    • wxProcess -- процесс, выполняемый функцией wxExecute;
    • wxTimer -- таймер;
    • wxStopWatch -- "секундомер";
    • wxMimeTypesManager -- работа с типами MIME;
    • wxSystemSettings -- глобальные настройки системы;
    • wxSystemOptions -- параметры времени выполнения;
    • wxAcceleratorTable -- таблица "акселераторов" (комбинаций клавиш);
    • wxAutomationObject -- OLE-автоматизация;
    • wxFontMapper -- подстановка шрифтов;
    • wxEncodingConverter -- преобразование текста к другой кодовой таблице;
    • wxCalendarDateAttr -- используется элементом wxCalendarCtrl;
    • wxQuantize -- преобразование изображения к меньшему количеству цветов;
    • wxSingleInstanceChecker -- проверка наличия в системе других выполняемых копий данной программы;
    • wxModule -- загружаемые и выгружаемые программные модули.
  • Дополнительные библиотеки (расширения wxWidgets):
    • wxMozilla -- встраивание браузера Mozilla в приложение wxWidgets (wxmozilla.sourceforge.net);
    • wxIndustrialControls -- набор дополнительных визуальных элементов для отображения аналоговых и цифровых величин (www.koansoftware.com/en/prd_svil_wxindctrl.htm);
    • wxCURL -- доступ к ресурсам HTTP, FTP, Telnet и WebDAV с помощью библиотеки LibCURL (sourceforge.net/projects/wxcurl, homepage.mac.com/codonnell/wxcurldav);
    • ToasterBox -- всплывающие окна для вывода сообщений в стиле MSN (toasterbox.sourceforge.net);
    • wxVTK -- интерфейс к библиотеке трёхмерной графики VTK (wxvtk.sourceforge.net);
    • wxDockIt -- "плавающие" и "прикрепляемые" панели (sourceforge.net/projects/wxextended);
    • wxIFM (Interface Management System) -- ещё одна реализация "плавающих" и "прикрепляемых" панелей на основе технологии "плагинов" (www.snakesoft.net/wxifm);
    • wxMathPlot -- визуализация математических графиков (wxmathplot.sourceforge.net);
    • wxTreeMultiCtrl -- элемент wxTreeCtrl с расширенными возможностями www.solidsteel.nl/jorg/components/treemultictrl/wxTreeMultiCtrl.php);
    • wxVirtualDirTreeCtrl -- ещё одно расширение класса wxTreeCtrl (www.solidsteel.nl/jorg/components/virtualdirtreectrl/wxVirtualDirTreeCtrl.php);
    • wxPropertyGrid -- таблицы свойств (www.geocities.com/jmsalli/propertygrid/index.html);
    • wxSMTP -- работа с электронной почтой (www.frank-buss.de/wxwindows/wino.html);
    • wxResizeableControl -- редактируемые визуальные элементы (de.geocities.com/markusgreither/resizec.htm);
    • wxHyperlinkCtrl -- визуальный элемент-гиперссылка (www.spaceblue.com/codedetail.php?CodeID=7, wxcode.sourceforge.net);
    • wxSQLite -- библиотека для работы с СУБД SQLite (wxsqlite.sourceforge.net);
    • wxIE -- встраивание браузера Microsoft Internet Explorer в приложение wxWidgets (sourceforge.net/projects/wxactivex);
    • wxCTB -- реализация последовательного протокола передачи данных и терминальная программа (www.iftools.com/download.en.html);
    • AWX -- набор визуальных элементов awxOutlookDialog, awxButton, awxToolbar, awxLed (www.iftools.com/awx.en.html);
    • wxSpellChecker -- проверка правописания (www-personal.umich.edu/%7Ejblough/wxspellchecker)
    • wxArt2D -- двумерная графика с поддержкой SVG и CVG (wxart2d.sourceforge.net);
    • wxImprola -- обработка изображений (improla.sourceforge.net);
    • wxHTML -- система помощи в формате HTML (ftp://biolpc22.york.ac.uk/pub/contrib/helpctrlex);
    • wxStEdit -- встраивание в приложение wxWidgets текстового редактора Scintilla (www.lehigh.edu/%7Ejrl1/wxwindows/wxStEdit);
    • wxLCDWindow -- элемент управления, похожий на жидкокристаллический дисплей (ftp://biolpc22.york.ac.uk/pub/contrib/lcdwindow)
    • mmwx -- набор элементов управления mmMultiButton, mmNavigator, mmMonthCtrl, mmDropMonth, mmDropMenu, mmDropWindow, mmHyperText, mmHyperBitmap, mmTextStretch, mmLogonDialog (mindmatters.no/mmwx);
    • LitWindow -- библиотека для быстрой разработки интерфейса программ (www.litwindow.com/Library);
    • Keybinder -- работа с клавишами-акселераторами (wxcode.sourceforge.net);
    • wxBetterDialog -- набор стандартных диалогов (wxcode.sourceforge.net);
    • wxBZipStream -- работа с архивами в формате Bzip (wxcode.sourceforge.net);
    • wxCrashReport -- формирование сообщений об ошибках в программе (wxcode.sourceforge.net);
    • wxHTTPServer -- Web-сервер (wxcode.sourceforge.net);
    • wxRarInputStream -- чтение информации из архивов RAR (wxcode.sourceforge.net); \item\emph{wxListCtrlEx -- элемент wxListCtrl с расширенными возможностями (wxcode.sourceforge.net);
    • wxSheet -- элемент, похожий на wxGrid, но с расширенным набором функций (wxcode.sourceforge.net);
    • wxStreamMerger -- поиск различий в двух потоках данных (wxcode.sourceforge.net);
    • wxOTL -- работа с базами данных ODBC, Oracle и DB2 (home.tiscali.be/t.bogaert/wxOTL);
    • DatabaseLayer -- интерфейс для работы с СУБД SQLite3, Firebird, MySQL, Postgresql, Oracle, а также ODBC (wxcode.sourceforge.net);
    • wxReportWriter -- табличные отчёты (www.daily.de/RepWrt).
  • Реализации wxWidgets для других языков программирования:
    • wxPython -- для языка Python (www.wxpython.org);
    • wxBasic -- для Бейсика (wxbasic.sourceforge.net);
    • wxPerl -- для языка Perl (wxperl.sourceforge.net);
    • wxRuby -- для Ruby (wxruby.rubyforge.org);
    • wxLua -- для Lua (www.luascript.thersgb.net);
    • wxPHP -- для PHP (wxphp.sourceforge.net, предварительные релизы пока доступны только через CVS);
    • wxJavaScript -- для JavaScript (wxjs.sourceforge.net);
    • wx4j -- для Java (www.wx4j.org);
    • wxEiffel -- для Eiffel (elj.sourceforge.net);
    • wx.NET -- для C# (.NET и Mono, wxnet.sourceforge.net);
    • wxHaskell -- для Haskell (wxhaskell.sourceforge.net);
    • wxEuphoria -- для Euphoria (wxeuphoria.sourceforge.net);
    • wxLisp -- для языка Lisp;
    • wxR -- для языка R.

Разумеется, в одной книге нельзя описать назначение и примеры использования всех перечисленных классов и библиотек, но мы постараемся рассмотреть самые важные из них.


Простейшее приложение wxWidgets

В листинге 1 приведён пример простейшего приложения на языке C++, написанного с использованием библиотеки wxWidgets, на рис.1 показан его внешний вид на фоне окна интегрированной среды разработки Anthemion DialogBlocks в системе Windows, а на рис.2 -- в Linux. Как видим, мы создали пустое окно с заголовком.

Простейшее приложение wxWidgets в системе Windows на фоне окна DialogBlocks

Простейшее приложение wxWidgets в системе Linux на фоне окна DialogBlocks

Листинг 1. Простейшее приложение wxWidgets (файл examples-wx/00/00.cpp),

    1 // Простейшее приложение wxWidgets
    2
    3 #include <wx/wx.h>
    4
    5 class MyApp: public wxApp {
    6
    7     virtual bool OnInit();
    8 };
    9
   10 DECLARE_APP(MyApp)
   11 IMPLEMENT_APP(MyApp)
   12
   13 bool MyApp::OnInit() {
   14     wxFrame *frame = new wxFrame(
   15             NULL, wxID_ANY, _("Здравствуй, wxWidgets!"),
   16             wxPoint(50,50),  wxSize(400,300) );
   17     frame->Show(TRUE);
   18     SetTopWindow(frame);
   19     return TRUE;
   20 }

Что для этого пришлось сделать:

  • (3) Подключили библиотеку wxWidgets.
  • (5-7) Описали класс MyApp ("Моё приложение") -- наследник класса wxApp с единственным методом OnInit, выполняемым при инициализации программы.
  • (10-11) С помощью макросов DECLARE_APP и IMPLEMENT_APP сформировали требуемую для текущей платформы инфраструктуру приложения. Например, на платформе Windows макрос IMPLEMENT_APP реализует главную функцию WinMain. Что касается DECLARE_APP, то этот макрос всего лишь сохраняет ссылку на свой аргумент в глобальной переменной wxTheApp, благодаря чему с помощью вызова метода wxGetApp всегда можно получить доступ к экземпляру приложения (вместо того, чтобы явно использовать в программе глобальную переменную app). Поэтому в данном случае макрос DECLARE_APP используется только для полноты картины.
  • (13-19) Описали реализацию метода OnInit: создали фрейм размером 400 x 300 пикселов, вывели его на экран (17) так, чтобы левый верхний угол находился в точке с координатами (50, 50), и указали, что этот фрейм будет являться главным окном приложения (18), т.е. при закрытии этого окна будет завершена сама программа.

Макрос wxT() требуется использовать перед всеми строковыми константами, имеющимися в программе. В режиме компиляции ANSI он ничего не делает, а в режиме Unicode -- добавляет перед строкой, как и положено, символ L. Вместо wxT() можно писать _T() или _() (один знак подчёркивания).

В реальных приложениях на C++ интерфейсная часть с декларациями классов и функций помещается в отдельный заголовочный файл *.h, но в нашей первой программе мы постарались сделать всё как можно проще.

Чуть позже мы увидим, как выглядит консольное приложение wxWidgets.


Установка wxWidgets

Перед тем, как компилировать программы, использующие библиотеку wxWidgets, сначала надо собрать саму библиотеку. Рассмотри, как это сделать на различных платформах. Заметим, что начинающим лучше использовать для компиляции как библиотеки wxWidgets, так и своих программ интегрированную среду разработки Anthemion DialogBlocks. Читатели, последовавшие этому совету, могут сразу перейти к следующему разделу.

Сборка библиотеки wxWidgets в системе Windows с помощью Visual C++

  • Загрузите программу установки wxMSW-?.?.?-Setup.exe (вместо символов ? указывается номер версии), она доступна в Интернете по адресу prdownloads.sourceforge.net/wxwindows.
  • Запустите инсталлятор. Укажите каталог установки, например, C:/wxWidgets.
  • После установки библиотеки добавьте системную переменную окружения WXWIN, которая будет указывать на каталог установки wxWidgets. Для этого:
    • Щёлкните правой кнопкой мыши по значку Мой компьютер на рабочем столе.
    • Выберите команду Свойства (Properties).
    • После открытия диалога Свойства системы перейдите на закладку Дополнительно (Advanced).
    • Нажмите кнопку Переменные среды (Environment Variables).
    • В появившемся диалоге Переменные среды в разделе Системные переменные (System variables) нажмите кнопку Создать (New).
    • Откроется диалог Изменение системной переменной (рис.). В поле Имя переменной (Variable name) напишите WXWIN, а в поле Значение переменной (Variable value) -- полное имя каталога, в который установлена библиотека.
    • Нажмите кнопку OK, после закрытия диалога еще раз нажмите OK в окне Переменные среды и, наконец, третий раз нажмите OK, чтобы закрыть диалог Свойства системы.

    Добавление переменной окружения в системе Windows

  • Теперь необходимо выполнить компиляцию библиотеки. Если на вашем компьютере установлена программа Microsoft Visual Studio 6.0 или Microsoft Visual Studio .NET 2003, просто откройте файл c:/wxWidgets/build/wx.dsw и выполните компиляцию проекта. В результате в папке c:/wxWidgets/lib появятся файлы, необходимые для компиляции наших будущих программ. Если вы хотите использовать динамические библиотеки dll, а не статические lib, то вместо файла проекта wx.dsw надо открыть wx-dll.dsw. В зависимости от источника, через который получены файлы проектов *.dsp и *.dsw, они могут содержать символы конца строк LF, принятые в Unix/Linux-системах, вместо пары символов CR+LF, как это принято в Windows. В результате при попытке открыть такой проект в MS Visual Studio будет выдаваться сообщение об ошибке. Единственный способ исправить ситуацию -- преобразовать символы конца строк к нужному формату. Это можно сделать, например, с помощью редактора EmEditor (www.emurasoft.com). Что касается Microsoft Visual Studio 2005, то эта среда разработки, наконец-то, научилась работать с форматом файлов Unix/Linux.

Доступно несколько вариантов компиляции библиотеки wxWidgets:

  • Debug или Release -- с информацией для отладчика или без;
  • ANSI или Unicode -- без или с поддержкой Unicode;
  • lib или dll -- формируются статические или динамические библиотеки;
  • Monolithic=1 или 0 -- получается одна большая библиотека или несколько;
  • Native или Universal -- используются нативные визуальные элементы целевой платформы или универсальные.

Можно проводить компиляцию с любым набором значений этих параметров -- созданные в результате библиотеки не помешают друг другу.

При компиляции в режиме Debug все библиотеки получают после номера релиза суффикс d, в режиме Unicode -- суффикс u. Например:

  • wxbase28.lib -- библиотека, скомпилированная в режиме ANSI Release;
  • wxbase28d.lib -- библиотека, скомпилированная в режиме ANSI Debug;
  • wxbase28u.lib -- библиотека, скомпилированная в режиме Unicode Release;
  • wxbase28ud.lib -- библиотека, скомпилированная в режиме Unicode Debug;
Вместо интегрированной среды, для компиляции wxWidgets можно воспользоваться командной строкой, для этого перейдите в каталог $(WXWIN)/build/msw и выполните команды:
nmake -f makefile.vc BUILD=release SHARED=0 UNICODE=0 MONOLITHIC=0  \
    WXUNIV=0 USE_EXCEPTIONS=1 USE_ODBC=1 USE_OPENGL=0 USE_THREADS=1 \
    USE_HTML=1 USE_XRC=1 USE_GUI=1 RUNTIME_LIBS=dynamic clean
nmake -f makefile.vc BUILD=release SHARED=0 UNICODE=0 MONOLITHIC=0  \
    WXUNIV=0 USE_EXCEPTIONS=1 USE_ODBC=1 USE_OPENGL=0 USE_THREADS=1 \
    USE_HTML=1 USE_XRC=1 USE_GUI=1 RUNTIME_LIBS=dynamic all

Можно указать другие комбинации флагов (все флаги можно найти в файле makefile.vc, в разделе Variables), только следует иметь в виду, что при установленном режиме UNICODE=1 на платформе Windows 9x (но не выше) требуется также указать MSLU=1, а при USE_XRC=1 на любой платформе необходимо задать USE_HTML=1. Перед тем, как начинать компиляцию при установленном флаге USE_ODBC=1, необходимо исправить значение

#define wxUSE_ODBC 1
в файле $(WXWIN)/include/wx/msw/setup.h. Если такого файла в указанном каталоге нет, тогда там находится файл setup0.h. Его следует скопировать и переименовать, дав имя setup.h.

Процесс компиляции библиотеки wxWidgets занимает довольно много времени: в зависимости от установленных режимов, компилятора и быстродействия компьютера может потребоваться от 10 минут до получаса. Результат компиляции (библиотеки lib или dll) будут помещены в папки $(WXWIN)/lib/vc_lib или $(WXWIN)/lib/vc_dll.

Сборка библиотеки wxWidgets в MS Windows с помощью Borland C++

Можно воспользоваться либо свободно распространяемым компилятором командной строки BC++, либо вызвать из командной строки компилятор, входящий в состав интегрированной среды Borland C++Builder:
make -f makefile.bcc BUILD=release SHARED=0 UNICODE=0 MONOLITHIC=0  \
    WXUNIV=0 USE_EXCEPTIONS=1 USE_ODBC=1 USE_OPENGL=0 USE_THREADS=1 \
    USE_HTML=1 USE_XRC=1 USE_GUI=1 RUNTIME_LIBS=dynamic

Созданные в результате статические (при SHARED=0) или динамические (при SHARED=1) библиотеки будут помещены в каталог $(WXWIN)/lib/bcc_lib или $(WXWIN)/lib/bcc_dll.

Сборка библиотеки wxWidgets в MS Windows с помощью MinGW

При разработке межплатформенных приложений удобно использовать один и тот же компилятор как в Windows, так и в Linux. Это возможно, т.к. существует компилятор GNU C++, работающий в системе Linux, но доступный также и в MS Windows, где он известен под именем MinGW (Minimalist Gnu Compiler for Windows). Начинающим проще всего установить MinGW Studio -- это и сам компилятор с необходимыми библиотеками, и интегрированная среда разработки программ.

Порядок установки MinGW Studio:

  • Запустите на выполнение файл MinGWStudioFullSetup-2.05.exe (цифры в конце имени обозначают номер версии и могут быть другими).
  • После появления на экране диалога мастера установки нажмите кнопку Next (Далее).
  • Прочитайте лицензионное соглашение и нажмите I Agree (Согласен).
  • Пометьте галочками все устанавливаемые компоненты: среда разработки MinGW Developer Studio, компилятор MinGW, примеры программ (Samples) и справка по Win32 API. Снова нажмите Next.
  • Укажите каталог установки (Destination Folder), например, c:/MinGWStudio.
  • Нажмите кнопку Install (Установить).
  • После окончания процесса установки нажмите Finish (Конец).
  • Добавьте в системную переменную окружения PATH каталог c:/MinGW/bin, чтобы потом при необходимости упростить запуск процесса компиляции из командной строки. Перезагрузите компьютер.
  • В каталоге c:/MinGWStudio/Templates найдите текстовый файл wxwindows.tpl. В разделах [Debug] и [Release] в подразделе //linker следует проверить значение параметра libraries (библиотеки). В конце имени некоторых библиотек указан номер их версии: 25, 26, 27 или 28. Следует указать номер, соответствующий релизу имеющейся у вас библиотеки wxWidgets.

После установки компилятора можно провести компиляцию библиотеки wxWidgets. Зайдите в каталог $(WXWIN)/build/msw и выполните команду:

mingw32-make.exe -f makefile.gcc                            \
    INCLUDE="C:/MinGWStudio/MinGW/include"                  \
    LIB="D:/MinGWStudio/MinGW/lib"                          \
    USE_XRC=1 BUILD=release UNICODE=1 WXUNIV=0 MONOLITHIC=0 \
    USE_EXCEPTIONS=1 USE_ODBC=1 USE_OPENGL=0 USE_GUI=1      \
    RUNTIME_LIBS=dynamic SHARED=0 all
Можно указать другие комбинации флагов (все флаги можно найти в файле makefile.gcc, в разделе Variables). Статические или динамические библиотеки (*.a или *.dll), созданные в результате компиляции, будут помещены в каталог $(WXWIN)/lib/gcc-lib или $(WXWIN)/lib/gcc-dll соответственно. Процесс компиляции длится раза в два дольше, чем при использовании MS Visual C++, при этом на экран выводится длинная вереница предупреждений (warnings), которые можно игнорировать.

Сборка wxWidgets в Linux/BSD/Unix/Mac OS с помощью GCC

Для компилятора gcc сборка библиотеки wxWidgets выполняется следующим образом:
  • В каталоге wxWidgets создайте подкаталог, например, с именем BuildGTK2ud (для компиляции с использованием GTK2 в режиме Unicode Debug);
  • в этом каталоге выполните команду
    ../configure --enable-unicode --enable-debug --disable-shared --disable-monolithic
    Будет проведён анализ конфигурации системы и созданы make-файлы для сборки библиотеки;
  • в этом же каталоге выполните команду make. Результат компиляции будет помещён в подкаталог BuildGTK2ud/lib;
  • если необходимо использование библиотеки wxWidgets всеми пользователями системы, то можно перейти в режим суперпользователя (команда su) и выполнить make install, в результате чего файлы, необходимые для компиляции программ, будут скопированы в каталог /usr/local.


Компиляция приложений wxWidgets

Для компиляции программ с использованием библиотеки wxWidgets можно использовать практически любой компилятор C++. К сожалению, настройки проекта или текст make-файла зависят от того, какой именно компилятор мы используем и на какой платформе производится компиляция.

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

Всё это выглядит достаточно сложным, поэтому начинающим лучше использовать для компиляции своих программ простую интегрированную среду разработки Anthemion DialogBlocks, которая сама при необходимости может выполнить перекомпиляцию библиотеки wxWidgets с нужным набором опций.

Anthemion DialogBlocks

Anthemion DialogBlocks -- коммерческая IDE (Integrated Development Environment, интегрированная среда) для разработки графических приложений wxWidgets. Сама эта программа написана с использованием wxWidgets, одним из основных разработчиков этой библиотеки, поэтому доступна для любой платформы. Программу можно бесплатно скачать с сайта www.anthemion.co.uk/dialogblocks. В незарегистрированной версии ограничено число элементов в одном диалоге: не более 30. Но если мы не используем редактор диалогов, то возможности бесплатной версии программы ничем не ограничены.

Установка DialogBlocks в MS Windows

Для установки DialogBlocks в Windows достаточно запустить на выполнение инсталлятор DialogBlocks-3.12-Unicode-Setup.exe (цифры обозначают номер релиза и могут быть другими).

После первого запуска программы необходимо выполнить команду меню View | Settings (или нажать комбинацию клавиш Ctrl+T) и в разделе Settings/Paths (Установки/Пути) указать путь к библиотеке wxWidgets (параметр WXWIN) и каталог установки компилятора. Если планируется использовать MinGW, то надо определить значение параметра MINGWDIR, например, C:\MinGWStudio\MinGW. Для Microsoft Visual C++ требуется установить значение MSVCDIR, например, c:\Program Files\Microsoft Visual Studio .NET 2003. Чтобы сохранить изменения, надо нажать кнопку OK.

Установка DialogBlocks в Linux

Установить DialogBlocks в Linux, как это обычно бывает, несколько сложнее:
  • Распакуйте архив DialogBlocks-x.xx-i386.tar.gz, например:
    % cd ~
% tar xvfz DialogBlocks-3.12.tar.gz
    (цифры обозначают номер версии и могут быть другими).
  • Добавьте каталог DialogBlocks-3.12 в переменную окружения PATH:
    % export DIALOGBLOCKSDIR=`pwd`/DialogBlocks-3.12
% export PATH=$PATH:$DIALOGBLOCKSDIR
    После этого запустить программу на выполнение можно с помощью команды
    % dialogblocks
    Если вы не хотите добавлять ещё один каталог в список путей PATH, то существует другой способ. Можно подготовить небольшой скрипт:
    #!/bin/sh
# Invokes DialogBlocks
export DIALOGBLOCKSDIR=/home/mydir/DialogBlocks-3.12
$DIALOGBLOCKSDIR/dialogblocks $*
    и разместить его в каталоге, который уже указан в PATH, например, /usr/local/bin.
После первого запуска программы необходимо выполнить команду меню View | Settings (или нажать комбинацию клавиш Ctrl+T) и в разделе Settings/Paths (Установки/Пути) указать путь к библиотеке wxWidgets (параметр WXWIN).

Удаление программы DialogBlocks

Если вы по каким-либо причинам захотите удалить DialogBlocks (это может произойти, только если вы разочаруетесь в разработке межплатформенных программ), то в MS Windows, как обычно, следует воспользоваться мастером установки и удаления программ (Пуск | Настройка | Панель управления | Установка и удаление программ).

В Linux достаточно просто удалить каталог DialogBlocks-3.12 (и скрипт для запуска, если вы его создавали).

Работа с DialogBlocks

Среду разработки DialogBlocks можно использовать для создания любых приложений wxWidgets, как консольных, так и с графическим интерфейсом. Во втором случае имеется два варианта: можно программировать графический интерфейс приложения в текстовом режиме, путём вызова функций-методов соответствующих классов, либо воспользоваться визуальным редактором диалогов.

Второй вариант мы рассмотрим чуть позже, а пока остановимся на "чистом" программировании.

Типичная последовательность действий для создания проекта:

  • Выполним команду меню File | New Project (Файл | Новый проект) или нажмём комбинацию клавиш Ctrl+N.
  • Укажем каталог и имя проекта в поле Project filename (для выбора каталога можно воспользоваться кнопкой Browse, рис.). Сбросим галочку Use template (Использовать шаблон). Нажмём кнопку Next (Далее).

    Создание проекта в DialogBlocks: шаг 1

  • Снимем галочку Generate C++ for XRC (Сфомировать код C++ для работы с XRC-ресурсами) и снова нажмём Next.
  • Сбросим галочку Generate application class (Сформировать код для класса приложения, рис.).

    Создание проекта в DialogBlocks: шаг 2

  • Нажмём кнопку Create Standard Configurations (Создать стандартные конфигурации). В раскрывающемся списке Compiler (Компилятор) выберем пункт, соответствующий нашему компилятору, например, MinGW или VC++. Поставим галочки напротив тех конфигураций, которые предполагается использовать в проекте: Debug (с информацией для отладчика), Release (без информации для отладчика), Unicode Debug или Unicode Release (рис.). Нажмём OK, а затем Next.

    Создание проекта в DialogBlocks: шаг 3

  • Выберем кодировку для файлов с исходными текстами и описаниями XRC-ресурсов (раскрывающийся список Source encoding), а также кодировку для файла проекта (раскрывающийся список Project encoding, рис.).

    Создание проекта в DialogBlocks: шаг 4

  • Нажмём кнопку Finish (Конец).
  • Раскроем дерево проекта в левой части окна и нажмём правой кнопкой мыши по изображению папки Sources (Исходные тексты). В раскрывшемся меню выберем команду Add Sources Files (Добавить файлы с исходными текстами). В поле Имя файла укажем любое имя, например, 00.cpp.
  • Впишем текст программы (рис.). Сохраним все изменения, нажав кнопку Save project на панели инструментов или выполнив команду меню File Save Project (кроме того, доступна комбинация клавиш Ctrl+S).

    Создание проекта в DialogBlocks: шаг 5

    Создание проекта в DialogBlocks: шаг 6

  • Перед компиляцией укажем конфигурацию, выполнив команду меню Built | Configurations. В разделе Settings/Configurations (Установки/Конфигурации) выберем одну из конфигураций, созданных мастером проекта, или добавим новую (кнопки Add или Standard). В таблице свойств конфигурации (рис.) установим нужные значения для флагов Shared mode (статические или динамические библиотеки wxWidgets), Modularity (один большой файл библиотеки wxWidgets или несколько), Runtime linking (статические или динамические библиотеки времени выполнения), Use exceptions (использовать ли исключения), Use ODBC (использовать ли модули поддержки ODBC для доступа к базам данных), Use OpenGL (использовать ли библиотеку OpenGL) и другие, чтобы они соответствовали тем значениям, при которых была выполнена компиляция библиотеки wxWidgets. Начинающие могут ничего не изменять, положившись на те значения опций, что выбраны по умолчанию. Если какие-либо настройки не совпадут (или если библиотека wxWidgets пока что вообще не компилировалась) -- не страшно, DialogBlocks в нужный момент предложит перекомпилировать библиотеку wxWidgets с новой комбинацией параметров и при необходимости исправит значения флагов в файле setup.h. Набор флагов компиляции можно узнать в текстовом файле build.cfg, расположенном в том каталоге, куда были сохранены библиотеки (*.lib, *.dll или *.a) при компиляции wxWidgets. Если используется компилятор MinGW, а в тексте программы имеются русские символы, то необходимо скорректировать параметр Preprocessor flags: после %AUTO% написать пробел и добавить определение кодировки, например:
    %AUTO% --input-charset=cp1251
    Допустимы также значения ISO-8859-5, koi8-r, cp866. В системе Linux обычно используется утилита configure, поэтому значение Preprocessor flags игнорируется, вместо этого определение кодировки можно дописать в параметре Optimization. После установки всех параметров конфигурации надо нажать кнопку OK.
  • Выполним команду меню Build |Build Project или просто нажмём F8. Если будет предложено автоматически исправить файл setup.h и перекомпилировать библиотеку wxWidgets, то либо ответим утвердительно и подождём окончания процесса компиляции (от 10 минут до получаса), после чего вновь выполним команду Build | Build Project, либо вернёмся к предыдущему пункту и изменим значения параметров конфигурации, чтобы они соответствовали тем, при которых производилась компиляция библиотеки wxWidgets.
  • Для запуска скомпилированной программы следует нажать клавишу F9, а для запуска её в отладчике -- выполнить команду меню Build | Debug Project.
Заметим, что при компиляции программы DialogBlocks создаёт в каталоге, где расположен проект, make-файл со стандартным названием makefile.* (где расширение .* зависит от выбранного компилятора), и обновляет его только при необходимости, поэтому если вы хотите разместить в одном каталоге несколько проектов, то для каждого из них придётся в разделе Configurations задать уникальное значение параметра Project makefile.

Microsoft Visual C++

В операционной системе Windows проект приложения wxWidgets для Microsoft Visual Studio создаётся следующим образом (автор использовал MS Visual Studio 7.1 .NET 2003, но в MS Visual Studio 6.0 последовательность действий практически та же):
  • Откроем MS Visual C++.
  • Начнём создание нового проекта.
  • В левой части диалога в группе Visual C++ Projects выберем пункт Win32.
  • В правой части диалога щёлкнем мышкой на значок Win32 Project.
  • В нижней части диалога введём название проекта simple1 и зададим каталог, в котором будут располагаться все файлы проекта (рис.).

    Выбор типа проекта в среде MS Visual Studio 7 .NET

  • Нажмём кнопку OK.
  • В раскрывшемся окне мастера проекта щёлкнем мышкой на пункт Application Settings (Установки приложения), выберем тип будущего приложения Windows Application (Приложение Windows) и установим опцию Empty project (Пустой проект) (рис.).

    Настройка проекта MS Visual Studio 7 .NET

  • Нажмём кнопку Finish.
  • Теперь создадим файл с текстом программы. Для этого выполним команды меню File | New | File (или просто нажмём комбинацию клавиш Ctrl+N), в группе Visual C++ выберем тип файла C++ File (.cpp) и нажмём кнопку Open (Открыть).
  • Введём текст программы (листинг ).
  • Сохраним файл под именем simple1.cpp в каталог, указанный при создании проекта.
  • Добавим файл к проекту, для этого воспользуемся пунктом меню Project | Add Existing Item (Проект | Добавить существующий файл) и укажем только что сохранённый файл simple1.cpp.
  • Выберем пункт меню Project | Properties (Проект | Свойства) и раскроем группу C/C++. Выберем пункт General (Общие).
  • В поле Additional Include Directories (Дополнительные пути поиска) для конфигурации Debug укажем три каталога (рис.):
            $(WXWIN)\include
        $(WXWIN)\contrib\include
        $(WXWIN)\lib\vc_lib\mswd
    Если мы намерены использовать версию Unicode, то mswd слудет заменить на mswud.

    Задание каталогов поиска h-файлов

  • Для конфигурации Release (если мы намерены её использовать) зададим те же самые каталоги, только вместо mswd (mswud) укажем msw (wswu):
            $(WXWIN)\include
        $(WXWIN)\contrib\include
        $(WXWIN)\lib\vc_lib\msw
  • В разделе Preprocessor (Препроцессор) в поле Preprocessor Definitions для конфигурации Debug впишем следующие определения (рис.):
            WIN32;_DEBUG;_WINDOWS;__WINDOWS__;__WXMSW__;__WXDEBUG__;
        WXDEBUG=1;__WIN95__;__WIN32__;WINVER=0x0400;STRICT

    Опции препроцессора

  • Аналогично для конфигурации Release:
            NDEBUG;WIN32;_WINDOWS;__WINDOWS__;__WXMSW__;
        __WIN95__;__WIN32__;WINVER=0x0400;STRICT
  • В разделе Code Generation (Генерация кода) для конфигурации Debug в поле Runtime library (Библиотека времени выполнения) выберем режим Multi-threaded Debug DLL (/MDd).
  • Для конфигурации Release, если необходимо, для этого же параметра выберем значение Multi-threaded DLL (/MD).
  • Раскроем группу Linker (Компоновщик) и в разделе General (Общие) для пункта Additional Library Directories (Пути поиска дополнительных библиотек) для конфигурации Debug укажем три каталога (рис.):
            $(WXWIN)\lib
        $(WXWIN)\contrib\lib
        $(WXWIN)\lib\vc_lib

    Задание каталогов поиска библиотек для компоновщика

  • Для конфигурации Release, если необходимо, укажем те же самые каталоги.
  • В разделе Input в поле Additional Dependences (Дополнительные библиотеки) для конфигурации Debug зададим:
            wxmsw28d_core.lib wxbase28d.lib wxtiffd.lib wxjpegd.lib
        wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib
        user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib
        shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib
        rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib oleacc.lib
    Если используется Unicode, вместо wxmsw28d_core.lib и wxbase28d.lib надо указать wxmsw28ud_core.lib и wxbase28ud.lib.
  • Для конфигурации Release:
            wxmsw28_core.lib wxbase28.lib wxtiff.lib wxjpeg.lib
        wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib
        user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib
        shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib
        rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib oleacc.lib
    Если используется Unicode, вместо wxmsw28_core.lib и wxbase28.lib надо указать wxmsw28u_core.lib и wxbase28u.lib.
  • Нажмём кнопку OK.
  • Выполним компиляцию проекта: Build | Build Solution.
  • Запустим программу на выполнение, выбрав пункт меню Debug | Start или Debug | Start Without Debugging. Результат выполнения программы показан на рис.

Как видим, процесс создания проекта довольно трудоёмок, даже для простой программы. Разумеется, в нашем случае не все указанные библиотеки были так уж необходимы, но мы стремились к тому, чтобы описать универсальный алгоритм создания проекта, работающий во всех практически важных случаях.

Генерация make-файла для любого компилятора с помощью Bakefile

Вместо того, чтобы даже для простейшей программы создавать проект в интерактивном режиме, гораздо проще подготовить make-файл, чтобы использовать его для компиляции проекта в режиме командной строки. К сожалению, различные компиляторы используют make-файлы различного формата. Поэтому существуют специальные утилиты, которые позволяют генерировать make-файлы для практически любого компилятора. Среди таких утилит стоит выделить Bakefile и Cmake. В данном разделе мы рассмотрим первую из них.

Утилита Bakefile доступна как в исходных кодах (она написана на языке Python), так и в скомпилированном виде для различных платформ. Страница проекта в Интернете имеет адрес http://bakefile.sourceforge.net. Инсталлятор для Windows можно найти на CD-ROM, прилагаемом к этой книге (файл bakefile-0.2.0-setup.exe). Для компиляции утилиты на других платформах на CD-ROM имеется архив с исходными текстами (файл bakefile-0.2.0.tar.gz).

После установки Bakefile надо скопировать файлы *.bkl из каталога

%WXWIN%/build/bakefiles/wxpresets/presets
в каталог presets утилиты Bakefile. А ещё лучше вместо этого определить переменную окружения BAKEFILE_PATHS, чтобы она указывала на каталог
%WXWIN%/build/bakefiles/wxpresets

Утилита Bakefile использует описание проекта на языке XML. Для нашего первого приложения wxWidgets такой файл приведён в листинге .

Листинг . Описание проекта для Bakefile (файл 00/build/00.bkl)

 1  <?xml version="1.0" ?>
 2  <makefile>
 3      <include file="presets/wx.bkl"/>
 4      <exe id="00" template="wx">
 5          <app-type>gui</app-type>
 6          <debug-info>on</debug-info>
 7          <runtime-libs>dynamic</runtime-libs>
 8          <sources>../00.cpp</sources>
 9          <wx-lib>core</wx-lib>
10          <wx-lib>base</wx-lib>
11      </exe>
12  </makefile>

В строке (3) подключаются определения, необходимые для работы с wxWidgets. Файл wx.bkl и используемые им файлы wx_win32.bkl и wx_unix.bkl находятся в каталоге %WXWIN%/build/bakefiles/presets. Если утилита Bakefile не находит эти файлы в текущем каталоге, то она просматривает все папки, перечисленные в системной переменной окружения BAKEFILE_PATHS. Также можно явно указать требуемый каталог в командной строке после ключа -I при вызове Bakefile.

В строке (4) заданы тип и имя исполняемого файла, а в (8) указан путь к файлу с исходным текстом программы. Все make-файлы для проектов на основе wxWidgets принято размещать в отдельном каталоге, поэтому перед именем 00.cpp стоят две точки, обозначающие переход на один уровень вверх. Часто таких файлов бывает больше одного, тогда их имена отделяются друг от друга пробелом. Заголовочные файлы *.h в этот список не включают.

Если целью компиляции является создание не исполняемого файла, а библиотеки, то вместо exe пишут (для любой платформы):

  • dll -- для создания динамической библиотеки;
  • lib -- для создания статической библиотеки;
  • module -- для создания библиотеки, загружаемой во время выполнения программы (так называемый плагин).
Для добавления к проекту каталогов, которые необходимо просматривать в поисках включаемых файлов, используют тег <include>, который оформляется аналогично тегу <sources>. При задании путей на любой платформе используют прямой слэш "/".

Сохранив XML-описание в файле с именем 00.bkl, можно сгенерировать make-файл, например, для компилятора Visual C++. Для этого в командной строке Windows надо выполнить команду

"C:\Program Files\Bakefile\src\bakefile" -f msvc 00.bkl
В результате будет создан файл makefile.vc.

Теперь для компиляции программы достаточно выполнить из командной строки:

nmake -f makefile.vc
Будет сформирован исполняемый файл 00.exe, а также служебные файлы 00.obj, 00.pdb, 00.ilk, которые можно удалить.

При компиляции можно указывать дополнительные опции:

nmake -f makefile.vc WX_DEBUG=0 WX_SHARED=1 WX_UNICODE=1
Флаг WX_DEBUG показывает, надо ли включать информацию для отладчика; при WX_SHARED=1 используются динамические dll-библиотеки вместо статических lib; при установке флага WX_UNICODE в программе будут использованы строки в формате Unicode. Разумеется, чтобы компиляция при выбранных режимах оказалась удачной, требуется предварительно собрать библиотеку wxWidgets с той же самой комбинацией флагов.

Заметим, что в новых версиях wxWidgets в шаблонах Bakefile из имён опций исключён префикс WX_.

Если требуется сгенерировать make-файл для другого компилятора, то в команде запуска Bakefile вместо msvc следует указать любой из доступных ключей:

  • borland для Borland C/C++;
  • cbuilderx для C++ Builder X;
  • mars для Digital Mars;
  • gnu для GNU C++;
  • autoconf для GNU autoconf;
  • mingw для MinGW;
  • watcom для OpenWatcom;
  • msvc для создания make-файла MS Visual C++;
  • msvc6prj для создания проекта MS Visual Studio 6.
Чтобы посмотреть другие доступные режимы, надо запустить Bakefile с параметром -h.

В системе Linux для генерации make-файла и компиляции проекта используются аналогичные команды:

bakefile -f gnu 00.bkl
make -f GNUmakefile
Опции здесь не используются, т.к. в Linux режимы компиляции задаются с помощью утилиты wx-config.

Linux KDevelop

В системе Linux для создания приложений wxWidgets можно использовать стандартную среду разработки KDevelop. Опишем типичную последовательность действий:
  • Запустим KDevelop.
  • В главном меню выберем команду Проект | Новый (Project | New).
  • Раскроем группу C++, затем подгруппу wxWidgets и выберем шаблон Simple Hello wxWidgets Application (рис.).

    Создание проекта wxWidgets в KDevelop

  • Введём имя будущего проекта, например, wx00, нажмём четыре раза кнопку Далее и, наконец, Готово.
  • Будет сгенерирован шаблон проекта с одним фреймом, меню и строкой состояния.
  • Выполним компиляцию: Сборка | Собрать проект (или просто F8). Нас спросят, надо ли выполнять automake и configure, ответим утвердительно.
  • Если компиляция закончилась успешно, запустим программу: Отладка | Запуск (или Ctrl+Shift+F9). На рис. показано, как выглядит программа, сгенерированная мастером проекта wxWidgets.

    Работающая программа на фоне окна KDevelop

  • Теперь можно изменить по своему усмотрению содержимое файлов wx00.cpp и wx00.h, начав разработку своей программы.


Меню и строка состояния

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

Для создания панели меню в wxWidgets используется класс wxMenuBar. К панели "прикрепляются" главные пункты меню wxMenu (этот же класс используется и для создания всплывающих меню, а также подпунктов-групп). Каждый элемент меню -- это экземпляр класса wxMenuItem. Его конструктор обычно в программе явно не вызывается, обращение к нему происходит автоматически при добавлении к меню нового элемента. Это делается с помощью методов класса wxMenu: Append, AppendCheckItem, AppendRadioItem или Insert, InsertCheckItem, InsertRadioItem.

Последовательность действий для создания главного меню окна следующая:

  • Определяем неотрицательный целочисленный идентификатор для каждого пункта меню:
    enum {
    ID_MENU_ABOUT = wxID_HIGHEST+1,
    ID_MENU_ASST,
    // .............
}
    Обычно это делается в заголовочном файле. Значения идентификаторов в диапазоне от 4999 до 5999 зарезервированы самой библиотекой wxWidgets и не могут использоваться в программе. Возможно, в будущем этот диапазон будет расширен, поэтому безопаснее для определения его границ использовать константы wxID_LOWEST (нижняя граница) и wxID_HIGHEST (верхняя граница).
  • В конструкторе главного окна приложения создаём панель главного меню:
    wxMenuBar *mb = new wxMenuBar();
  • Создаём группу пунктов меню:
    wxMenu *hmenu = new wxMenu();
  • Добавляем новый пункт меню:
    hmenu->Append( ID_MENU_ABOUT, // идентификатор пункта.
              _("&О программе"), // текстовая метка.
              _("О программе")); // строка-подсказка.
  • Добавляем разделитель, если необходимо:
    hmenu->AppendSeparator();
  • Добавляем новый пункт с двумя состояниями:
    hmenu->AppendCheckItem( ID_MENU_ASST,
                       _("&Показывать помощника"),
                       _("Показывать помощника") );
  • Добавляем меню к панели:
    mb->Append(hmenu, _("&Справка"));
  • Создаём остальные группы меню и добавляем их к панели.
  • Устанавливаем панель главного меню для окна приложения:
    SetMenuBar(mb);
  • Определяем функции-обработчики для каждого пункта меню, например:
    void MyFrame::OnAbout(wxCommandEvent& event) {
    wxMessageBox( _("Простая программа ..."),
                  _("О программе"),
                  wxOK | wxICON_INFORMATION, this );
}
    Функция wxMessageBox выводит на экран окно с сообщением, текст которого задан первым параметром. Второй параметр функции -- заголовок окна, третий -- набор битовых флагов, определяющих, какие кнопки будут показаны в нижней части окна (в данном случае только кнопка OK) и вид иконки перед текстом сообщения. Последний параметр -- это указатель на родительское окно (или NULL).
  • Связываем каждый пункт со своей функцией-обработчиком. Обычно это делается в специальной таблице обработчиков событий, которая оформляется с помощью макросов:
    BEGIN_EVENT_TABLE(MyFrame, wxFrame) // Начало таблицы событий:
    // Обработчик для пункта "О программе":
    EVT_MENU(ID_MENU_ABOUT, MyFrame::OnAbout)

    // Обработчик для пункта "Показывать помощника":
    EVT_MENU(ID_MENU_ASST, MyFrame::OnShowAsst)

    // .............
END_EVENT_TABLE() // Конец таблицы.

Если пункт меню имеет два состояния, то узнать, помечен он или нет, можно с помощью метода IsChecked.

Чтобы сделать пункт меню недоступным для выбора, используют метод Enable(id, enable). Здесь id -- это целочисленный идентификатор пункта, enable может принимать значения true или false. Чтобы запретить или разрешить работу с меню верхнего уровня, следует вызывать метод wxMenuBar::EnableTop(pos, enable).

Для создания в нижней части окна строки состояния используется метод wxFrame::CreateStatusBar(n), где параметр n указывает число горизонтальных полей, на которые будет разделена строка. Чтобы задать ширину этих полей, используется метод

wxFrame::SetStatusWidths(int n, int *widths)
Для вывода текста в любое поле строки состояния служит метод
wxFrame::SetStatusText(const wxString& text, int number = 0)

В листингах . и . приведен пример программы, в которой используется главное меню и строка состояния.

Листинг. Меню и строка состояния (файл examples-wx/01/01.h)

    1 // Меню и строка состояния
    2
    3 #include <wx/wx.h>
    4
    5 class MyFrame: public wxFrame {
    6 public:
    7     MyFrame::MyFrame(wxWindow* parent, wxWindowID id,
    8                      const wxString& title,
    9                      const wxPoint& pos = wxDefaultPosition,
   10                      const wxSize& size = wxDefaultSize);
   11     void OnAbout(wxCommandEvent& event); // О программе.
   12     void OnExit(wxCommandEvent& event);  // Выход.
   13     void OnClose(wxCloseEvent& event);   // При закрытии.
   14     void OnSize(wxSizeEvent& event); // При изменении размеров.
   15     void OnMouseMotion(wxMouseEvent& event); // При перемещении указателя
   16     мыши.
   17 private:
   18     DECLARE_EVENT_TABLE()
   19 };
   20
   21 class MyApp: public wxApp {
   22 public:
   23     virtual bool OnInit();
   24 };
   25 DECLARE_APP(MyApp)
   26
   27 enum {
   28     ID_MENU_ABOUT = wxID_HIGHEST+1,
   29     ID_MENU_EXIT
   30 };

Листинг. Меню и строка состояния (файл examples-wx/01/01.cpp)

    1 // Меню и строка состояния
    2
    3 #include "01.h"
    4
    5 BEGIN_EVENT_TABLE(MyFrame, wxFrame) // Таблица обработчиков событий:
    6     EVT_MENU(ID_MENU_ABOUT, MyFrame::OnAbout) // О программе.
    7     EVT_MENU(ID_MENU_EXIT, MyFrame::OnExit)   // Выход.
    8     EVT_CLOSE(MyFrame::OnClose)  // При закрытии фрейма.
    9     EVT_SIZE(MyFrame::OnSize) // При изменении размеров фрейма.
   10     EVT_MOTION(MyFrame::OnMouseMotion) //При перемещении указателя мыши.
   11 END_EVENT_TABLE()
   12
   13 MyFrame::MyFrame( wxWindow* parent, wxWindowID id,
   14                   const wxString& title, // Заголововок.
   15                   const wxPoint& pos,    // Позиция.
   16                   const wxSize& size)    // Размер.
   17         : wxFrame(parent, id, title, pos, size) {
   18
   19     wxMenuBar *mb = new wxMenuBar(); // Панель меню.
   20     wxMenu *menu = new wxMenu();     // Группа пунктов.
   21     menu->Append( ID_MENU_ABOUT,     // Пункт меню.
   22                   wxT("&О программе"),
   23                   wxT("О программе"));
   24     menu->AppendSeparator();         // Разделитель.
   25     menu->Append( ID_MENU_EXIT,      // Пункт меню.
   26                   wxT("В&ыход"),
   27                   wxT("Выход из программы"));
   28     mb->Append(menu, wxT("&Файл"));
   29     SetMenuBar(mb);
   30
   31     CreateStatusBar(4); // Строка состояния.
   32     int widths[] = { -2, -1, 50, 90 };
   33     SetStatusWidths(4, widths);
   34     SetStatusText(wxT("Добро пожаловать в wxWidgets!"), 0);
   35 }
   36
   37 void MyFrame::OnAbout(wxCommandEvent& event) { // О программе.
   38     wxMessageBox( wxT("Эта простая программа\n")
   39                   wxT("демонстрирует работу с меню\n")
   40                   wxT("и строкой состояния."),
   41                   wxT("О программе"),
   42                   wxOK | wxICON_INFORMATION, this );
   43 }
   44
   45 void MyFrame::OnExit(wxCommandEvent& event) {
   46     Close(); // Закрытие главного окна - выход из программы.
   47 }
   48
   49 void MyFrame::OnClose(wxCloseEvent& event) { // Перед закрытием.
   50     wxMessageDialog *d = new wxMessageDialog( this,
   51                             wxT("Выйти из программы?"),
   52                             wxT("Подтвердите"),
   53                             wxOK | wxCANCEL );
   54     if (d->ShowModal() == wxID_OK) { // Ответ "Да".
   55         Destroy();    // Закрытие фрейма ( или Close() ).
   56     } else  {                        // Ответ "Нет".
   57         d->Destroy(); // Уничтожение диалога с вопросом.
   58     }
   59 }
   60
   61 void MyFrame::OnSize(wxSizeEvent& event) { //При изменении размеров.
   62     wxSize sz = GetSize(); // Размер фрейма.
   63     if (GetStatusBar())
   64         SetStatusText( wxString::Format( wxT("(%d,%d)"),
   65                             sz.GetX(), sz.GetY() ),
   66                        1 );
   67 }
   68
   69 void MyFrame::OnMouseMotion(wxMouseEvent& event) { //При движении мыши.
   70     if (GetStatusBar())
   71         SetStatusText( wxString::Format( wxT("%d,%d"),
   72                             event.GetX(), event.GetY() ),
   73                        2 );
   74 }
   75
   76 //--------------------------------------------------------
   77 IMPLEMENT_APP(MyApp)
   78
   79 bool MyApp::OnInit() {
   80     MyFrame *frame = new MyFrame(
   81                 NULL, -1, wxT("Меню и строка состояния"),
   82                 wxDefaultPosition,  wxSize(500,300) );
   83     frame->CenterOnScreen(wxBOTH); // По центру экрана.
   84     frame->Show();
   85     SetTopWindow(frame);
   86     return TRUE;
   87 }
В конструкторе фрейма (13-35) мы сначала вызвали конструктор родительского класса wxFrame (17), затем создали панель главного меню (19,29) с одним пунктом "Файл" (20,28), содержащее два подпункта: "О программе" (21) и "Выход" (25), с разделителем между ними (24). Задали обработчик для каждого пункта (6,7). Создали строку состояния с четырьмя полями (31), определили ширину полей (32). Отрицательная значение -2 в данном случае означает, что поле будет растяжимо и его ширина по сравнению с другим растяжимым полем (имеющим "ширину" -1) будет в два раза больше.

В первое поле строки состояния (на самом деле "нулевое") мы выводим фиксированный текст (34), который при наведении указателя мыши на пункты меню будет временно заменяться текстом подсказки, определённым при создании этих пунктов (23,27); второе поле будет показывать размеры фрейма в пикселах (64), третье -- координаты указателя мыши (71). Четвёртое поле пока оставлено свободным (чуть позже оно пригодиться для вывода дня недели, даты и времени).

Затем мы задали методы, выполняемые при изменении размеров фрейма (9,61), перемещении указателя мыши (10,69) и закрытии фрейма (8,49). В последнем случае на экран вызывается диалог с вопросом (50-54). Если пользователь подтверждает выход из программы, то мы уничтожаем фрейм с помощью метода Close или Destroy (55), завершая работу приложения, если нет -- удаляем только диалог с вопросом (57), а программа продолжает работать.

Заметим, что из-за статического связывания событий с методами-обработчиками при старте программы может оказаться, что таблица событий уже работает, а соответствующий визуальный элемент ещё не создан Поэтому в (63,70) мы при работе со строкой состояния сначала вызываем метод GetStatusBar и проверяем, не вернул ли он нулевой указатель. Обращаем внимание на первый встретившийся нам пример различного поведения приложения wxWidgets в разных системах: если удалить условный оператор (63,70), то в Linux при старте программы будет выдано сообщение об ошибке сегментации, а в Windows всё будет работать, как обычно.

В листинге приведено XML-описание проекта для утилиты Bakefile.

XML-описание проекта (файл examples-wx/01/build/01.bkl)

    1 <?xml version="1.0" ?>
    2 <makefile>
    3     <include file="presets/wx.bkl"/>
    4     <exe id="01" template="wx">
    5         <app-type>gui</app-type>
    6         <debug-info>off</debug-info>
    7         <runtime-libs>dynamic</runtime-libs>
    8         <sources>../01.cpp</sources>
    9         <wx-lib>core</wx-lib>
   10         <wx-lib>base</wx-lib>
   11     </exe>
   12 </makefile>

Меню и строка состояния

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


Обработка событий в приложениях wxWidgets

В приложениях wxWidgets используются два основных способа связывания событий с функциями-обработчиками. Первый способ -- динамический -- осуществляется с помощью метода Connect. Этот метод многие визуальные (и некоторые невизуальные) элементы наследуют от класса wxEvtHandler, являющегося базовым для большинства других классов библиотеки wxWidgets. Метод Connect имеет три формы вызова:
void wxEvtHandler::Connect(
    int id, int lastId,
    wxEventType eventType,
    wxObjectEventFunction function,
    wxObject* userData = NULL,
    wxEvtHandler* eventSink = NULL )

void wxEvtHandler::Connect(
    int id, wxEventType eventType,
    wxObjectEventFunction function,
    wxObject* userData = NULL,
    wxEvtHandler* eventSink = NULL )

void wxEvtHandler::Connect(
    wxEventType eventType,
    wxObjectEventFunction function,
    wxObject* userData = NULL,
    wxEvtHandler* eventSink = NULL)
Здесь указывается целочисленный идентификатор id элемента-источника событий (а для первой формы -- диапазон идентификаторов от id до lastId), тип события eventType, функция-обработчик function, дополнительные данные userData, если они нужны обработчику, и, наконец, при необходимости задаётся объект eventSink, для которого будет вызываться функция-обработчик. По умолчанию последний параметр принимает значение this. Параметр function обычно приходится явно приводить к нужному типу, для этого применяются макросы вида wxXXXHandler, которые имеются для каждого типа событий wxXXXEvent.

Например, чтобы связать метод MyFrame::OnExit с пунктом меню "Выход" (допустим, этот пункт имеет идентификатор ID_MENU_EXIT), надо в конструкторе фрейма (или любом другом месте) вызвать метод Connect следующим образом:

Connect( ID_MENU_EXIT, wxEVT_COMMAND_MENU_SELECTED,
         wxCommandEventHandler(MyFrame::OnExit) );
Чтобы отменить связь между событием и функцией-обработчиком, используют метод Disconnect:
bool Disconnect(
    wxEventType eventType = wxEVT_NULL,
    wxObjectEventFunction function = NULL,
    wxObject* userData = NULL,
    wxEvtHandler* eventSink = NULL )

bool Disconnect(
    int id = wxID_ANY,
    wxEventType eventType = wxEVT_NULL,
    wxObjectEventFunction function = NULL,
    wxObject* userData = NULL,
    wxEvtHandler* eventSink = NULL )

bool Disconnect(
    int id, int lastId = wxID_ANY,
    wxEventType eventType = wxEVT_NULL,
    wxObjectEventFunction function = NULL,
    wxObject* userData = NULL,
    wxEvtHandler* eventSink = NULL )
Например:
frame->Disconnect( ID_MENU_EXIT, wxEVT_COMMAND_MENU_SELECTED,
                   wxCommandEventHandler(MyFrame::OnQuit) );

Но чаще применяется второй способ -- статическое связывание с помощью таблицы обработчиков событий (как в листинге . ). Для этого служат макросы BEGIN_EVENT_TABLE (начало таблицы событий) и END_EVENT_TABLE (конец таблицы). Два параметра макроса начала таблицы -- это пользовательский класс и его базовый класс библиотеки wxWidgets. Каждая строка таблицы -- тоже макрос, имя которого соответствует виду события, а параметры -- задают идентификаторы элементов и функции-обработчики. Например:

1  BEGIN_EVENT_TABLE(MyFrame, wxFrame)
2    EVT_MENU    (ID_MENU_EXIT, MyFrame::OnExit)
3    EVT_MENU    (ID_MENU_RUN,  MyFrame::Run)
4    EVT_SIZE    (              MyFrame::OnSize)
5    EVT_BUTTON  (ID_BTN1,      MyFrame::OnButton1)
6  END_EVENT_TABLE()
Здесь определены два обработчика для двух пунктов меню (2-3), обработчик изменения размеров фрейма (4) (идентификатор элемента этому событию не требуется) и обработчик нажатия на кнопку (5).

Сами функции обратного вызова определяются в этом случае как члены класса MyFrame и имеют строго определённый набор аргументов, зависящий от вида события. В документации на библиотеку wxWidgets при описании каждого класса приводятся все макросы событий и параметры функций-обработчиков. Например, обработчик выбора пункта меню:

void MyFrame::OnExit(wxCommandEvent& event) {
    .....
}
Обработчик нажатия на кнопку имеет тот же параметр event -- объект, хранящий сведения о происшедшем событии.

Для использования таблицы событий в объявление класса требуется добавить макрос DECLARE_EVENT_TABLE() и, конечно же, заголовки функций-обработчиков:

class MyFrame : public wxFrame {
public:
    .......
    void OnEnter(wxCommandEvent& event);
    .......
private:
    DECLARE_EVENT_TABLE()
};

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

Нельзя определить таблицу событий для экземпляра базового класса, например, для фрейма wxFrame. Приходится сначала определить свой класс (порождая его от wxFrame, даже если он не имеет никакой дополнительной функциональности, кроме обработки требуемого события) и только для этого класса задавать таблицу событий.

Имеется также другой способ динамического добавления функций обратного вызова -- с помощью методов PushEventHandler и PopEventHandler класса wxWindow. Мы обсудим этот вариант чуть позже.

До версии 2.0 библиотеки wxWidgets существовал ещё один метод статического связывания событий: путём переопределения виртуальных методов, предусмотренных для обработки того или иного события (например, метод OnSize соответствовал событию, происходящему при изменении размеров элемента). Сейчас этот способ почти не используется.


Разработка GUI в программе DialogBlocks

Покажем, как создать меню (см. листинг 01.cpp) в программе Anthemion DialogBlocks, в диалоговом режиме, затратив минимум усилий на написание программного кода:
  • Откроем программу Anthemion DialogBlocks (автор использовал версию 3.10, её можно бесплатно скачать с сайта www.anthemion.co.uk/dialogblocks).
  • Создадим новый проект: File | New Project (или Ctrl+N). В поле Project filename укажем полное имя файла проекта, нажмём два раза кнопку Next.
  • Поставим галочку Generate application class и зададим имя нового класса MyApp в поле Application class name. Укажем имя заголовочного файла в поле Application class header file и имя cpp-файла в поле Application class implementation file, после чего снова нажмём кнопку Next.
  • Нажмём кнопку Create Standard Configuration и из раскрывающегося списка выберем используемый нами компилятор. Нажмём кнопки OK и Next.
  • Укажем кодировку исходных текстовых программы (windows-1251 или любую другую) в графе Source encoding и кодировку файла проекта в поле Project encoding, после чего нажмём кнопку Finish.
  • Слева в дереве проекта раскроем пункт Windows (здесь перечисляются все визуальные элементы нашего проекта). Выполним команду меню Element | Add Frame. Укажем заголовок фрейма, имя класса, имя файла с определением класса и имя заголовочного файла (рис.). Нажмём кнопку OK. Теперь, нажав клавишу F5 или выполнив команду меню Element | Test Window, можно посмотреть, как выглядит наш фрейм. Пока что он пуст.

    Создание нового фрейма в DialogBlocks

  • Добавим главное меню. Для этого слева, в дереве проекта щёлкнем правой кнопкой мыши по только что созданному фрейму и в раскрывшемся контекстном меню выберем команду Add Element. На экране появится список всех визуальных элементов. Прокручивая список вниз, найдём группу Bars, в ней выделим элемент wxMenuBar и нажмём OK. В дереве проекта будет создан новый пункт, вложенный в группу MyFrame.

    Добавление пунктов меню в DialogBlocks

  • Определим команды меню. Выделим в дереве проекта объект wxMenuBar и выполним команду Element | Menubar | Add menu (или снова воспользуемся контекстным меню). Справа в редакторе свойств в графе Label укажем текст метки: &Файл. В дереве проекта выделим только что добавленное меню и выполним Element | Menubar | Add menu item. В редакторе свойств зададим текстовую метку &О программе, а в графе Id name -- идентификатор ID_MENU_ABOUT (рис.). Перейдём на закладку Event handlers (Обработчики событий) и поставим галочку напротив пункта wxEVT_COMMAND_MENU_SELECTED. Выделим щелчком мыши этот пункт и в поле Function name впишем название метода-обработчика: OnAbout. В поле Action выберем действие ShowMessage и зададим текст сообщения в графе Parameters: Эта простая программа... Добавим разделитель пунктов меню, для чего снова вернёмся на закладку Editor и выполним команду Element | Menubar | Add separator. Наконец, создадим ещё один пункт меню "В&ыход" с идентификатором ID_MENU_EXIT и обработчиком OnExit. Поле Action (действие) оставим пустым, чуть позже мы сами впишем в теле функции команду закрытия фрейма.
  • Теперь вернёмся к классу приложения, выделив его в дереве проекта. В редакторе свойств укажем главное окно Main window: ID_MYFRAME.
  • Выполним команду Build | Configurations. Выделим щелчком мыши нужную конфигурацию (Debug, Release, Unicode Debug или Unicode Release) и внизу в редакторе свойств проверим и при необходимости исправим значения параметров. Они должны совпадать со значениями, выбранными нами при компиляции библиотеки wxWidgets.
  • После этого можно выполнить компиляцию проекта: Build | Build Project. Если окажется, что заданный набор параметров не совпадает с теми режимами, которые мы использовали при компиляции wxWidgets, то программа предложит нам перекомпилировать саму библиотеку. Тогда нам останется либо вернуться к предыдущему пункту, либо согласиться на перекомпиляцию. В последнем случае после успешного окончания компиляции библиотеки придётся снова запустить компиляцию проекта.
  • Чтобы выполнить скомпилированную программу, достаточно нажать F9. Пока наша программа умеет только реагировать на выбор первого пункта меню.
  • Теперь надо дописать исходный текст программы. Текст заголовочного файла отображается на закладке h, определения функций -- на закладке cpp. Изучите сгенерированный мастером текст. Осталось сделать не так уж много. Найдём в тексте обработчик пункта меню "Выход". Удалим символ "@" в строках комментария до и после найденной строки (или удалим обе закомментированные строки) и вместо сгенерированного мастером вызова event.Skip() впишем метод закрытия фрейма: Close(). Удалив маркеры, мы запретили мастеру проекта изменять тело функции. Теперь, если мы захотим заново сгенерировать исходные тексты проекта, нажав кнопку Regenerate (она находится сверху под закладками страниц), то изменённая нами строка не будет запорчена.
Самостоятельно добавьте обработчики движения мыши и изменения размеров фрейма, как в нашем предыдущем проекте.


Дата и время в строке состояния

В одном из предыдущих проектов мы оставили одно поле строки состояния свободным, пообещав показать способ отображения в нём текущей даты и времени. Выполняем обещание. В листингах . и . приведён текст программы, а на рис. показан внешний вид приложения в системах Windows и Linux.

Дата и время строке состояния (файл examples-wx/02/02.h)

    1 // Дата и время в строке состояния. Работа с таймерами.
    2
    3 #include "wx/datetime.h"
    4
    5 // Сокращённые названия дней недели:
    6 wxString WDN[7] = {wxT("Вс"), wxT("Пд"), wxT("Вт"), wxT("Ср"),
    7                    wxT("Чт"), wxT("Пц"), wxT("Сб") };
    8
    9 class MyFrame: public wxFrame { // Главное окно.
   10 public:
   11     wxDateTime *m_datetime;       // Дата и время.
   12     wxTimer *m_timer1, *m_timer2; // Таймеры.
   13     long m_counter;               // Счетчик.
   14
   15     MyFrame(const wxString& title);    // Конструктор.
   16     void OnTimer(wxTimerEvent& event); // Обработка события таймера.
   17     void OnClose(wxCloseEvent& event); // При закрытии окна.
   18 private:
   19     DECLARE_EVENT_TABLE()
   20 };
   21
   22 enum { // Целочисленные идентификаторы таймеров.
   23     ID_TIMER1 = 100,
   24     ID_TIMER2
   25 };
   26
   27 class MyApp: public wxApp { // Приложение.
   28 public:
   29     virtual bool OnInit();
   30 };
   31 DECLARE_APP(MyApp)

Дата и время строке состояния (файл examples-wx/02/02.cpp)

    1 // Дата и время в строке состояния. Работа с таймерами.
    2 #include <wx/wx.h>
    3 #include "02.h"
    4
    5 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
    6     EVT_TIMER(-1, MyFrame::OnTimer) // При срабатывании любого таймера.
    7     EVT_CLOSE(MyFrame::OnClose)     // При закрытии окна.
    8 END_EVENT_TABLE()
    9
   10 MyFrame::MyFrame(const wxString& title)
   11         : wxFrame(NULL, -1, title) {
   12     CreateStatusBar(3);
   13     int widths[3] = { -1, 50, 170 };
   14     SetStatusWidths(3, widths);
   15     SetStatusText(wxT("Добро пожаловать в wxWidgets!"), 0);
   16     m_datetime = new wxDateTime();
   17     m_timer1 = new wxTimer(this, ID_TIMER1);
   18     m_timer1->Start(1000, wxTIMER_CONTINUOUS);
   19     m_timer2 = new wxTimer(this, ID_TIMER2);
   20     m_counter = 0;
   21     m_timer2->Start(5000, wxTIMER_CONTINUOUS);
   22 }
   23
   24 void MyFrame::OnTimer(wxTimerEvent& event) {
   25     if ( GetStatusBar() ) {
   26         long id = event.GetId(); // Какой таймер сработал?
   27         if (id == ID_TIMER1) {
   28             *m_datetime = wxDateTime::Now();
   29             wxString dw = WDN[m_datetime -> GetWeekDay()];
   30             wxString s=m_datetime->Format(wxT("%d/%m/%Y %H:%M:%S"));
   31             SetStatusText(dw + wxT(" ") + s, 2);
   32         } else {
   33             m_counter++;
   34             SetStatusText(wxString::Format(wxT("%d"),m_counter),1);
   35         }
   36     }
   37 }
   38
   39 void MyFrame::OnClose(wxCloseEvent& event) {
   40     delete m_timer1;
   41     delete m_timer2;
   42     Destroy();
   43 }
   44
   45 //-----------------------------------------
   46 IMPLEMENT_APP(MyApp)
   47 bool MyApp::OnInit() {
   48     MyFrame *frame=new MyFrame(wxT("Часы в строке состояния"));
   49     frame->CenterOnScreen(wxBOTH);
   50     frame->Show();
   51     return TRUE;
   52 }

Дата и время в строке состояния

Здесь для периодического изменения отображаемого на экране текста используются таймеры. Чтобы проиллюстрировать работу с несколькими таймерами, мы в одном поле строки состояния выводим день недели, дату и время с точностью до секунды, а в другом -- количество пятисекундных интервалов, прошедших с момента запуска программы.

Для создания таймера wxTimer используется конструктор

wxTimer(wxEvtHandler *owner, int id = -1)
В первом параметре указывается владелец таймера -- элемент, который будет получать сообщения при его срабатывании, второй параметр -- целочисленный идентификатор, по значению которого владелец сможет определить, какой именно таймер сработал.

Ещё необходимо связать событие таймера с какой-либо функцией-обработчиком, для этого используется таблица событий.

Наконец, таймер нужно включить, вызвав для него метод Start, при этом требуется указать нужную длительность в миллисекундах и режим работы: значение wxTIMER_CONTINUOUS соответствует непрерывной работе таймера, т.е. его автоматическому запуску после каждого срабатывания до тех пор, пока не будет вызван метод Stop, а значение wxTIMER_ONE_SHOT -- одиночному циклу работы (после первого же срабатывания при необходимости потребуется повторный вызов метода Start).

Ещё один проект с использованием таймеров мы рассмотрим чуть позже.


Отладка приложений wxWidgets

К этому времени наши проекты уже стали достаточно сложными, поэтому пора поговорить об их отладке.

В системе Windows для этого можно использовать отладчик, имеющийся в той интегрированной среде разработки, в которой вы работаете. К сожалению, программа DialogBlocks поддерживает интерфейс с отладчиком только для конфигураций MinGW, поэтому при использовании компилятора Microsoft C++ приходится сначала с помощью DialogBlocks создавать проект (для этого имеется стандартная конфигурация Visual C++ Project), который затем можно компилировать и отлаживать в Microsoft Visual Studio. В любом случае, это проще, чем самостоятельно указывать все нужные параметры проекта в Visual Studio.

В Linux для отладки можно воспользоваться как программой DialogBlocks, так и обычным отладчиком gdb.

Разумеется, для того, чтобы можно было вести отладку исходного текста программы, а не результата её дизассемблирования, компиляция должна проводиться в режиме Debug или Unicode Debug.

Для вывода на экран или в файл различного рода отладочной информации, в wxWidgets имеется набор классов wxLog, wxLogStderr, wxLogStream, wxLogTextCtrl, wxLogWindow, wxLogGui, wxLogNull, wxLogChain, wxLogPassThrough, wxStreamToTextRedirector. Об их использовании можно почитать в документации.


Основные элементы интерфейса

Для ввода и вывода информации в wxWidgets предусмотрены следующие визуальные элементы:
  • wxStaticText -- одна или несколько строк нередактируемого текста;
  • wxTextCtrl -- текстовое поле;
  • wxCheckBox -- независимый переключатель, имеющий два или три состояния;
  • wxRadioButton -- радиокнопка (зависимый переключатель) с двумя возможными состояниями;
  • wxRadioBox -- группа радиокнопок;
  • wxSlider -- вертикальный или горизонтальный "ползунок";
  • wxSpinButton и wxSpinCtrl -- поле для ввода чисел с кнопками инкремента/декремента;
  • wxChoice -- список, из которого можно выбрать один элемент;
  • wxComboBox -- комбинация wxTextCtrl и Choice;
  • wxListBox -- набор строк для выбора одной или нескольких из них;
  • wxCheckListBox -- список строк, где в каждой строке отображается элемент wxCheckBox;
  • wxDatePickerCtrl, wxCalendarCtrl -- для ввода даты;
  • wxButton, wxBitmapButton, wxToggleButton -- кнопки;
  • wxListCtrl, wxListView -- список строк с одним/несколькими столбцами;
  • wxTreeCtrl -- для представления иерархической информации в виде дерева;
  • wxGauge -- горизонтальный или вертикальный узкий прямоугольник, показывающий изменение некоторой величины путём частичной заливки своей площади;
  • wxStaticBitmap -- для вывода изображения.

Кратко рассмотрим эти элементы управления. Подробное описание всех методов можно найти в справочнике основных классов библиотеки wxWidgets в конце этой книги.

Надпись wxStaticText

Класс wxStaticText предназначен для отображения надписи -- одной или нескольких строк нередактируемого текста.

Обычно надпись создаётся с помощью конструктора:

wxStaticText *st = new wxStaticText(parent,id,label,pos,size,style,name);
Конструктор описан в файле wx/stattext.h следующим образом:
wxStaticText::wxStaticText(
    wxWindow* parent,  wxWindowID id,
    const wxString& label,
    const wxPoint& pos = wxDefaultPosition,
    const wxSize& size = wxDefaultSize,
    long style = 0, const wxString& name = "staticText" )
Параметры:
  • parent -- непустой указатель на родительский элемент (фрейм, диалог);
  • id -- целочисленный идентификатор создаваемого элемента (или wxID_ANY = -1);
  • label -- отображаемый текст (для разрыва строки используется "\n");
  • pos -- координаты элемента (в пикселах) в системе отсчёта родителя;
  • size -- размеры элемента в пикселах;
  • style -- набор стилей (см. ниже);
  • name -- имя элемента (обычно не используется).

Как и для других элементов, имеется второй вариант: сначала создаём "пустую" надпись

wxStaticText *st;
а затем вызываем метод Create с теми же параметрами, что и у конструктора
st->Create ( parent, id, label, pos, size, style, name);

Для параметра style используются стилевые флаги wxALIGN_LEFT, wxALIGN_RIGHT и wxALIGN_CENTRE для установки различных режимов горизонтального выравнивания текста (по левому, по правому краю, по центру). Класс wxStaticText, как и остальные элементы управления, порождён от классов wxControl, wxWindow, wxEvtHandler и wxObject, поэтому также можно использовать стили базового класса wxWindow.

Для изменения текста надписи используется метод SetLabel:

st->SetLabel( wxT("Текст\nЕще текст") );
По умолчанию размеры элемента изменяются так, чтобы наилучшим образом разместить новый текст. Можно использовать стилевой флаг wxST_NO_AUTORESIZE, чтобы отменить автоматическое изменение размеров.

Метод GetLabel (без параметров) позволяет получить текущий текст, отображаемый элементом wxStaticText.

Метод Wrap(int width) форматирует текст таким образом, чтобы ширина каждой строки (в пикселах) была как можно ближе к параметру width.

Методы для установки цвета, шрифта символов и других параметров наследуются от класса wxWindow.

Текстовое поле wxTextCtr

Класс wxTextCtrl предназначен для отображения и редактирования текста.

Конструктор:

wxTextCtrl (
    wxWindow* parent, wxWindowID id,
    const wxString& value = "",
    const wxPoint& pos = wxDefaultPosition,
    const wxSize& size = wxDefaultSize,
    long style = 0,
    const wxValidator& validator = wxDefaultValidator,
    const wxString& name = wxTextCtrlNameStr )
Параметры:
  • parent -- непустой указатель на родительский элемент (фрейм, диалог);
  • id -- целочисленный идентификатор создаваемого элемента (или -1);
  • value -- отображаемый текст;
  • pos -- координаты элемента в системе отсчёта родителя;
  • size -- размеры элемента;
  • style -- стили;
  • validator -- валидатор (см. ниже);
  • name -- имя элемента.

Кроме обычных стилей класса wxWindow, для wxTextCtrl определены следующие стилевые флаги:

  • wxTE_READONLY -- редактирование текста запрещено;
  • wxTE_MULTILINE -- многострочное поле;
  • wxTE_PASSWORD -- для ввода пароля (символы заменяются звёздочками);
  • wxTE_PROCESS_TAB -- поле ввода будет само обрабатывать нажатие на клавишу Tab (по умолчанию Tab используется для перехода между элементами интерфейса);
  • wxTE_PROCESS_ENTER -- элемент будет генерировать событие wxEVT_COMMAND_TEXT_ENTER при нажатии на клавишу Enter;
  • wxTE_RICH -- использовать RTF-форматирование (только для платформы Win32);
  • wxTE_RICH2 -- использовать RTF-форматирование версии 2.0 или 3.0 (только для платформы Win32);
  • wxTE_AUTO_URL -- для отображения адреса URL; при выборе генерируется событие wxTextUrlEvents (только на платформе Win32 при установленном флаге wxTE_RICH или на платформе wxGTK2 при установленном флаге wxTE_MULTILINE);
  • wxTE_NOHIDESEL -- на платформе Win32 показывать выделение внутри поля ввода даже когда оно не имеет фокуса (по умолчанию в Win32 выделенные символы отображаются другим стилем, только если элемент имеет фокус ввода);
  • wxHSCROLL -- показать горизонтальную полосу прокрутки и отменить перенос текста на другие строки (не работает в wxGTK1);
  • wxTE_LEFT -- выравнивание текста по левому краю (действует по умолчанию);
  • wxTE_CENTRE -- выравнивание текста по центру (работает пока только на платформах wxMSW и wxGTK2);
  • wxTE_RIGHT -- выравнивание текста по правому краю (только wxMSW и wxGTK2);
  • wxTE_DONTWRAP -- то же, что и wxHSCROLL;
  • wxTE_CHARWRAP -- переносить строки не на границе слов, как обычно, а в любом месте (только wxUniv и wxGTK2);
  • wxTE_WORDWRAP -- переносить строки по границам слов (только wxUniv и wxGTK2);
  • wxTE_BESTWRAP -- переносить строки по словам, но если слово выходит за пределы поля ввода, то разрывать слово (это правило действует по умолчанию);
  • wxTE_CAPITALIZE -- сделать первый символ заглавным (только для PocketPC и смартфонов).

Экземпляр класса wxTextCtrl генерирует события типа wxCommandEvent, для их обработки в таблице событий можно использовать следующие макросы:

  • EVT_TEXT(id, func) -- при каждом изменении текста возбуждается событие wxEVT_COMMAND_TEXT_UPDATED. Данный макрос связывает с этим событием функцию обратного вызова func. Здесь id -- целочисленный идентификатор элемента;
  • EVT_TEXT_ENTER(id, func) -- определяет обработчик для события wxEVT_COMMAND_TEXT_ENTER, которое генерируется при нажатии на клавишу Enter, если установлен стилевой флаг wxTE_PROCESS_ENTER;
  • EVT_TEXT_MAXLEN(id, func) -- если с помощью метода SetMaxLength установлена максимальное число символов, которые можно ввести с клавиатуры, то при попытке ввода б\'ольшего числа символов возбуждается событие wxEVT_COMMAND_TEXT_MAXLEN. Данный макрос определяет функцию обратного вызова для этого события. Как и прежде, id -- это числовой идентификатор.

Функции-обработчики всех перечисленных событий должны иметь единственный аргумент типа wxCommandEvent.

Список выбора wxChoice и wxComboBox

Для выбора одного пункта из заданного списка используются элементы wxChoice и wxComboBox. На платформе Windows они выглядят совершенно одинаково -- как раскрывающийся список пунктов (хотя работают немного по-разному), а в Linux элемент wxChoice более похож на всплывающее меню.

Конструкторы для обоих элементов практически ничем не отличаются:

wxChoice( wxWindow *parent, wxWindowID id, const wxPoint& pos,
          const wxSize& size, int n, const wxString choices[],
          long style = 0,
          const wxValidator& validator = wxDefaultValidator,
          const wxString& name = "choice" )

wxChoice( wxWindow *parent, wxWindowID id, const wxPoint& pos,
          const wxSize& size,
          const wxArrayString& choices,
          long style = 0,
          const wxValidator& validator = wxDefaultValidator,
          const wxString& name = "choice" )

wxComboBox( wxWindow* parent, wxWindowID id,
            const wxString& value = "",
            const wxPoint& pos = wxDefaultPosition,
            const wxSize& size = wxDefaultSize,
            int n, const wxString choices[],
            long style = 0,
            const wxValidator& validator = wxDefaultValidator,
            const wxString& name = "comboBox" )

wxComboBox( wxWindow* parent, wxWindowID id,
            const wxString& value,
            const wxPoint& pos, const wxSize& size,
            const wxArrayString& choices,
            long style = 0,
            const wxValidator& validator = wxDefaultValidator,
            const wxString& name = "comboBox" )

Список пунктов можно задать или в виде массива строк wxString (при этом, как обычно в C++, требуется указать число элементов n), или в виде экземпляра класса wxArrayString (тогда параметр n не нужен). Поэтому у каждого из конструкторов имеются две формы вызова.

В поле wxComboBox можно ввести любой текст (если только не указан стилевой флаг wxCB_READONLY), а wxChoice допускает только выбор из имеющегося списка. Поэтому конструктор wxComboBox имеет ещё один дополнительный параметр value, в котором задаётся начальный текст в поле ввода (он может не совпадать с текстом пунктов, если не задан флаг wxCB_READONLY).

Параметр style определяет набор стилевых флагов. Для wxComboBox:

  • wxCB_SIMPLE -- вместо раскрывающегося списка пунктов будет показано окно б\'ольшей высоты, в котором сразу видны все пункты (этот флаг доступен только для платформы Windows);
  • wxCB_DROPDOWN -- обычный вид (поле ввода с раскрывающимся списком);
  • wxCB_READONLY -- то же, что и wxCB_DROPDOWN, но можно только выбрать один из пунктов. Ввести в поле текст, отсутствующий в списке выбора, невозможно даже программно;
  • wxCB_SORT -- список выбора будет отсортирован по алфавиту.
Кроме перечисленных, доступны любые стилевые флаги класса wxWindow.

Для wxChoice не определены какие-либо специфические стили, поэтому используются стилевые флаги класса wxWindow.

И wxChoice, и wxComboBox наследуют свои методы от одних и те же базовых классов: wxControlWithItems, wxControl, wxWindow, wxEvtHandler и wxObject.

Установить текущий пункт можно с помощью метода SetSelection(n), где n -- номер пункта (считая с 0). Чтобы узнать номер текущего пункта, надо вызвать метод GetSelection(). Если ни один из пунктов не выбран, то возвращается wxNOT_FOUND.

К пунктам можно обращаться не только по номеру, но также указывая текст, совпадающий с какой-либо строкой из списка выбора. Так, задать текущий пункт можно с помощью метода SetStringSelection(string), а узнать текст текущего пункта -- с помощью GetStringSelection(). Т.к. текущий текст в поле ввода wxComboBox может не совпадать ни с одним из пунктов списка выбора, то для wxComboBox определён метод GetValue(), который возвращает строку wxString. Чтобы установить текущий текст в поле ввода wxComboBox, надо вызвать SetValue(text).

Список выбора можно динамически пополнять с помощью методов Append(item) и Insert(item, pos). Для удаления пункта служит Delete(n). Метод GetCount() возвращает число пунктов в списке выбора. Чтобы очистить список, надо вызвать Clear(). Можно изменить текст любого пункта с помощью метода SetString(n, string). Текущий текст для любого пункта с номером n возвращает метод GetString(n).

Для поиска строки в списке выбора используется метод FindString(string), который возвращает или номер найденного пункта (считая с 0), или значение wxNOT_FOUND (не найдено).

Для wxChoice задано только одно событие wxEVT_COMMAND_CHOICE_SELECTED. Оно происходит, когда пользователь с помощью левой кнопки мыши или клавиши Enter выбирает из списка любой пункт. Обработчик этого события определяется в таблице событий с помощью макроса

EVT_CHOICE(id, func)

Для wxComboBox определено три события: выбор пункта, изменение текста в поле ввода и нажатие клавиши Enter. Соответствующие макросы:

  • EVT_COMBOBOX(id, func) -- обработка события wxEVT_COMMAND_COMBOBOX_SELECTED (выбор пункта). Чтобы узнать выбранный текст, надо вызвать метод GetValue();
  • EVT_TEXT(id, func) -- обработка события wxEVT_COMMAND_TEXT_UPDATED (изменение текста в поле ввода);
  • EVT_TEXT_ENTER(id, func) -- обработка события wxEVT_COMMAND_TEXT_ENTER (пользователь нажал клавишу Enter, когда текстовый курсор находился в поле ввода).

Функция-обработчик всех этих событий должна иметь единственный аргумент типа wxCommandEvent.

Разумеется, для wxChoice и wxComboBox определены все события базовых классов.

Кнопки wxButton, wxBitmapButton и wxToggleButton

Кнопки wxButton могут иметь только текстовую надпись. Если вместо текста надо отображать рисунок, то используются wxBitmapButton. Если кнопка с текстовой надпистью должна иметь два устойчивых состояния: нажато/не нажато -- то для этого имеется класс wxToggleButton.

В остальном все три элемента работают практически одинаково. Базовыми для них являются классы wxControl, wxWindow, wxEvtHandler и wxObject. Кроме этого, класс wxBitmapButton произведён от wxButton.

Конструкторы:

wxButton()

wxButton( wxWindow* parent, wxWindowID id,
          const wxString& label = wxEmptyString,
          const wxPoint& pos = wxDefaultPosition,
          const wxSize& size = wxDefaultSize,
          long style = 0,
          const wxValidator& validator = wxDefaultValidator,
          const wxString& name = "button" )

wxBitmapButton()

wxBitmapButton( wxWindow* parent, wxWindowID id,
                const wxBitmap& bitmap,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& size = wxDefaultSize,
                long style = wxBU_AUTODRAW,
                const wxValidator& validator = wxDefaultValidator,
                const wxString& name = "button" )

wxToggleButton()

wxToggleButton( wxWindow* parent, wxWindowID id,
                const wxString& label,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& size = wxDefaultSize,
                long style = 0, const wxValidator& val,
                const wxString& name = "checkBox" )

Стилевые флаги wxButton:

  • wxBU_LEFT -- горизонтальное выравнивание текста на кнопке по левому краю (только для Windows и GTK+);
  • wxBU_TOP -- вертикальное выравнивание по верхнему краю (только для Windows и GTK+);
  • wxBU_RIGHT -- горизонтальное выравнивание по правому краю (только для Windows и GTK+);
  • wxBU_BOTTOM -- вертикальное выравнивание по нижнему краю (только для Windows и GTK+);
  • wxBU_EXACTFIT -- создаётся кнопка минимально возможных размеров (вместо стандартных размеров по умолчанию);
  • wxNO_BORDER -- плоская кнопка, не окружённая рамкой (только для Windows и GTK+).
Как обычно, действительны все стилевые флаги базового класса wxWindow. Класс wxBitmapButton тоже поддерживает все перечисленные стили (кроме wxBU_EXACTFIT и wxNO_BORDER), а также стиль
  • wxBU_AUTODRAW -- на платформе Windows данный флаг показывает, что кнопка должна иметь трёхмерную рамку и может отображать только одну, определённую в конструкторе пиктограмму (при этом в момент нажатия на кнопку имитируется её "вдавливание" прорисовкой утопленной рамки).
Без этого флага рамка отсутствует, а пиктограмм для отображения различных состояний кнопки можно задать целых пять: а) в обычном состоянии, б) когда кнопка имеет фокус ввода с клавиатуры, в) в момент нажатия, г) когда кнопка не доступна и д) когда указатель мыши находится над кнопкой (последнее состояние появилось только в wxWidgets версии 2.7). Определение пиктограмм для этих состояний производится не в конструкторе, а с помощью специальных методов SetBitmapLabel, SetBitmapFocus, SetBitmapSelected, SetBitmapDisabled и SetBitmapHover.

Для класса wxToggleButton никакие дополнительные стили не определены.

Установить текст надписи на кнопке wxButton можно с помощью метода SetLabel(label). Текущий текст надписи возвращает метод GetLabel().

Кнопка может являться основной для некоторой панели или диалога, в этом случае именно она будет активирована при нажатии клавиши Enter. Для установки основной кнопки диалога используется метод SetDefault() (его желательно вызывать сразу после создания кнопки, во всяком случае, перед созданием других кнопок), а для задания основной кнопки панели -- метод wxPanel::SetDefaultItem(btn).

Для wxButton определено только одно событие wxEVT_COMMAND_BUTTON_CLICKED -- кнопка отпущена после нажатия. Соответствующий макрос для таблицы обработчиков: EVT_BUTTON(id, func).

У кнопки ToggleButton, кроме этого, имеются ещё два метода: SetValue(state) устанавливает состояние кнопки (state=true -- кнопка нажата, false -- отжата) и GetValue() -- получить текущее состояние кнопки. Для задания обработчика нажатия на кнопку wxToggleButton используется макрос EVT_TOGGLEBUTTON(id, func). При программной установке состояния кнопки методом SetValue обработчик нажатия не вызывается.

Кнопка wxBitmapButton может отображать только рисунок (одновременное отображение и рисунка, и текста не реализовано из-за проблем в системе Windows). Однако, имеется дополнительная библиотека mmwx, в которой определён класс mmMultiButton -- кнопка, на которой одновременно отображаются и текст, и рисунок.


Диалог подключения к информационной системе

Рассмотрим изученные элементы управления на примере диалога подключения к некоторой информационной системе. Допустим, требуется выбрать конфигурацию (режим работы), ввести имя пользователя и пароль, после чего передать введённые данные основной программе.

В листингах . и . приведен текст программы, а на рис. показан внешний вид диалога в системах Windows и Linux.

Подключение к базе данных (файл examples-wx/login1/login1.h)

    1 // Диалог подключения к базе данных.
    2
    3 class LoginDialog : public wxDialog {
    4 public:
    5     wxComboBox *m_conf;
    6     wxTextCtrl *m_username;
    7     wxTextCtrl *m_password;
    8
    9     LoginDialog(wxWindow* parent, wxWindowID id,
   10              const wxString& title,
   11              int n, wxString confs[], int defaultConf=0);
   12     void OnChoice(wxCommandEvent& event);
   13     void OnEnter(wxCommandEvent& event);
   14     void OnStart(wxCommandEvent& event);
   15     void OnCancel(wxCommandEvent& event);
   16     void DoStart();
   17     void GetLoginData(wxArrayString& data);
   18 private:
   19     DECLARE_EVENT_TABLE()
   20 };
   21
   22 enum {
   23     ID_CONF =101,
   24     ID_USERNAME,
   25     ID_PASSWORD
   26 };
   27
   28 class MyApp : public wxApp {
   29 public:
   30     bool OnInit();
   31 };
   32 DECLARE_APP(MyApp)

В (22-26) мы определяем три идентификатора для трёх полей диалога.

Подключение к базе данных (файл examples-wx/login1/login1.cpp)

    1 // Диалог подключения к базе данных.
    2 #include <wx/wx.h>
    3 #include "login1.h"
    4
    5 BEGIN_EVENT_TABLE(LoginDialog, wxDialog)
    6     EVT_COMBOBOX(ID_CONF, LoginDialog::OnChoice)
    7     EVT_TEXT_ENTER(ID_USERNAME, LoginDialog::OnEnter)
    8     EVT_TEXT_ENTER(ID_PASSWORD, LoginDialog::OnEnter)
    9     EVT_BUTTON(wxID_OK, LoginDialog::OnStart)
   10     EVT_BUTTON(wxID_CANCEL, LoginDialog::OnCancel)
   11 END_EVENT_TABLE()
   12
   13
   14 LoginDialog::LoginDialog(
   15                 wxWindow* parent, wxWindowID id,
   16                 const wxString& title,
   17                 int n, wxString confs[],
   18                 int defaultConf )
   19         : wxDialog(parent, id, title,
   20                 wxDefaultPosition, wxSize(470, 255),
   21                 wxCAPTION | wxMINIMIZE_BOX |
   22                 wxSYSTEM_MENU | wxTHICK_FRAME ) {
   23
   24     wxFont *titleFont = new wxFont(
   25                     14, wxFONTFAMILY_SWISS,
   26                     wxFONTSTYLE_NORMAL,
   27                     wxFONTWEIGHT_BOLD, FALSE, wxT(""),
   28                     wxFONTENCODING_DEFAULT);
   29     wxFont *normalFont = new wxFont(
   30                     13, wxFONTFAMILY_SWISS,
   31                     wxFONTSTYLE_NORMAL,
   32                     wxFONTWEIGHT_NORMAL, FALSE, wxT(""),
   33                     wxFONTENCODING_DEFAULT);
   34     SetFont(*normalFont);
   35
   36     wxStaticText *st0 = new wxStaticText(
   37               this, -1, wxT("Выберете конфигурацию,\n")
   38                         wxT("введите имя пользователя и пароль"),
   39               wxPoint(0, 2), wxSize(480, 50),
   40               wxALIGN_CENTER | wxST_NO_AUTORESIZE);
   41     st0 -> SetFont(*titleFont);
   42
   43     wxStaticText *st1 = new  wxStaticText(
   44             this, -1, wxT("Конфигурация:"), wxPoint(10, 60) );
   45     wxStaticText *st2 = new wxStaticText(
   46             this, -1, wxT("Пользователь:"), wxPoint(10, 100) );
   47     wxStaticText *st3 = new wxStaticText(
   48             this, -1, wxT("Пароль:"), wxPoint(10,140) );
   49
   50     m_conf = new wxComboBox(
   51                 this, ID_CONF, wxT(""),
   52                 wxPoint(130, 56), wxSize(320, -1),
   53                 n, confs, wxCB_READONLY );
   54     m_conf->SetSelection(defaultConf);
   55
   56     m_username = new wxTextCtrl(
   57                     this, ID_USERNAME, wxT(""),
   58                     wxPoint(130, 96), wxSize(320, -1),
   59                     wxTE_PROCESS_ENTER );
   60     m_username->SetMaxLength(32);
   61
   62     m_password = new wxTextCtrl(
   63                     this, ID_PASSWORD, wxT(""),
   64                     wxPoint(130, 136), wxSize(320, -1),
   65                     wxTE_PASSWORD | wxTE_PROCESS_ENTER );
   66     m_password->SetMaxLength(32);
   67
   68     wxButton *btnStart = new wxButton(
   69                     this, wxID_OK, wxT("Вход"),
   70                     wxPoint(130, 180), wxSize(80, -1) );
   71     wxButton *btnCancel = new wxButton(
   72                     this, wxID_CANCEL, wxT("Отмена"),
   73                     wxPoint(250, 180), wxSize(80, -1) );
   74     m_username->SetFocus();
   75 }
   76
   77 void LoginDialog::OnChoice(wxCommandEvent& event) {
   78     m_username->SetFocus();
   79 }
   80
   81 void LoginDialog::OnEnter(wxCommandEvent& event) {
   82     switch (event.GetId()) {
   83         case ID_USERNAME:   m_password -> SetFocus(); break;
   84         case ID_PASSWORD:   DoStart(); break;
   85     }
   86     event.Skip();
   87 }
   88
   89 void LoginDialog::OnStart(wxCommandEvent& event) {
   90     DoStart();
   91 }
   92
   93 void LoginDialog::OnCancel(wxCommandEvent& event) {
   94     EndModal(wxID_CANCEL);
   95 }
   96
   97 void LoginDialog::DoStart() {
   98     EndModal(wxID_OK);
   99 }
  100
  101 void LoginDialog::GetLoginData(wxArrayString& data) {
  102     data.Clear();
  103     data.Add( m_conf->GetStringSelection() );
  104     data.Add( m_username->GetValue() );
  105     data.Add( m_password->GetValue() );
  106 }
  107
  108 //-----------------------------------
  109 IMPLEMENT_APP(MyApp)
  110
  111 bool MyApp::OnInit() {
  112     wxString confs[3] = {
  113         wxT("Бухгалтерия"),
  114         wxT("Зарплата"),
  115         wxT("Торговля")
  116     };
  117     LoginDialog *ld = new LoginDialog(
  118                             NULL, -1, wxT("Вход в систему"),
  119                             3, confs, 2);
  120     if ( ld -> ShowModal() == wxID_OK) {
  121         wxArrayString *data = new wxArrayString();
  122         ld->GetLoginData(*data);
  123         wxMessageDialog *dlg = new wxMessageDialog(
  124                 NULL,
  125                 wxString::Format(
  126                     wxT("Конфигурация: %s\n")
  127                     wxT("Имя пользователя: %s\n")
  128                     wxT("Пароль: %s"),
  129                     data->Item(0).c_str(),
  130                     data->Item(1).c_str(),
  131                     data->Item(2).c_str() ),
  132                 wxT("Вы ввели"),
  133                 wxOK | wxICON_INFORMATION );
  134         dlg->ShowModal();
  135         dlg->Destroy();
  136
  137         wxFrame *frame = new wxFrame(
  138                  NULL, -1, wxT("Информационная система") );
  139         frame->CenterOnScreen(wxBOTH);
  140         SetTopWindow(frame);
  141         ld->Destroy();
  142         frame->Show();
  143     } else {
  144         ld->Destroy();
  145     }
  146     return TRUE;
  147 }

Подключение к базе данных

Конструктор диалога LoginDialog (14) принимает шесть параметров: parent -- идентификатор родительского окна, id -- идентификатор самого диалога, title -- заголовок окна диалога, n и confs -- массив названий имеющихся конфигураций (режимов работы) и default -- начальный номер конфигурации при старте диалога. В конструкторе создаются четыре текстовые надписи wxStaticText (36-48): одна -- для заголовочной надписи и три -- для подписей к полям ввода. В (24-33) определяются два шрифта, в (34) устанавливается шрифт по умолчанию для всех элементов диалога, а в (41) -- шрифт для заголовочной надписи. В (50-53) создаётся поле с раскрывающимся списком для выбора конфигурации, в \(54) устанавливается номер выбранного по умолчанию пункта списка.

В (56-59) мы создаём текстовое поле для ввода имени пользователя, а в (60) ограничиваем максимальную длину текста в этом поле. В (62-66) создаём текстовое поле для ввода пароля и также ограничиваем максимальную длину текста.

В (68-73) создаются две кнопки: "Вход" и "Отмена". В (74) курсор устанавливается в поле ввода имени пользователя.

В таблице событий (5-11) определяются обработчики следующих событий: 1) выбор пункта из списка конфигураций; 2) нажатие Enter в поле ввода имени пользователя; 3) нажатие Enter в поле ввода пароля; 4) нажатие кнопки "Вход"; 5) нажатие кнопки "Отмена". Заметим, что для обработки второго и третьего события потребовалось использование стилевого флага wxTE_PROCESS_ENTER при создании полей для ввода имени пользователя (59) и пароля (65).

Обработчики первых трёх событий OnChoice (77) и OnEnter (81) просто переводят фокус ввода к следующему элементу. Обратите внимание, как в методе OnEnter определяется, какое именно поле ввода сформировало событие. Если это поле ввода пароля, то вызывается метод DoStart (97), который закрывает диалог с кодом возврата wxID_OK.

Обработчик четвёртого события (89) вызывает метод DoStart(), который, как мы уже говорили, просто закрывает диалог с кодом возврата wxID_OK. Наконец, пятое событие (93) приводит к закрытию окна с кодом возврата wxID_CANCEL.

Метод GetLoginData (101) предназначен для получения всех данных, введённых пользователем: названия конфигурации, имени пользователя и строки пароля.

В методе OnInit (111) приложения wxApp мы определяем массив из трёх строк -- названий конфигураций (112), создаём диалог (117) и выводим его на экран в модальном режиме (120). После закрытия диалога, если код возврата равен wxID_OK, то на экран выводится сообщение (123), позволяющее проконтролировать все три введённых параметра (разумеется, это служит только демонстрационным целям, в реальном приложении вместо этого сообщения потребуется проверка корректности имени пользователя и пароля). После закрытия окна сообщения (135) на экран выводится главное окно приложения (142). Пока что это пустой фрейм, мы заполним его элементами в следующих примерах.

В данном проекте мы указывали положение элементов на форме в пикселах. Этот способ не слишком желателен при разработке межплатформенных приложений, поскольку в зависимости от установленных в системе шрифтов все надписи могут "вылезти" за те пределы, что были заданы для них на той платформе, где проводилась разработка приложения. Кроме того, на различных платформах отличаются друга от друга размеры стандартных элементов форм (рамок, чекбоксов, строк состояния, меню и панелей инструментов). Реальные программы часто содержат на своих формах достаточно большое число элементов, поэтому даже малое изменение ширины или высоты любого из них может привести к заметному изменению внешнего виды всей формы.

Эту проблему можно ослабить, если вместо пикселов использовать какие-либо другие единицы измерения, которые учитывают размер текущего шрифта. Назовём такие единицы относительными.

Размер относительной единицы измерения (в оригинале dialog units) по ширине вычисляются делением на 4 средней ширины символа стандартного шрифта, а по высоте -- делением на 8 средней высоты символа. Таким образом, если указывать все координаты и размеры не в пикселах, а в относительных единицах, то при изменении размеров символов в различных операционных системах диалоговые окна будут выглядеть более-менее правильно (например, подписи не будут "наезжать" на поля ввода).

В библиотеке wxWidgets в файле include/wx/defs.h определён макрос wxDLG_UNIT:

#define wxDLG_UNIT(parent, pt) parent->ConvertDialogToPixels(pt)
который используется для преобразования относительных координат и размеров, заданных в системе отсчёта окна win, в обычные пикселы. Например, в (36-40) вместо
wxStaticText *st0 = new wxStaticText( this, -1, wxT("..."),
    wxPoint(0, 2), wxSize(480, 50),  wxALIGN_CENTER )
надо было написать, например, следующее:
wxStaticText *st0 = new wxStaticText( this, -1, wxT("..."),
    wxDLG_UNIT(this, wxPoint(0, 1)),
    wxDLG_UNIT(this, wxSize(260, 30),  wxALIGN_CENTER )

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


Менеджеры размещения

Менеджеры размещения (сайзеры) применяются для управления размещением элементов на панелях, окнах и диалогах. В wxWidgets имеются сайзеры следующих типов:
  • wxBoxSizer и wxStaticBoxSizer -- позволяют размещать элементы в одну строку или один столбец (второй отличается от первого только наличием рамки wxStaticBox с заголовком вокруг группы элементов);
  • wxGridSizer -- располагает элементы в ячейках воображаемой двумерной таблицы, размеры всех ячеек одинаковы (рассчитываются по размерам наибольшего элемента);
  • wxFlexGridSizer -- то же, что и wxGridSizer, но ширина каждого столбца и высота каждой строки подбирается индивидуально;
  • wxGridBagSizer -- то же, что и wxFlexGridSizer, но координаты каждого элемента (номер строки и столбца) указываются явно, кроме того, каждый элемент может занимать несколько смежных ячеек воображаемой таблицы по ширине и/или высоте.

Диалоги на основе сайзеров в системе Linux

На рис. показано, как выглядят два чекбокса и два текстовых поля ввода в Linux при использовании различных сайзеров.

Алгоритм работы с менеджерами размещения выглядит следующим образом:

  • Создаём сайзер, вызывая соответствующий конструктор;
  • добавляем к нему любой элемент (или пустой промежуток) с помощью методов Add, Insert или Prepend; при этом задаём горизонтальное и вертикальное выравнивание элемента и отступы от него до края ячейки, а также коэффициент растяжимости, который будет определять степень изменения размеров элемента при изменении размеров родительского окна;
  • после добавления всех элементов вызываем для окна (или панели) метод SetSizerAndFit (или последовательно два метода SetSizer и SetSizerHints), чтобы связать событие EVT_SIZE (изменение размера окна) с процедурой пересчёта размеров ячеек сайзера;
  • затем, при необходимости, к сайзеру можно динамически добавлять новые элементы.

Использование wxBoxSizer

При создании wxBoxSizer указывается только ориентация wxVERTICAL или wxHORIZONTAL:
wxBoxSizer(int orient)

Добавление элементов производится с помощью метода Add:

wxSizerItem* Add(wxWindow* window, const wxSizerFlags& flags)

wxSizerItem* Add(wxWindow* window, int proportion = 0,
        int flag = 0, int border = 0, wxObject* userData = NULL )

wxSizerItem* Add(wxSizer* sizer, const wxSizerFlags& flags)

wxSizerItem* Add(wxSizer* sizer, int proportion = 0,
        int flag = 0, int border = 0, wxObject* userData = NULL )

wxSizerItem* Add(int width, int height, int proportion = 0,
        int flag = 0, int border = 0, wxObject* userData = NULL )
Как видим, можно добавить не только экземпляр класса wxWindow (или производный от него), но и другой сайзер, а также пустой промежуток заданного размера.

Параметр flag -- это набор битов:

  • wxTOP -- элемент "прикрепляется" к верхней границе ячейки;
  • wxBOTTOM -- к нижней границе;
  • wxLEFT -- к левой границе;
  • wxRIGHT -- к правой границе;
  • wxALL -- ко всем четырём границам;
  • wxEXPAND -- элемент будет расширен, чтобы занять всё доступное пространство своей ячейки;
  • SHAPED -- элемент будет расширен, чтобы занять всё доступное пространство своей ячейки, но его пропорции (отношение высоты к ширине) будут сохранены;
  • wxFIXED_MINSIZE -- по умолчанию при вычислении минимальных размеров каждой ячейки вызывается метод wxWindow::GetAdjustedBestSize для элемента, занимающего эту ячейку. Таким образом, результат может измениться при изменении элемента. Но при установке этого флага минимальный размер будет зафиксирован;
  • wxALIGN_CENTER -- выравнивание элемента по центру;
  • wxALIGN_LEFT -- горизонтальное выравнивание по левому краю;
  • wxALIGN_RIGHT -- горизонтальное выравнивание по правому краю;
  • wxALIGN_TOP -- вертикальное выравнивание по верхнему краю;
  • wxALIGN_BOTTOM -- вертикальное выравнивание по нижнему краю;
  • wxALIGN_CENTER_VERTICAL -- вертикальное выравнивание по центру;
  • wxALIGN_CENTER_HORIZONTAL -- горизонтальное выравнивание по центру.
Параметр proportion определяет коэффициент растяжимости добавляемого элемента в основном направлении сайзера (для orient=wxVERTICAL указывается растяжение по вертикали, для orient=wxHORIZONTAL -- по горизонтали). Если коэффициент равен 0, то размеры элемента сохраняются при изменении размера окна; любое положительное значение задаёт степень изменения размера элемента по сравнению с другими. Если коэффициенты растяжимости всех элементов равны, то их размер одинаково растёт при увеличении размера окна.

Параметр border задаёт расстояние от края элемента до тех границ ячейки, к которым он "прикреплён" согласно значению flag.

Последний параметр позволяет связать с добавляемым элементом какой-либо объект данных (например, имя столбца таблицы базы данных, из которого берутся значения для данного элемента).

С помощью метода Add элементы добавляются в конец строки (столбца), т.е. правее (ниже) уже имеющихся элементов. Если требуется нарушить этот порядок, можно использовать метод Insert:

wxSizerItem* Insert( size_t index, wxWindow* window,
                     const wxSizerFlags& flags )

wxSizerItem* Insert(size_t index, wxWindow* window,
                    int proportion = 0,int flag = 0,
                    int border = 0,
                    wxObject* userData = NULL)

wxSizerItem* Insert( size_t index, wxSizer* sizer,
                     const wxSizerFlags& flags )

wxSizerItem* Insert(size_t index, wxSizer* sizer,
                    int proportion = 0, int flag = 0,
                    int border = 0,
                    wxObject* userData = NULL)

wxSizerItem* Insert(size_t index, int width, int height,
                    int proportion = 0, int flag = 0,
                    int border = 0,
                    wxObject* userData = NULL)
где параметр index задаёт номер ячейки (считая с 0), в которую вставляется элемент. При вставке все остальные элементы сдвигаются вправо (вниз).

Чтобы вставить элемент в самую первую (т.е. нулевую) позицию, использую метод Prepend:

wxSizerItem* Prepend(wxWindow* window, const wxSizerFlags& flags)

wxSizerItem* Prepend(wxWindow* window, int proportion = 0,
                     int flag = 0, int border = 0,
                     wxObject* userData = NULL)

wxSizerItem* Prepend(wxSizer* sizer, const wxSizerFlags& flags)

wxSizerItem* Prepend(wxSizer* sizer, int proportion = 0,
                     int flag = 0, int border = 0,
                     wxObject* userData = NULL)

wxSizerItem* Prepend(int width, int height, int proportion = 0,
                     int flag = 0, int border= 0,
                     wxObject* userData = NULL)

Чтобы удалить элемент из ячейки сайзера, используется метод Detach:

bool Detach(wxWindow* window)
bool Detach(wxSizer* sizer)
bool Detach(size_t index)
который возвращает true, если указанный элемент найден и успешно удалён. При этом сам элемент не уничтожается (деструктор для него не вызывается), а все остальные элементы сдвигаются влево (вверх). Чтобы удалить элемент и уничтожить его (т.е. вызвать деструктор), применяется метод Remove с теми же параметрами.

После добавления (удаления) элементов, иногда требуется вызвать метод Layout(), который инициирует переразмещение элементов с сохранением их размеров, и метод RecalcSizes(), который вызывает перерасчёт размера сайзера и его элементов.

Диалог с wxBoxSizer в системе Windows

Небольшой пример использования wxBoxSizer приведён в листинге, а на рис. показан внешний вид диалога в системе Windows.

Пример использовая wxBoxSizer

    1 MyDialog::MyDialog( wxWindow *parent,
    2                     wxWindowID id,
    3                     const wxString &title)
    4         : wxDialog( parent, id, title,
    5                     wxDefaultPosition, wxDefaultSize,
    6                     wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
    7
    8     // Создаём wxBoxSizer:
    9     wxBoxSizer  *topSizer = new wxBoxSizer(wxVERTICAL);
   10
   11     // Добавляем поле для ввода текста, минимальные размеры 100x60:
   12     topSizer->Add(new wxTextCtrl( this, wxID_ANY, wxT(""),
   13                   wxDefaultPosition,wxSize(100,60),wxTE_MULTILINE),
   14             1,          // Коэффициент растяжения по вертикали.
   15             wxEXPAND |  // По горизонтали также будет растяжение.
   16             wxALL,      // Прикреплён ко всем четырём границам.
   17             10 ); // Расстояние 10 пикселей от границ до элемента.
   18
   19     // Создаём сайзер для кнопок:
   20     wxBoxSizer *buttonSizer = new wxBoxSizer( wxHORIZONTAL );
   21
   22     // Добавляем кнопку:
   23     buttonSizer->Add( new wxButton( this, wxID_OK, "OK" ),
   24             0,     // Кнопка по горизонтали не растяжима.
   25             wxALL, // Прикреплена ко все границам,
   26                    // по умолчанию выравнивание по верхнему краю.
   27             10 );  // Расстояние 10 пикселей от границ до кнопки.
   28
   29     // Добавляем кнопку:
   30     buttonSizer->Add( new wxButton( this, wxID_CANCEL, "Cancel" ),
   31             0,    // Кнопка по горизонтали не растяжима.
   32             wxALL,// Прикреплена ко все границам,
   33                   // по умолчанию выравнивание по верхнему краю.
   34             10 ); // Расстояние 10 пикселей от границ до кнопки.
   35
   36     // Добавляем сайзер с кнопками к главному сайзеру:
   37     topSizer->Add( buttonSizer,
   38             0, // Сайзер кнопок не растягивается по вертикали.
   39             wxALIGN_CENTER ); // Без границ, выравнивание по центру.
   40
   41     SetSizer( topSizer );     // Устанавливаем главный сайзер окна.
   42     topSizer->Fit( this );    // Пересчёт размеров ячеек сайзера.
   43     topSizer->SetSizeHints( this ); // Устанавливаем размер окна, исходя
   44                             // из минимально возможных размеров сайзера.
   45 }

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

Использование wxStaticBoxSizer

Как уже говорилось, менеджер размещения wxStaticBoxSizer отличается от своего родителя wxBoxSizer только тем, что управляет размерами рамки wxStaticBox. Заметим, что рамку надо создать отдельно и передать при создании wxStaticBoxSizer в качестве параметра, хотя в последних релизах wxWidgets это требование уже отменено, т.к. появилась вторая форма конструктора, где рамка создаётся автоматически:
wxStaticBoxSizer(wxStaticBox* box, int orient)

wxStaticBoxSizer( int orient, wxWindow *parent,
                  const wxString& label = wxEmptyString )
Здесь label -- это текст надписи для рамки.

Пример использования wxStaticBoxSizer приведён в листинге, на рис. показан внешний вид диалога в системе Windows при двух различных размерах окна, а на рис. -- внешний вид диалога в системе Linux.

Диалог с wxStaticBoxSizer в системе Windows при различных размерах окна

Диалог с wxStaticBoxSizer в системе Linux

Использование wxStaticBoxSizer (файл examples-wx/sboxsizer/sboxsizer.cpp)

    1 // Пример использования wxStaticBoxSizer.
    2
    3 #include <wx/wx.h>
    4
    5 class MyDialog: public wxDialog {
    6 public:
    7     MyDialog( wxWindow *parent, wxWindowID id,
    8               const wxString& title );
    9 };
   10
   11 class MyApp: public wxApp {
   12 public:
   13     virtual bool OnInit();
   14 };
   15 DECLARE_APP(MyApp)
   16
   17 //-----------------------------------------------
   18 MyDialog::MyDialog( wxWindow *parent,
   19                          wxWindowID id,
   20                          const wxString& title )
   21     : wxDialog(parent, id, title,
   22                wxDefaultPosition, wxDefaultSize,
   23                wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
   24
   25     // Создаём главный сайзер wxBoxSizer:
   26     wxBoxSizer *topSizer = new wxBoxSizer( wxVERTICAL );
   27
   28     // Создаём рамку wxStaticBox,
   29     // а затем сайзер wxStaticBoxSizer:
   30     wxStaticBox *sbox1 = new wxStaticBox(this,
   31                                      wxID_ANY,
   32                                      wxT("Получатель") );
   33     wxStaticBoxSizer *sizer1 = new wxStaticBoxSizer(
   34                                      sbox1, wxVERTICAL );
   35
   36     // Создаём надпись и поле для ввода ФИО.
   37     // Добавляем их к сайзеру.
   38     sizer1->Add( new wxStaticText(
   39                         this, wxID_ANY,
   40                         wxT("ФИО")),
   41                  0, wxLEFT, 3 );
   42     wxTextCtrl *tc1 = new wxTextCtrl(
   43         this, wxID_ANY, wxT(""),
   44         wxDefaultPosition, wxSize(200, -1) );
   45     sizer1->Add( tc1, 0, wxALL|wxEXPAND, 2 );
   46
   47     // Создаём надпись и поле для ввода адреса.
   48     // Добавляем их к сайзеру.
   49     sizer1->Add( new wxStaticText(
   50                         this, wxID_ANY, wxT("Адрес")),
   51                  0, wxLEFT, 3 );
   52     wxTextCtrl *tc2 = new wxTextCtrl(
   53                             this, wxID_ANY, wxT(""),
   54                             wxDefaultPosition, wxSize(200, -1),
   55                             wxTE_MULTILINE );
   56     sizer1->Add( tc2, 1, wxALL|wxEXPAND, 2 );
   57
   58     // Создаём рамку wxStaticBox, а затем сайзер wxStaticBoxSizer:
   59     wxStaticBox *sbox2 = new wxStaticBox(this, wxID_ANY,
   60                                          wxT("Отправитель") );
   61     wxStaticBoxSizer *sizer2 = new wxStaticBoxSizer(sbox2,
   62                                                     wxVERTICAL);
   63     // Создаём надпись и поле для ввода ФИО. Добавляем их к сайзеру.
   64     sizer2->Add( new wxStaticText( this, wxID_ANY, wxT("ФИО") ),
   65                  0, wxLEFT, 3 );
   66     wxTextCtrl *tc3 = new wxTextCtrl(this, wxID_ANY, wxT(""),
   67                                      wxDefaultPosition,
   68                                      wxSize(200, -1) );
   69     sizer2->Add( tc3, 0, wxALL|wxEXPAND );
   70
   71     // Создаём надпись и поле для ввода адреса.
   72     // Добавляем их к сайзеру.
   73     sizer2->Add( new wxStaticText(this, wxID_ANY, wxT("Адрес")),
   74                                   0, wxLEFT, 3 );
   75     wxTextCtrl *tc4 = new wxTextCtrl(this, wxID_ANY, wxT(""),
   76                                      wxDefaultPosition,
   77                                      wxSize(200, -1),
   78                                      wxTE_MULTILINE );
   79     sizer2->Add( tc4, 1, wxALL|wxEXPAND, 2 );
   80
   81     // Добавляем сайзеры с полями ввода к главному сайзеру:
   82     topSizer->Add(sizer1, 1, wxALL|wxEXPAND, 2 );
   83     topSizer->Add(sizer2, 1, wxALL|wxEXPAND, 2 );
   84
   85     // Создаём сайзер для кнопок:
   86     wxBoxSizer *buttonSizer = new wxBoxSizer( wxHORIZONTAL );
   87
   88     // Добавляем кнопку:
   89     buttonSizer->Add( new wxButton( this, wxID_OK, wxT("OK") ),
   90                       0, wxALL, 10 );
   91
   92     // Добавляем кнопку:
   93     buttonSizer->Add( new wxButton( this, wxID_CANCEL, wxT("Cancel")),
   94                       0, wxALL, 10 );
   95
   96     // Добавляем сайзер с кнопками к главному сайзеру:
   97     topSizer->Add( buttonSizer, 0, wxALIGN_CENTER );
   98
   99     // Устанавливаем главный сайзер окна:
  100     SetSizerAndFit( topSizer );
  101 }
  102
  103 //------------------------------------------------------
  104 IMPLEMENT_APP(MyApp)
  105
  106 bool MyApp::OnInit() {
  107     MyDialog *frame = new MyDialog(
  108             NULL, wxID_ANY, wxT("wxStaticBoxSizer") );
  109     SetTopWindow(frame);
  110     frame->ShowModal();
  111     frame->Destroy();
  112     return TRUE;
  113 }

Здесь при увеличении ширины и высоты окна будет увеличиваться ширина всех четырёх полей ввода, а также высота двух полей для ввода адреса. Высота полей для ввода фамилии сохраняется неизменной (см. рис.).

Использование wxGridSizer

При создании wxGridSizer указывается начальное число строк и столбцов воображаемой таблицы, в ячейках которой будут располагаться все элементы, а также дополнительное расстояние между соседними ячейками по вертикали и горизонтали:
wxGridSizer(int rows, int cols, int vgap, int hgap)
wxGridSizer(int cols, int vgap = 0, int hgap = 0)
Если значение rows и/или cols равно 0, то оно будет вычислено автоматически.

Для добавления элементов к wxGridSizer используются тот же самый метод Add, что и для wx(Static)BoxSizer. Добавляемые элементы располагаются по строкам: сначала заполняется первая строка, затем вторая и т.д. Заметим, что по мере добавления новых элементов число строк и/или столбцов может свободно превысить указанное в конструкторе значение.

Применение wxGridSizer иллюстрируется в листинге, на рис. показан внешний вид диалога в системе Windows. Здесь 9 кнопок располагаются в таблице 3x3, для центральной кнопки задан режим растяжения с сохранением пропорций, а для всех остальных -- режим выравнивания с сохранением размеров. Заметим, что при увеличении размеров окна все столбцы и строки сайзера растягиваются одинаково, т.к. по определению wxGridSizer поддерживает одинаковый размер всех своих ячеек. Можно управлять только растяжением элемента внутри ячейки.

Диалог с wxGridSizer в системе Windows

Пример использования wxGridSizer (файл gridsizer/gridsizer.cpp)

    1 // Пример использования wxGridSizer.
    2
    3 #include <wx/wx.h>
    4
    5 class MyDialog: public wxDialog {
    6 public:
    7     MyDialog( wxWindow *parent, wxWindowID id,
    8               const wxString& title );
    9 };
   10
   11 class MyApp: public wxApp {
   12 public:
   13     virtual bool OnInit();
   14 };
   15 DECLARE_APP(MyApp)
   16
   17 //-----------------------------------------------
   18 MyDialog::MyDialog( wxWindow *parent,
   19                     wxWindowID id,
   20                     const wxString& title )
   21         : wxDialog(
   22                parent, id, title,
   23                wxDefaultPosition, wxDefaultSize,
   24                wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
   25
   26         // Создаём wxGridSizer:
   27         wxGridSizer *gs = new wxGridSizer( 3, 3, 1, 1 );
   28
   29         // Создаём 9 кнопок:
   30         wxButton *btns[9];
   31         for (int i = 0; i < 9; i++)
   32             btns[i] = new wxButton(
   33                  this, wxID_ANY,
   34                  wxString::Format(wxT("%d"), i),
   35                  wxDefaultPosition,
   36                  wxSize(50,35) ) ;
   37
   38         // Добавляем кнопки к wxGridSizer:
   39         gs->Add( btns[0], 0, wxALIGN_LEFT|wxALIGN_TOP);
   40         gs->Add( btns[1], 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_TOP);
   41         gs->Add( btns[2], 0, wxALIGN_RIGHT|wxALIGN_TOP);
   42         gs->Add( btns[3], 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
   43         gs->Add( btns[4], 1,
   44                 wxALIGN_CENTER_HORIZONTAL|
   45                 wxALIGN_CENTER_VERTICAL|wxSHAPED);
   46         gs->Add( btns[5], 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL);
   47         gs->Add( btns[6], 0, wxALIGN_LEFT|wxALIGN_BOTTOM);
   48         gs->Add( btns[7], 0, wxALIGN_CENTER_HORIZONTAL|
   49                                                     wxALIGN_BOTTOM);
   50         gs->Add( btns[8], 0, wxALIGN_RIGHT|wxALIGN_BOTTOM);
   51
   52         // Устанаваливаем сайзер для окна:
   53         SetSizerAndFit( gs );
   54 }
   55
   56 //------------------------------------------------------
   57 IMPLEMENT_APP(MyApp)
   58
   59 bool MyApp::OnInit() {
   60     MyDialog *frame = new MyDialog(
   61             NULL, wxID_ANY, wxT("wxGridSizer") );
   62     SetTopWindow(frame);
   63     frame->ShowModal();
   64     frame->Destroy();
   65     return TRUE;
   66 }
   67

Использование wxFlexGridSizer

Ограничение на размер ячеек снято в классе wxFlexGridSizer: теперь ширину столбцов и высоту строк можно сделать различными. Чтобы задать "эластичное" направление, в котором wxFlexGridSizer будет поддерживать различную высоту строк или ширину столбцов, надо вызвать метод SetFlexibleDirections(direction). Если direction=wxHORIZONTAL, то "эластичными" будут столбцы (будет различаться их ширина), а если direction=wxVERTICAL -- то строки (будет различаться их высота). По умолчанию direction=wxBOTH, т.е. "эластичны" оба направления.

Затем можно указать, как будет изменяться размер ячеек в "неэластичном" направлении (если оно есть), вызвав метод GetNonFlexibleGrowMode(mode). Если mode=wxFLEX_GROWMODE_NONE, то в этом направлении размер ячеек изменяться не будет. При wxFLEX_GROWMODE_ALL размер всех ячеек в "неэластичном" направлении будет возрастать одинаково. По умолчанию действует режим mode=wxFLEX_GROWMODE_SPECIFIED, когда увеличивается размер только тех строк (столбцов), для которых вызван метод AddGrowableRow или AddGrowableCol.

Методы AddGrowableRow(idx, proportion) и AddGrowableCol(idx, proportion) позволяют указать, какие именно строки и столбцы будут "эластичными". Здесь idx -- номер "эластичной" строки (столбца), считая с 0, а proportion -- коэффициент, позволяющий задать различную степень изменения размера у разных элементов.

Пример использования wxFlexGridSizer иллюстрируется в листинге, на рис. показан внешний вид диалога в системе Windows.

Диалог с wxFlexGridSizer в системе Windows

Пример использования wxFlexGridSizer (файл examples-wx/fgs/fgs.cpp)

    1 // Пример использования wxFlexGridSizer.
    2
    3 #include <wx/wx.h>
    4
    5 class MyDialog: public wxDialog {
    6 public:
    7     MyDialog( wxWindow *parent, wxWindowID id,
    8               const wxString& title );
    9 };
   10
   11 class MyApp: public wxApp {
   12 public:
   13     virtual bool OnInit();
   14 };
   15 DECLARE_APP(MyApp)
   16
   17 //-----------------------------------------------
   18 MyDialog::MyDialog( wxWindow *parent,
   19                     wxWindowID id,
   20                     const wxString& title )
   21         : wxDialog(
   22                parent, id, title,
   23                wxDefaultPosition, wxDefaultSize,
   24                wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
   25
   26         // Создаём wxFlexGridSizer:
   27         wxFlexGridSizer *fgs = new wxFlexGridSizer(3, 3, 1, 1);
   28
   29         // Эластичными будут оба направления:
   30         fgs->SetFlexibleDirection(wxBOTH);
   31
   32         // Если бы было неэластичное направление, то здесь мы бы
   33         // вызвали метод fgs->SetNonFlexibleGrowMode(...);
   34
   35         // Средняя колонка и средняя строка будут эластичными:
   36         fgs->AddGrowableCol(1, 0);
   37         fgs->AddGrowableRow(1, 0);
   38
   39         // Создаём 9 кнопок:
   40         wxButton *btns[9];
   41         for (int i = 0; i < 9; i++)
   42             btns[i] = new wxButton(
   43                  this, wxID_ANY,
   44                  wxString::Format(wxT("%d"), i),
   45                  wxDefaultPosition,
   46                  wxSize(50,35) ) ;
   47
   48         // Добавляем кнопки к wxFlexGridSizer:
   49         fgs->Add( btns[0], 0, wxALIGN_LEFT|wxALIGN_TOP);
   50         fgs->Add( btns[1], 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_TOP);
   51         fgs->Add( btns[2], 0, wxALIGN_RIGHT|wxALIGN_TOP);
   52         fgs->Add( btns[3], 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
   53         fgs->Add( btns[4], 1, wxALIGN_CENTER_HORIZONTAL|
   54                               wxALIGN_CENTER_VERTICAL|wxEXPAND);
   55         fgs->Add( btns[5], 0, wxALIGN_RIGHT|
   56                               wxALIGN_CENTER_VERTICAL);
   57         fgs->Add( btns[6], 0, wxALIGN_LEFT|wxALIGN_BOTTOM);
   58         fgs->Add( btns[7], 0, wxALIGN_CENTER_HORIZONTAL|
   59                               wxALIGN_BOTTOM);
   60         fgs->Add( btns[8], 0, wxALIGN_RIGHT|wxALIGN_BOTTOM);
   61
   62         // Увеличим размер центральнрй кнопки:
   63         btns[4]->SetMinSize( wxSize(70, 50) );
   64
   65         // Устанаваливаем размеры сайзера по размерам окна:
   66         SetSizer(fgs);
   67         fgs->Fit(this);
   68         fgs->SetSizeHints(this);
   69 }
   70
   71 //------------------------------------------------------
   72 IMPLEMENT_APP(MyApp)
   73
   74 bool MyApp::OnInit() {
   75     MyDialog *frame = new MyDialog(
   76             NULL, wxID_ANY, wxT("wxFlexGridSizer") );
   77     SetTopWindow(frame);
   78     frame->ShowModal();
   79     frame->Destroy();
   80     return TRUE;
   81 }
   82

Приведём ещё один пример использования wxFlexGridSizer -- диалог ввода периода времени (интервала между двумя датами). Текст программы приведён в листингах . и ., а внешний вид диалога -- на рис.

Диалог с wxFlexGridSizer: ввод периода времени

Чтобы облегчить использование этого диалога в других проектах, мы поместили определение класса PeriodDialog и функцию GetPeriod для его вызова в отдельный файл.

Диалог для ввода периода (файл examples-wx/period1/period1.h)

    1 #include <wx/datectrl.h>
    2
    3 class  PeriodDlg: public wxDialog {
    4 public:
    5     wxDatePickerCtrlBase *m_dpc1;
    6     wxDatePickerCtrlBase *m_dpc2;
    7
    8     PeriodDlg(wxWindow* parent,
    9               wxWindowID id,
   10               const wxString& title=wxT("Введите период"),
   11               bool allowNone=TRUE,
   12               const wxPoint& pos=wxDefaultPosition);
   13 private:
   14     DECLARE_EVENT_TABLE()
   15 };
   16
   17 bool GetPeriod(wxWindow* parent,
   18                wxDateTime& dFrom,
   19                wxDateTime& dTo,
   20                const wxString& title=wxT("Введите период"),
   21                bool allowNone=TRUE);

Диалог для ввода периода (файл examples-wx/period1/period1.cpp)

    1 // Диалог для ввода периода (две даты)
    2
    3 #include <wx/wx.h>
    4
    5 #include "period1.h"
    6
    7 BEGIN_EVENT_TABLE(PeriodDlg, wxDialog)
    8 END_EVENT_TABLE()
    9
   10 PeriodDlg::PeriodDlg(
   11                wxWindow* parent, wxWindowID id,
   12                const wxString& title,
   13                bool allowNone,
   14                const wxPoint& pos)
   15          : wxDialog(parent, id, title,
   16                     pos, wxDefaultSize,
   17                     wxCAPTION|wxRESIZE_BORDER) {
   18
   19     wxFont font = wxFont(14, wxFONTFAMILY_SWISS,
   20                   wxFONTSTYLE_NORMAL,
   21                   wxFONTWEIGHT_NORMAL,
   22                   FALSE, wxT("") );
   23     SetFont(font);
   24
   25     wxFlexGridSizer *fgs1 = new wxFlexGridSizer(2,1,3,3);
   26     fgs1->AddGrowableCol(0);
   27     fgs1->AddGrowableRow(0);
   28     fgs1->SetFlexibleDirection(wxBOTH);
   29
   30     wxStaticBox *stbox = new wxStaticBox(this, wxID_ANY,
   31                             wxT("Период:") );
   32     wxStaticBoxSizer *sbs = new wxStaticBoxSizer(
   33                                     stbox, wxVERTICAL);
   34
   35     wxFlexGridSizer *fgs2 = new wxFlexGridSizer(2, 2, 8, 10);
   36     fgs2->AddGrowableCol(1);
   37     fgs2->SetFlexibleDirection(wxHORIZONTAL);
   38
   39     long st = wxDP_DROPDOWN | wxDP_SHOWCENTURY;
   40     if (allowNone)
   41         st |= wxDP_ALLOWNONE;
   42
   43     fgs2->Add(new wxStaticText(this, wxID_ANY, wxT("С")),
   44              0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT);
   45     m_dpc1 = new wxDatePickerCtrl(
   46         this, wxID_ANY, wxDefaultDateTime,
   47         wxDefaultPosition, wxDefaultSize, st);
   48     fgs2->Add(m_dpc1, 1, wxALL|wxGROW);
   49
   50     fgs2->Add(new wxStaticText(this, wxID_ANY, wxT("По")),
   51              0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT);
   52     m_dpc2 = new wxDatePickerCtrl(
   53         this, wxID_ANY, wxDefaultDateTime,
   54         wxDefaultPosition, wxDefaultSize, st);
   55
   56     fgs2->Add(m_dpc2, 1, wxALL|wxGROW);
   57     sbs->Add(fgs2, 1, wxALL|wxGROW);
   58     fgs1->Add(sbs, 1, wxALL|wxGROW);
   59
   60     wxStdDialogButtonSizer *sdbs =
   61                     new wxStdDialogButtonSizer();
   62     wxButton *btnOk = new wxButton(
   63                         this, wxID_OK, wxT("&Да"));
   64     sdbs->AddButton(btnOk);
   65     btnOk->SetDefault();
   66
   67     wxButton *btnCancel = new wxButton(
   68                         this, wxID_CANCEL,
   69                         wxT("&Нет"));
   70     sdbs->AddButton(btnCancel);
   71
   72     sdbs->Realize();
   73     fgs1->Add(sdbs, 0, wxALL|wxALIGN_CENTER, 8);
   74
   75     SetSizerAndFit(fgs1);
   76     wxSize sz = GetSize();
   77     SetSize(sz.GetWidth()+25, sz.GetHeight());
   78 }
   79
   80 //------------------------------------------------------
   81 bool GetPeriod(wxWindow* parent,
   82                wxDateTime& dFrom,
   83                wxDateTime& dTo,
   84                const wxString& title,
   85                bool allowNone) {
   86     PeriodDlg *dlg = new PeriodDlg(
   87                 parent, wxID_ANY, title, allowNone);
   88     if (dlg->ShowModal() == wxID_OK) {
   89         dFrom = dlg->m_dpc1->GetValue();
   90         dTo = dlg->m_dpc2->GetValue();
   91         dlg->Destroy();
   92             return TRUE;
   93     }else{
   94         dlg->Destroy();
   95         return FALSE;
   96     }
   97 }
Здесь мы создали сайзер wxFlexFridSizer (25) с двумя строками и одним столбцом, в верхнюю ячейку которого вставили сайзер wxStaticBoxSizer (30,58), чтобы нарисовать рамку вокруг элементов диалога. Внутри рамки мы разместили ещё один сайзер wxFlexFridSizer (35,57) размером 2x2, в котором создали две надписи wxStaticText (43,50) и два поля для ввода даты wxDatePickerCtrl (45,52). В нижнюю ячейку внешнего сайзера поместили сайзер для кнопок wxStdDialogButtonSizer (60), который будет рассмотрен чуть позже в этой главе.

Пример программы для тестирования диалога показан в листинге.

Программа для тестирования диалога (файл examples-wx/period1/period1.h),

    1 // Тестовая прорграмма для диалога выбора диапазона дат.
    2
    3 #include <wx/wx.h>
    4
    5 #include "period1.h"
    6
    7 class PeriodDlgTestApp : public wxApp {
    8 public:
    9     bool OnInit();
   10 private:
   11 };
   12 DECLARE_APP(PeriodDlgTestApp)
   13 IMPLEMENT_APP(PeriodDlgTestApp)
   14
   15 bool PeriodDlgTestApp::OnInit() {
   16     static wxDateTime d1 = wxInvalidDateTime;
   17     static wxDateTime d2 = wxInvalidDateTime;
   18     wxString msg;
   19     if (GetPeriod(NULL, d1, d2)) {
   20         msg = wxT("Период:\n");
   21         if (d1 != wxDefaultDateTime) {
   22             msg += wxString::Format(
   23                 wxT("с   %02d/%02d/%04d\n"),
   24                 d1.GetDay(), d1.GetMonth()+1,
   25                 d1.GetYear() );
   26         }else
   27             msg += wxT("с ---\n");
   28         if (d2 != wxDefaultDateTime) {
   29             msg += wxString::Format(
   30                 wxT("по %02d/%02d/%04d\n"),
   31                 d2.GetDay(), d2.GetMonth()+1,
   32                 d2.GetYear() );
   33         }else
   34             msg += wxT("по ---\n");
   35     }else
   36         msg = wxT("Вы отказались от выбора периода!");
   37
   38     wxMessageBox(msg, wxT("Результат"), wxOK);
   39     return TRUE;
   40 }

Использование wxGridBagSizer

Предыдущие сайзеры могли размещать каждый элемент только в одной из своих ячеек. Класс wxGridBagSizer используется, если требуется разместить какие-либо элементы в нескольких смежных по вертикали и/или горизонтали ячейках.

При создании wxGridBagSizer можно задать только расстояние между ячейками по вертикали и горизонтали:

wxGridBagSizer(int vgap = 0, int hgap = 0)

Новые элементы добавляются с помощью метода Add:

wxSizerItem* Add(wxWindow* window,
    const wxGBPosition& pos,
    const wxGBSpan& span = wxDefaultSpan,
    int flag = 0, int border = 0,
    wxObject* userData = NULL)

wxSizerItem* Add(wxSizer* sizer,
    const wxGBPosition& pos,
    const wxGBSpan& span = wxDefaultSpan,
    int flag = 0, int border = 0,
    wxObject* userData = NULL)

wxSizerItem* Add(int width, int height,
    const wxGBPosition& pos,
    const wxGBSpan& span = wxDefaultSpan,
    int flag = 0, int border = 0,
    wxObject* userData = NULL)

wxSizerItem* Add(wxGBSizerItem* item)
Здесь параметр pos задаёт номер строки и столбца, span -- число соседних ячеек по вертикали и горизонтали. Например, если надо, чтобы кнопка располагалась в первой (считая с 0) строке, в нулевом столбце и занимала две строки по вертикали и три столбца по горизонтали:
gbs->Add( btn, wxGBPosition(1, 0), wxGBSpan(2, 3) )
Метод Add возвращает ненулевой указатель, если элемент успешно добавлен (т.е. если все указанные ячейки не заняты другими элементами).

Чтобы узнать, не пересекается ли данный элемент или позиция сайзера с другими элементами, используется метод CheckForIntersection:

bool CheckForIntersection( wxGBSizerItem* item,
                           wxGBSizerItem* excludeItem = NULL )

bool CheckForIntersection( const wxGBPosition& pos,
                           const wxGBSpan& span,
                           wxGBSizerItem* excludeItem = NULL)
Возвращается true, если элемент item (или группа ячеек pos, span) пересекается с каким-либо другим элементом сайзера. В параметре excludeItem, если требуется, указывается элемент, который не надо учитывать.

В остальном класс wxGridBagSizer работает аналогично своему "родителю" wxFlexGridSizer.

Пример использования wxGridBagSizer показан в листинге, на рис. приведён внешний вид диалога в системе Windows.

Диалог с wxGridBagSizer в системе Windows

Пример использования wxGridBagSizer (файл examples-wx/gbs/gbs.cpp)

    1 // Пример использования wxGridBagSizer.
    2
    3 #include <wx/wx.h>
    4 #include <wx/gbsizer.h>
    5
    6 class MyDialog: public wxDialog {
    7 public:
    8     MyDialog( wxWindow *parent, wxWindowID id,
    9               const wxString& title );
   10 };
   11
   12 class MyApp: public wxApp {
   13 public:
   14     virtual bool OnInit();
   15 };
   16 DECLARE_APP(MyApp)
   17
   18 //-----------------------------------------------
   19 MyDialog::MyDialog( wxWindow *parent,
   20                     wxWindowID id,
   21                     const wxString& title )
   22         : wxDialog(
   23                parent, id, title,
   24                wxDefaultPosition, wxDefaultSize,
   25                wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
   26
   27         // Создаём wxGridBagSizer:
   28         wxGridBagSizer *gbs = new wxGridBagSizer(3, 3);
   29
   30         // Эластичными будут оба направления:
   31         gbs->SetFlexibleDirection(wxBOTH);
   32
   33         // Если бы было неэластичное направление, то здесь мы бы
   34         // вызвали метод gbs->SetNonFlexibleGrowMode(...);
   35
   36         // Средняя колонка и средняя строка будут эластичными:
   37         gbs->AddGrowableCol(1, 0);
   38         gbs->AddGrowableRow(1, 0);
   39
   40         // Создаём 8 кнопок:
   41         wxButton *btns[8];
   42         for (int i = 0; i < 8; i++)
   43             btns[i] = new wxButton(
   44                  this, wxID_ANY,
   45                  wxString::Format(wxT("%d"), i),
   46                  wxDefaultPosition,
   47                  wxSize(50,35) ) ;
   48
   49         // Добавляем кнопки к сайзеру:
   50         gbs->Add( btns[0], wxGBPosition(0,0), wxGBSpan(1,1),
   51                   wxALIGN_LEFT|wxALIGN_TOP);
   52         gbs->Add( btns[1], wxGBPosition(0,1), wxGBSpan(1,1),
   53                   wxALIGN_CENTER_HORIZONTAL|wxALIGN_TOP);
   54         gbs->Add( btns[2], wxGBPosition(0,2), wxGBSpan(1,1),
   55                   wxALIGN_RIGHT|wxALIGN_TOP);
   56         gbs->Add( btns[3], wxGBPosition(1,0), wxGBSpan(1,1),
   57                   wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
   58         gbs->Add( btns[4], wxGBPosition(1,1), wxGBSpan(1,1),
   59                   wxALIGN_CENTER_HORIZONTAL|
   60                   wxALIGN_CENTER_VERTICAL|wxEXPAND);
   61         gbs->Add( btns[5], wxGBPosition(1,2), wxGBSpan(1,1),
   62                   wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL);
   63         gbs->Add( btns[6], wxGBPosition(2,0), wxGBSpan(1,1),
   64                   wxALIGN_LEFT|wxALIGN_BOTTOM);
   65         gbs->Add( btns[7], wxGBPosition(2,1), wxGBSpan(1,2),
   66                   wxALIGN_CENTER_HORIZONTAL|
   67                   wxALIGN_BOTTOM|wxEXPAND);
   68
   69         // Увеличим размер центральнрй кнопки:
   70         btns[4]->SetMinSize( wxSize(70, 50) );
   71
   72         // Устанаваливаем размеры сайзера по размерам окна:
   73         SetSizer(gbs);
   74         gbs->Fit(this);
   75         gbs->SetSizeHints(this);
   76 }
   77
   78 //------------------------------------------------------
   79 IMPLEMENT_APP(MyApp)
   80
   81 bool MyApp::OnInit() {
   82     MyDialog *frame = new MyDialog(
   83             NULL, wxID_ANY, wxT("wxGridBagSizer") );
   84     SetTopWindow(frame);
   85     frame->ShowModal();
   86     frame->Destroy();
   87     return TRUE;
   88 }
   89

Сайзер для кнопок

Для размещения в диалогах стандартных кнопок класс wxDialog (и, разумеется, все производные от него классы) имеют метод
wxSizer* CreateButtonSizer(long flags)
который создаёт сайзер с кнопками, указанными в наборе битовых флагов flags: это могут быть wxOK, wxCANCEL, wxYES_NO, wxYES, wxNO, wxHELP, wxFORWARD, wxBACKWARD, wxSETUP, wxMORE и wxNO_DEFAULT.

Диалог ввода пароля с использованием wxBoxSizer и wxFlexGridSizer

В одном из предыдущих проектов мы разработали диалог ввода пароля. Там использовались абсолютные или относительные координаты и размеры всех элементов. Исправим листинги login1.h и login1.cpp, чтобы все элементы размещались внутри wxFlexGridSizer, а кнопки расположим в сайзере wxBoxSizer. Новый текст программы показан в листингах . и .

Диалог с использованием сайзеров (файл examples-wx/login2/login2.h)

    1 // Диалог подключения к базе данных.
    2
    3 class LoginDialog : public wxDialog {
    4 public:
    5     wxComboBox *m_conf;
    6     wxTextCtrl *m_username;
    7     wxTextCtrl *m_password;
    8
    9     LoginDialog(wxWindow* parent, wxWindowID id,
   10              const wxString& title,
   11              int n, wxString confs[], int defaultConf=0);
   12     void OnChoice(wxCommandEvent& event);
   13     void OnEnter(wxCommandEvent& event);
   14     void OnStart(wxCommandEvent& event);
   15     void OnCancel(wxCommandEvent& event);
   16     void DoStart();
   17     void GetLoginData(wxArrayString& data);
   18 private:
   19     DECLARE_EVENT_TABLE()
   20 };
   21
   22 enum {
   23     ID_CONF =101,
   24     ID_USERNAME,
   25     ID_PASSWORD
   26 };
   27
   28 class MyApp : public wxApp {
   29 public:
   30     bool OnInit();
   31 };
   32 DECLARE_APP(MyApp)

Диалог с использованием сайзеров (файл examples-wx/login2/login2.cpp)

    1 // Диалог подключения к базе данных.
    2 #include <wx/wx.h>
    3 #include "login2.h"
    4
    5 BEGIN_EVENT_TABLE(LoginDialog, wxDialog)
    6     EVT_COMBOBOX(ID_CONF, LoginDialog::OnChoice)
    7     EVT_TEXT_ENTER(ID_USERNAME, LoginDialog::OnEnter)
    8     EVT_TEXT_ENTER(ID_PASSWORD, LoginDialog::OnEnter)
    9     EVT_BUTTON(wxID_OK, LoginDialog::OnStart)
   10     EVT_BUTTON(wxID_CANCEL, LoginDialog::OnCancel)
   11 END_EVENT_TABLE()
   12
   13
   14 LoginDialog::LoginDialog(
   15                 wxWindow* parent, wxWindowID id,
   16                 const wxString& title,
   17                 int n, wxString confs[],
   18                 int defaultConf )
   19         : wxDialog(parent, id, title,
   20                 wxDefaultPosition, wxSize(470, 255),
   21                 wxCAPTION | wxMINIMIZE_BOX |
   22                 wxSYSTEM_MENU | wxTHICK_FRAME ) {
   23
   24     wxFont *titleFont = new wxFont(
   25                     11, wxFONTFAMILY_SWISS,
   26                     wxFONTSTYLE_NORMAL,
   27                     wxFONTWEIGHT_BOLD, FALSE, wxT(""),
   28                     wxFONTENCODING_DEFAULT);
   29     wxFont *normalFont = new wxFont(
   30                     11, wxFONTFAMILY_SWISS,
   31                     wxFONTSTYLE_NORMAL,
   32                     wxFONTWEIGHT_NORMAL, FALSE, wxT(""),
   33                     wxFONTENCODING_DEFAULT);
   34     SetFont(*normalFont);
   35
   36     wxStaticText *st0 = new wxStaticText(
   37               this, -1,
   38               wxT("Выберете конфигурацию,\n")
   39               wxT("введите имя пользователя и пароль"),
   40               wxDefaultPosition, wxSize(280, 50),
   41               wxALIGN_CENTER | wxST_NO_AUTORESIZE);
   42     st0->SetFont(*titleFont);
   43
   44     wxStaticText *st1 = new  wxStaticText(this, -1,
   45                                     wxT("Конфигурация:"));
   46     wxStaticText *st2 = new wxStaticText(this, -1,
   47                                     wxT("Пользователь:"));
   48     wxStaticText *st3 = new wxStaticText(this, -1,
   49                                     wxT("Пароль:"));
   50
   51     m_conf = new wxComboBox(
   52                 this, ID_CONF, wxT(""),
   53                 wxDefaultPosition, wxSize(280, -1),
   54                 n, confs, wxCB_READONLY );
   55     m_conf->SetSelection(defaultConf);
   56
   57     m_username = new wxTextCtrl(
   58                     this, ID_USERNAME, wxT(""),
   59                     wxDefaultPosition, wxSize(280, -1),
   60                     wxTE_PROCESS_ENTER );
   61     m_username->SetMaxLength(32);
   62
   63     m_password = new wxTextCtrl(
   64                     this, ID_PASSWORD, wxT(""),
   65                     wxDefaultPosition, wxSize(280, -1),
   66                     wxTE_PASSWORD | wxTE_PROCESS_ENTER );
   67     m_password->SetMaxLength(32);
   68
   69     wxButton *btnStart = new wxButton(
   70                     this, wxID_OK, wxT("Вход"),
   71                     wxDefaultPosition, wxSize(80, -1) );
   72     wxButton *btnCancel = new wxButton(
   73                     this, wxID_CANCEL, wxT("Отмена"),
   74                     wxDefaultPosition, wxSize(80, -1) );
   75     m_username->SetFocus();
   76
   77     wxFlexGridSizer *sizer = new wxFlexGridSizer(8, 4, 8, 8);
   78     wxBoxSizer *btnSizer = new wxBoxSizer(wxHORIZONTAL);
   79
   80     sizer->Add( 6, 4 );
   81     sizer->Add( 6, 4 );
   82     sizer->Add( 6, 4 );
   83     sizer->Add( 6, 4 );
   84
   85     sizer->Add( 6, 6 );
   86     sizer->Add( 20, 20 );
   87     sizer->Add(st0, 0, wxALIGN_CENTER|wxEXPAND, wxALL);
   88     sizer->Add( 6, 6 );
   89
   90     sizer->Add( 6, 6 );
   91     sizer->Add(st1,0,wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL,wxALL);
   92     sizer->Add(m_conf, 0, wxALIGN_LEFT|wxEXPAND, wxALL);
   93     sizer->Add( 6, 6 );
   94
   95     sizer->Add( 6, 6 );
   96     sizer->Add(st2,0,wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL,wxALL);
   97     sizer->Add(m_username,0,wxALIGN_LEFT|wxEXPAND,wxALL);
   98     sizer->Add( 6, 6 );
   99
  100     sizer->Add( 6, 6 );
  101     sizer->Add(st3,0,wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL,wxALL);
  102     sizer->Add(m_password, 0, wxALIGN_LEFT|wxEXPAND, wxALL);
  103     sizer->Add( 6, 6 );
  104     sizer->Add( 6, 6 );
  105     sizer->Add( 6, 6 );
  106     sizer->Add( 6, 6 );
  107     sizer->Add( 6, 6 );
  108
  109     btnSizer->Add(btnStart, 0, wxALIGN_CENTER);
  110     btnSizer->Add( 50, 20 );
  111     btnSizer->Add(btnCancel, 0, wxALIGN_CENTER);
  112
  113     sizer->Add( 6, 6 );
  114     sizer->Add( 6, 6 );
  115     sizer->Add(btnSizer);
  116     sizer->Add( 6, 6 );
  117
  118     sizer->Add( 6, 6 );
  119     sizer->Add( 6, 6 );
  120     sizer->Add( 6, 6 );
  121     sizer->Add( 6, 6 );
  122
  123     sizer->AddGrowableCol(2, 1);
  124     SetSizerAndFit(sizer);
  125 }
  126
  127 void LoginDialog::OnChoice(wxCommandEvent& event) {
  128     m_username->SetFocus();
  129 }
  130
  131 void LoginDialog::OnEnter(wxCommandEvent& event) {
  132     switch (event.GetId()) {
  133         case ID_USERNAME:   m_password->SetFocus(); break;
  134         case ID_PASSWORD:   DoStart(); break;
  135     }
  136     event.Skip();
  137 }
  138
  139 void LoginDialog::OnStart(wxCommandEvent& event) {
  140     DoStart();
  141 }
  142
  143 void LoginDialog::OnCancel(wxCommandEvent& event) {
  144     EndModal(wxID_CANCEL);
  145 }
  146
  147 void LoginDialog::DoStart() {
  148     EndModal(wxID_OK);
  149 }
  150
  151 void LoginDialog::GetLoginData(wxArrayString& data) {
  152     data.Clear();
  153     data.Add( m_conf->GetStringSelection() );
  154     data.Add( m_username->GetValue() );
  155     data.Add( m_password->GetValue() );
  156 }
  157
  158 //-----------------------------------
  159 IMPLEMENT_APP(MyApp)
  160
  161 bool MyApp::OnInit() {
  162     wxString confs[3] = {
  163         wxT("Бухгалтерия"),
  164         wxT("Зарплата"),
  165         wxT("Торговля")
  166     };
  167     LoginDialog *ld = new LoginDialog(
  168                             NULL, -1,
  169                             wxT("Вход в систему"),
  170                             3, confs, 2);
  171     if ( ld->ShowModal() == wxID_OK) {
  172         wxArrayString *data = new wxArrayString();
  173         ld->GetLoginData(*data);
  174         wxMessageDialog *dlg = new wxMessageDialog(
  175                 NULL,
  176                 wxString::Format(
  177                     wxT("Конфигурация: %s\n")
  178                     wxT("Имя пользователя: %s\n")
  179                     wxT("Пароль: %s"),
  180                     data->Item(0).c_str(),
  181                     data->Item(1).c_str(),
  182                     data->Item(2).c_str() ),
  183                 wxT("Вы ввели"),
  184                 wxOK | wxICON_INFORMATION );
  185         dlg->ShowModal();
  186         dlg->Destroy();
  187
  188         wxFrame *frame = new wxFrame(
  189                             NULL, -1,
  190                             wxT("Информационная система"));
  191         frame->CenterOnScreen(wxBOTH);
  192         SetTopWindow(frame);
  193         ld->Destroy();
  194         frame->Show();
  195     } else {
  196         ld->Destroy();
  197     }
  198     return TRUE;
  199 }


Конфигурационный файл приложения

Обычно приложения хранят значения различных параметров в специальных конфигурационных файлах с расширением ini или cfg. В системе Windows для этих целей часто используется реестр. В wxWidgets имеется возможность работать как с файлами (класс wxFileConfig), так и с реестром (класс wxRegConfig). Кроме того, определён класс wxConfig, который в зависимости от текущей платформы выбирает то или иной вариант. В Linux по умолчанию работают с файлами, в Windows -- с реестром.

Читать и записывать в ini-файлы или реестр можно пока только логические значения, числа и строки. В будущем авторы библиотеки обещают ввести поддержку и других типов данных.

Рассмотрим небольшой пример, иллюстрирующий использование класса wxFileConfig. Допустим, в рабочем каталоге программы имеется файл z1.ini, в разделе app которого определено строковое значение некоторого параметра param1 и числовое значение другого параметра param2:

[app]
param1 = Какая-то строка
param2 = 12
.....

Фрагмент программы на C++, позволяющий прочитать значения этих параметров:

#include "wx/fileconf.h"

#define DEFAULT_PARAM1 wxT("Значение param1 по умолчанию")
#define DEFAULT_PARAM2 10
......................

wxFileConfig *config = new wxFileConfig(
                               wxT("z1"), wxEmptyString,
                               wxT("z1.ini"), wxEmptyString,
                               wxCONFIG_USE_RELATIVE_PATH );
wxFileConfig::Set(config); // Установили в качестве глобального объекта.

wxString param1 = config->Read("/app/param1", DEFAULT_PARAM1);
long param2 = config->Read("/app/param2", DEFAULT_PARAM2);

При вызове конструктора wxFileConfig первым параметром указывается имя приложения (в нашем примере z1), вторым -- суффикс, добавляемый к имени (используется для работы с реестром), третьим -- имя локального конфигурационного файла, четвёртым -- имя глобального файла, пятым -- режим (можно указывать стилевые флаги wxCONFIG_USE_LOCAL_FILE, wxCONFIG_USE_GLOBAL_FILE, wxCONFIG_USE_RELATIVE_PATH, wxCONFIG_USE_NO_ESCAPE_CHARACTERS). Наконец, шестой параметр -- кодировка конфигурационного файла (по умолчанию wxConvUTF8) используется, только если приложение скомпилировано в режиме Unicode, иначе кодировка конфигурационного файла всегда совпадает с кодировкой самого приложения).

Статический метод Set позволяет установить в начале программы глобальный конфигурационный объект, который после этого доступен с помощью статического метода Get. Это позволяет избежать необходимости каждый раз открывать файл (или реестр), когда он потребуется.

Кроме функций

wxString Read(const wxString& key, const wxString& defaultVal) const
long Read(const wxString& key, long defaultVal) const
можно использовать вариант, возвращающий логическое значение TRUE, если заданный параметр имеется в конфигурационном файле, и FALSE в противном случае:
bool Read(const wxString& key, wxString* str, const wxString& defaultVal)
bool Read(const wxString& key, long* l, long defaultVal) const

По ходу выполнения программы значения параметров могут быть изменены и записаны в файл z1.ini:

config->Write("/app/param1", param1);
config->Write("/app/param2", param2);
delete config; // Все изменения будут автоматически сохранены в ini-файле.

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

Для перебора элементов и групп в ini-файле предназначены методы GetFirstEntry и GetNextEntry, GetFirstGroup, GetNextGroup.

Пути к элементам конфигурационного файла могут быть абсолютными, как в приведённых примерах (начинаются символом "/"), так и относительными. В последнем случае они указываются относительно текущей (под)группы, которая выбирается вызовом метода SetPath.

Если мы хотим работать с реестром, то вместо wxFileConfig следует использовать класс wxRegConfig (включаемый файл "wx/msw/regconf.h").

Функциональность обоих вариантов инкапсулирует класс wxConfig:

#include "wx/config.h"
.......
wxConfig *config = new wxConfig("MyAppName");
wxString str;
if ( config->Read("/app/section1/subsection2", &str) ) {
    .......
}
.......
config->Write("/app/section1/subsection2", str)

В системе Linux wxConfig формирует код для обращения к файлу, а в Windows -- к реестру. Разумеется, никто не мешает разработчику отказаться от использования реестра и работать с конфигурационным файлом на любой платформе.

В листинге показан пример программы, работающей с ini-файлом, она формирует файл cfg1.ini следующего содержания:

[app]
p1=Строка1
p2=12
p3=12.56
p4=1

[app/subsection1]
p5=Строка2

[user]
[user/subsection2]
p6=Строка3
Здесь имеются две группы app и user, в каждой из которых помимо простых элементов создано по одной подгруппе: subsection1 и subsection2. Элементы p1, p2, p3 и p4 имеют тип wxString, long, double, bool соответственно и расположены в группе app. Строковый элемент p5 находится в подгруппе subsection1. В группе user нет элементов, но в её подгруппе subsection2 имеется единственный строковый элемент p6.

На рис. показано, как выглядит консольное окно приложения.

Работа с файлом ini

Работа с файлом ini (файл examples-wx/cfg1/cfg1.cpp)

    1 // Работа с ini-файлом. Консольное приложение.
    2 #include <wx/app.h>
    3 #include <wx/string.h>
    4 #include <wx/snglinst.h>
    5 #include <wx/fileconf.h>
    6 #include <wx/utils.h>
    7 #include <stdio.h>
    8
    9 int main(int argc, char **argv) {
   10     // Инициализируем библиотеку wx:
   11     wxInitializer initializer;
   12     if ( !initializer ) {
   13         fprintf(stderr,
   14             "Не могу инициализировать wxWidgets!");
   15         return -1;
   16     }
   17     // Конвертор для кодировки консольного окна:
   18 #ifdef __WXMSW__
   19     wxCSConv conv(wxFONTENCODING_CP866);
   20 #else
   21     wxCSConv conv(wxFONTENCODING_KOI8);
   22 #endif
   23
   24     #define cd(s)   ( (const char*)s.mb_str(conv) )
   25     #define cds(s)  ( (const char*)wxString(wxT(s)).mb_str(conv) )
   26
   27     // Проверим, не запущена ли уже другая копия программы:
   28     const wxString name = wxString::Format(
   29             wxT("wxcfg1-%s"), wxGetUserId().c_str() );
   30     wxSingleInstanceChecker checker;
   31     if ( checker.Create( name ) ) {
   32         if ( checker.IsAnotherRunning() ) {
   33             fprintf(stderr, cds("\nНельзя запустить 2-ю копию этой
   34             программы!\n"));
   35             wxSleep(2); // Задержка 2 секунды.
   36             return 1;
   37         }
   38     }
   39
   40     // Создаём объект wxFileConfig:
   41     wxFileConfig *config = new wxFileConfig(
   42                                wxT("cfg1"), wxEmptyString,
   43                                wxT("cfg1.ini"), wxEmptyString,
   44                                wxCONFIG_USE_RELATIVE_PATH );
   45     //Устанавливаем в качестве глобального объекта:
   46     wxFileConfig::Set(config);
   47
   48     // Создаём группу 'app' с элементами 'p1','p2','p3','p4':
   49     config->SetPath( wxT("/app") );
   50     config->Write( wxT("p1"), wxT("Строка1") );
   51     config->Write( wxT("p2"), 12 );
   52     config->Write( wxT("p3"), 12.56 );
   53     config->Write( wxT("p4"), TRUE );
   54
   55     // Создаём подгруппу 'subsection1' в текущей группе
   56     // и элемент p5 в ней:
   57     config->Write( wxT("subsection1/p5"), wxT("Строка2") );
   58
   59     // Создаём группу 'user', а в ней -  подгруппу 'subsection2'
   60     // с элементом p6:
   61     config->Write(wxT("/user/subsection2/p6"), wxT("Строка3"));
   62
   63     config->Flush(); // Записали изменения в файл.
   64
   65     // Читаем значения элементов:
   66     wxString p1, p5, p6;
   67     long p2;
   68     double p3;
   69     bool p4;
   70
   71     config->SetPath( wxT("/app") ); // Установили текущую группу.
   72
   73     if ( config->Read( wxT("p1"), &p1,
   74                        wxT("Строка1 по умолчанию") ) )
   75         printf( cds("\np1 = %s"), cd(p1) );
   76     else
   77         printf( cds("\nПо умолчанию p1 = %s"), cd(p1) );
   78
   79     if (config->Read( wxT("p2"), &p2, 10 ) )
   80         wxPrintf( wxT("\np2 = %ld"), p2 );
   81     else
   82         printf( cds("\nПо умолчанию p2 = %ld"), p2);
   83
   84     if (config->Read( wxT("p3"), &p3, 10.23 ) )
   85         wxPrintf( wxT("\np3 = %lf"), p3 );
   86     else
   87         printf( cds("\nПо умолчанию p3 = %lf"), p3);
   88
   89     if (config->Read( wxT("p4"), &p4, FALSE ) )
   90         wxFprintf( stdout, wxT("\np4 = %d"), p4 );
   91     else
   92         printf( cds("\ndefault p4 = %d"), p4 );
   93
   94     // Теперь используем абсолютные пути:
   95     config->Read( wxT("/app/subsection1/p5"), &p5 );
   96     printf( cds("\np5 = %s"), cd(p5) );
   97
   98     config->Read( wxT("/user/subsection2/p6"), &p6 );
   99     printf( cds("\np6 = %s"), cd(p6) );
  100
  101     config->SetPath( wxT("/") ); // Перешли на верхний уровень.
  102     long groups1 = config->GetNumberOfGroups(FALSE);
  103     printf(cds("\nКоличество групп верхнего уровня = %ld"), groups1);
  104
  105     long groups2 = config->GetNumberOfGroups(TRUE);
  106     printf(cds("\nКоличество групп и подгрупп = %ld"), groups2);
  107
  108     // Перебираем все группы:
  109     wxString val;
  110     long index;
  111     bool more = config->GetFirstGroup(val, index);
  112     while ( more ) {
  113         printf(cds("\nГруппа: %s"), cd(val) );
  114         more = config->GetNextGroup(val, index);
  115     }
  116     wxPrintf( wxT("\n") );
  117
  118     // Перебираем все элементы группы 'app':
  119     config->SetPath( wxT("/app") ); // Перешли в группу 'app'.
  120     more = config->GetFirstEntry(val, index);
  121     while ( more ) {
  122         printf(cds("\nЭлемент: %s"), cd(val) );
  123         more = config->GetNextEntry(val, index);
  124     }
  125
  126     delete config;
  127
  128     // Ждём нажатия клавиши ESC и завершаем программу:
  129     printf(cds("\nНажмите <Enter>..."));
  130     char c; wxScanf(wxT("%c"), &c);//while (!wxGetKeyState(WXK_RETURN));
  131     return 0;
  132 }

В отличие от наших предыдущих программ на C++, это приложение является консольным, т.е. не использует элементы графического интерфейса. Оно содержит обычную функцию main (9). Перед тем, как вызывать какие бы то ни было функций библиотеки wxWidgets, её требуется инициализировать (11).

Для вывода сообщений на экран в консольном приложении можно использовать обычные функции C или C++. Но для работы со строками типа wxString в библиотеке wxWidgets имеются специальные функции для вывода информации в консольном режиме: wxFprintf, wxPrintf, wxFputc, wxFputchar, wxFputs, wxPutc, wxPutchar, wxPuts, и для ввода: wxFscanf, wxScanf, wxGetc, wxGetchar, wxGets. В листинге . мы попытались показать некоторые возможные варианты (разумеется, это не может считаться хорошим стилем программирования: в реальных приложениях следует придерживаться какого-либо одного способа).

Если вы не хотите возиться с кодировками, то самое простое, что можно сделать для правильного отображения русских букв в консольном окне Windows, -- это в текстовом редакторе или с помощью любой утилиты изменить кодировку исходного текста программы на cp866, а потом выполнить компиляцию, но только не в режиме Unicode. Кроме того, всегда можно использовать собственные функции для подстановочной или перестановочной перекодировки. К сожалению, имеющийся в wxWidgets класс wxEncodingConverter, предназначенный для преобразования 8-битных кодировок, не поддерживает кодовую страницу cp866.

Но в режиме Unicode доступны специальные средства перекодировки строк: это классы wxMBConv и wxCSConv. Например, чтобы вывести на экран строку в кодировке cp866, достаточно создать экземпляр класса wxCSConv и указать его в качестве аргумента метода mb_str:

wxString str = wxT("Какой-то текст");
wxCSConv conv(wxFONTENCODING_CP866);
printf("\n%s", (const char*) str.mb_str(conv));

Если в форматной строке тоже имеются русские символы, то надо преобразовать и её:

wxString str = wxT("Какой-то текст");
wxCSConv conv(wxFONTENCODING_CP866);
printf((const char*)wxString(wxT("\n%s")).mb_str(conv), (const char*) str.mb_str(conv));
Здесь мы сначала преобразуем строку формата к типу wxString, чтобы потом применить к ней конвертор кодировки. Чтобы не загромождать исходный текст программы, удобнее использовать макросы:
#define cd(s)   ( (const char*)s.mb_str(conv) )
#define cds(s)  ( (const char*)wxString(wxT(s)).mb_str(conv) )

При передаче строковых аргументов функциям Format и (wx)printf надо предварительно преобразовать экземпляры класса wxString в указатели на сами строки. Если не применяется mb_str, т.е. если не нужна перекодировка, то для получения указателя используется метод wxString::c_str(), например, как в (29).

В (28-35) мы проверяем, не запущена ли уже другая копия нашей программы. Этим гарантируется, что никто больше не захочет записывать данные в файл cfg1.ini. Специально для таких целей в библиотеке wxWidgets имеется класс wxSingleInstanceChecker. При создании экземпляра этого класса требуется указать уникальное имя, которое будет идентифицировать именно нашу программу. Для этого рекомендуется использовать имя программы и имя пользователя (метод GetUserId). В результате будет обеспечено беспрепятственное выполнение нескольких экземпляров приложения от имени разных пользователей, но один и тот же пользователь не сможет запустить вторую копию: при попытке сделать это будет выдано соответствующее предупреждение и вторая копия программы будет аварийно завершена.

XML-описание консольного проекта для утилиты Bakefile:

    1 <?xml version="1.0" ?>
    2 <makefile>
    3     <include file="presets/wx.bkl"/>
    4     <exe id="cfg1" template="wx">
    5         <app-type>console</app-type>
    6         <debug-info>off</debug-info>
    7     <runtime-libs>dynamic</runtime-libs>
    8         <sources>../cfg1.cpp</sources>
    9         <wx-lib>core</wx-lib>
   10         <wx-lib>base</wx-lib>
   11     </exe>
   12 </makefile>
Обратите внимание на значение параметра app-type. Для компиляции проекта в Windows требуется указать режим Unicode:
nmake -f makefile.vc WX_SHARED=0 WX_DEBUG=0 WX_UNICODE=1

В Linux для тех же целей используется параметр configure  -enable-unicode.