Что такое wxWidgets? Для многих разработчиков это просто набор виджетов, занимающий на платформе Linux почетное третье место после вечных соперников Qt и GTK, и не все знают, что wxWidgets, это еще и история открытых графических интерфейсов. Набор визуальных элементов управления wxWidget появился на свет в 1992 году, тогда же, когда и XFree86. Все началось с того, что сотруднику Университета Эдинбурга Джулиану Смарту (Julian Smart) потребовалась кросс-платформенная библиотека для создания графических интерфейсов на платформах Sun и PC. У университета не нашлось средств на покупку кросс-платформенной библиотеки виджетов (да, были и такие времена), и тогда Смарт поступил так, как и подобает настоящему хакеру, – он начал писать собственную библиотеку графических элементов управления, которую назвал wxWindows. Проект бесплатной корсс-платформенной библиотеки быстро привлек внимание других программистов в университете, а вскоре – и за его приделами. Первая версия wxWindows поддерживала XView (набор виджетов, созданный Sun Microsystems) и MFC 1.0. В 2003 году представители компании Microsoft обратили внимание на xwWindows и вежливо попросили Дж. Смарта изменить имя библиотеки (слово “Windows” является зарегистрированной торговой маркой Microsoft в Великобритании). Переговоры продолжались долго, в качестве ответной уступки американская компания предложила материальную компенсацию (полученные от Microsoft деньги было решено потратить на развитие проекта), и в августе 2004 года библиотека wxWindows была официально переименована в wxWidgets. Почему же открытая библиотека wxWidgets не стала предпочтительным выбором программистов Linux, уступив эту честь полу-открытой Qt library? Причина банально проста: в те далекие времена, когда решалась судьба графических оболочек для Linux, wxWidgets (в те времена – все еще wxWindows) не поддерживала X11. На платформе Unix/Linux wxWidgets использовала виджеты Motif/Lesstif, а позднее – GTK. Версия библиотеки, способная работать с X11 без посторонней помощи, появилась только в 2002 году, когда территорию графических оболочек для Linux уже застолбили другие библиотеки. Библиотека wxWidgets распространяется на условиях wxWindows Licence (которую должна заменить wxWidgets Licence, отличающаяся от первой только названием). По сути своей wxWindows Licence – это «смягченный вариант» LGPL, позволяющий распространять производные продукты wxWidgets в бинарной форме на условиях разработчика без отчислений разработчика wxWidgets. Фактически, лицензия wxWindows предоставляет разработчику больше возможностей, чем лицензия GTK+, которая обязывает разработчика распространять производные библиотеки на условиях LGPL, и лицензия Qt, которая требует платить за коммерческое использование библиотеки. Приложения, использующие wxWidgets, можно программировать не только на родном для этой библиотеки C++, но и на других языках программирования (Java, Perl, Python). Существуют также интерфейсы wxWidgets для Microsoft .NET и Mono. На основе wxWidgets создано немало программ, самой известной из которых является, пожалуй, аудио-редактор Audacity (рис. 1).
Рисунок 1. Audacity – wxWidgets killer app.
Список платформ, поддерживаемых wxWidgets, внушает уважение. Вы можете использовать библиотеку вместе с GTK (Unix/Linux/MinGW), Lesstif/Motif (Unix/Linux) X11 (Unix/Linux/MinGW), Win32 (Windows, Windows CE), Carbon (Mac OS), Cocoa (Mac OS X, GNUstep), Protein (Palm OS), PM (OS/2) MGL (Unix/DOS). Само это перечисление демонстрирует важную особенность wxWidgets – «вертикальную» организацию набора виджетов. В отличие от Qt и GTK, которые ориентированы на платформы, библиотека wxWidgets ориентирована на интерфейсы. Если какой-либо из поддерживаемых wxWidgets интерфейсов переносится на новую платформу, перенос wxWidgets на эту платформу не должен представлять особых проблем. Согласно традиции версии wxWidgets для каждой платформы обозначаются добавлением префикса wx к сокращенному названию платформы. Например, wxWidgets для Windows обозначается как wxMSW, wxWidgets для GTK – как wxGTK, wxWidgets для X11 – как wxX11 и т.д. Еще одна интересная возможность, связанная с многоплатформееностью wxWidgets, – кросс-компиляция. На сайте проекта можно найти инструкции по компиляции wxWidgets-программ для Windows из-под Linux.
Выбирая между wxWidgets для GTK и wxWidgets для X11, следует помнить, что, не смотря на все усилия разработчиков, эти два набора визуальных компонентов все еще неравноценны. На сайтах некоторых проектов, использующих wxWidgets, вы найдете указания, что проект компилируется с wxGTK, но не с wxX11. Объясняется это тем, что виджетов wxUniversal, который использует wxX11, все еще не дотягивает по функциональности до набора GTK, на котором основана wxGTK. Разработанный с нуля wxUniversal представляет собой сравнительно недавнее добавление в wxWidgets. Этот набор виджетов предназначен, в перспективе, для тех платформ, у которых собственные наборы виджетов отсутствуют (хотя вряд ли сейчас можно найти такую платформу). Список виджетов и функций, которые присутствуют в wxGTK и wxMSW, но все еще не реализованы в wxX11, можно найти на сайте проекта. В качестве довода в пользу wxX11 можно указать то, что этот набор виджетов не нуждается в «прослойке» GTK, и может работать в системе, где библиотека GTK не установлена или не настроена должным образом. Окончательное решение при выборе между wxGTK и wxX11 следует принимать исходя из требований создаваемого приложения (есть ли в wxX11 все необходимые виджеты) и параметров GTK в целевой системе. При этом, в случае необходимости, базовую платформу можно будет сменить и на ходу (по крайней мере переход с wxX11 на wxGTK не вызовет проблем).
Помимо собственно визуальных компонентов wxWidgets предоставляет в распоряжение программиста классы для работы с базами данных (поддерживаются интерфейсы ODBC, XBase, SQLite), классы для работы с сокетами и популярными сетевыми протоколами, а также специальные классы для работы с HTML. Есть у wxWidgets и собственный классы, реализующие распространенные структуры данных (списки, очереди ит.п.), которые были введены в проект еще до появления в С++ стандартной библиотеки шаблонов. Поскольку сейчас использование шаблонов стандартной библиотеки представляется более целесообразным, вы можете сконфигурировать wxWidgets таким образом, чтобы библиотека использовала шаблоны, а не собственные реализации структур данных.
В wxWidgets реализованы сразу два способа определения обработчиков событий. Более старый способ, разработанный под влиянием MFC, основан на статических таблицах событий (event tables). Этот способ не позволяет манипулировать обработчиками событий во время выполнения программы. Более новый способ основан на использовании метода connect() и больше похож на динамический способ определения обработчиков событий, используемый в Qt library.
Разработчиков кросс-платформенных наборов виджетов можно разделить на два лагеря: одни стремятся к тому, чтобы визуальные компоненты выглядели по возможности одинаково на всех платформах (обычно это люди с твердыми убеждениями относительно того, каким должен быть правильный графический интерфейс). Наборы визуальных компонентов этого типа радуют глаз единством фирменного стиля. Разработчики из другого лагеря настаивают на том, чтобы на каждой платформе внешний вид графических элементов управления максимально соответствовал тому, что принято на данной платформе. К достоинствам этого подхода относят то, что единообразие внешнего вида приложений упрощает, якобы, освоение новых программ. Лично я с этим аргументом не согласен. На мой взгляд кнопки в стиле Aqua нисколько не мешают освоению Safari для Windows. На мой взгляд, самое сложное в освоении новой программы – согласовать подход разработчиков к решению поставленной задачи со своим собственным видением. Как бы там ни было, wxWidgets придерживается второго подхода, причем следует ему в гораздо большей степени, чем, скажем, Qt. Визуальные элементы wxWidgets не только выглядят на каждой платформе «как родные» (фактически, во многих случаях, классы wxWidgets – это просто обертки вокруг фирменных элементов управления), но и используют специфические возможности каждой платформы. Например, на платформе Win32 wxWidgets поддерживает метафайлы, которые отсутствуют в GTK. Если вы программируете интерфейсы с помощью wxWidgets, вы должны сами устанавливать баланс использования платформо-специфичных и кросс-платформенных возможностей библиотеки.
Сегодня практически каждый набор виджетов сопровождается средствами визуального программирования и другими вспомогательными инструментами. Библиотека wxWidgets не является исключением из этого правила. Вспомогательных средств разработки для wxWidgets существует немало, больше, пожалуй, чем для Qt и GTK, но при близком знакомстве с этими средствами заядлый линуксоид может испытать разочарование. Мы привыкли к тому, что базовые средства разработки для библиотек виджетов доступны нам на тех же условиях, что и сами библиотеки, однако с wxWidgets дело обстоит иначе. Наиболее функциональные системы визуального программирования для wxWidgets либо являются полностью коммерческими продуктами, либо распространяются бесплатно, но без исходных текстов. Интегрированная среда разработки DialogBlocks (рис. 2), доступная на сайте www.anthemion.co.uk/dialogblocks, претендует на роль официального средства разработки приложений wxWidgets на C++.
Рисунок 2. DialogBlocks
DialogBlocks включает в себя визуальный редактор окон wxWidgets, многооконный текстовый редактор, встроенную справочную систему. Интегрированная среда умеет генерировать Make-файлы для проектов wxWidgets и пересобирать саму библиотеку. Поддерживается и интерактивная отладка приложений (с использованием внешнего отладчика). Среда DialogBlocks – коммерческий продукт, который фактически распространяется на условиях shareware. Вы можете работать с программой бесплатно, однако, до тех пор, пока вы не оплатите регистрацию, функциональность DialogBlocks будет ограничена. Диалоговые окна в незарегистрированной версии могут включать не более 30 визуальных элементов, специальное окно периодически напоминает вам о необходимости регистрации, а многие визуальные элементы оказываются недоступны. При весьма солидных расценках (за новейшую версию разработчик требует $85, «студенческий» вариант обойдется вам на $40 дешевле) DialogBlocks отнюдь не поражает воображение удобством работы. Редактор исходных текстов лишен тех приятных мелочей (вроде автоматического завершения кода и подсказок для заголовков вызываемых функций), к которым мы давно привыкли в других коммерческих IDE. При первой попытке собрать проект система предлагает указать место расположения компилятора, но ввести путь к файлу gcc в открытом для этого диалоговом окне не удается (окно просто не реагирует на нажатие клавиш). Конечно, пользователя Linux такими мелочами не испугаешь, я тут же полез в окно настроек, где вручную сконфигурировал проект для сборки под wxX11. Далее выяснилось, что для того, чтобы собрать тестовое приложение надо сначала пересобрать wxWidgets, при этом некоторые поддиректории директории wxWidgets пришлось переименовывать вручную. После всех обработок напильником тестовое приложение скомпилировалось и запустилось. Впрочем, и разочарования на этом не кончились. В текстовом редакторе DialogBlocks отсутствуют функции быстрого перехода между реализацией функции и ее объявлением, перехода к выбранному заголовочному файлу и им подобные. Фактически встроенный редактор DialogBlocks не намного лучше, чем редактор KWrite. Те, кто готов платить за средство разработки для wxWidgets, могут обратить внимание на еще одну коммерческую IDE – wxDesigner (рис. 3).
Рисунок 3. wxDesigner
Хотя в вашем дистрибутиве Linux наверняка есть пакет разработчика для библиотеки wxWidgets, я рекомендую собрать библиотеку из исходных текстов, доступных на сайте проекта (wxwidgets.org – просто, на всякий случай). Собирая wxWidgets из исходных текстов, вы не только получаете новейшую версию библиотеки, но и более гибкие средства конфигурирования. Например, для этих статей я решил использовать wxX11, тогда как пакет wxWidgets из моего дистрибутива (Open SUSE) сконфигурирован для GTK+. Скрипт configure wxWidgets позволяет настраивать многие параметры библиотеки с помощью ключей. Например, для того, чтобы скомпилировать wxWidgets с поддержкой X11, командуем:
./configure --with-x11
Ключ --enable-stl указывает, что вместо классов структур данных wxWidgets следует использовать контейнеры STL. Полное описание ключей configure вы можете получить, как обычно, с помощью ключа --help. Знакомство с программированием в wxWidgets мы начнем, как всегда, с простейшего приложения (файл hwapp.cpp):
#include "wx/wx.h"
class HWFrame: public wxFrame
{
public:
HWFrame() : wxFrame(NULL, wxID_ANY, "First wxWidgets Application")
{
label = new wxStaticText(this, wxID_STATIC, "Hello World");
}
virtual ~HWFrame()
{
delete label;
}
private:
wxStaticText * label;
};
class HWApp: public wxApp
{
virtual bool OnInit()
{
HWFrame * myFrame = new HWFrame();
myFrame->Show();
return true;
}
};
IMPLEMENT_APP(HWApp);
Эта программа действительно очень проста. Она не обрабатывает события, не содержит сложных элементов интерфейса, все, что она делает, – выводит надпись “Hello World” в главном окне. Тем не менее, программа hwapp демонстрирует важнейшие особенности структуры приложения wxWidgets. Объявления всех классов, функций и макросов библиотеки виджетов становятся доступны нам в результате включения в текст программы одного единственного заголовочного файла – wx/wx.h. В отличие от Qt и gtkmm в wxWidgets, даже при написании простого приложения нам приходится объявлять сразу два собственных класса. Класс HWApp, который является потомком класса библиотечного wxApp, представляет собой главный класс приложения. Этот класс можно рассматривать как аналог класса QApplication из Qt, с той разницей, что в Qt нам редко приходится создавать собственный производный класс от QApplication. Класс HWFrame, который происходит от класса wxFrame, реализует главное окно нашей программы. Обратите внимание, что имена всех классов wxWidgets начинаются с префикса wx, а имена методов классов – с заглавной буквы. В потомке класса wxApp нам требуется перекрыть только один базовый метод – OnInit(). Этот виртуальный метод вызывается базовым классом в самом начале работы программы и именно на него возложена задача по созданию и отображению главного окна. Если метод OnInit() возвращает значение false, выполнение программы сразу же завершается. Если вам необходимо выполнить какие-либо специальные действия в процессе завершения программы (например, высвободить занятые программой ресурсы), вы можете перекрыть метод OnExit() класса wxApp. В методе OnInit() мы создаем объект класса-потомка wxFrame и вызываем его метод Show(), для того чтобы окно, созданное этим объектом, стало видимым. В классе HWFrame мы перекрываем конструктор и деструктор класса wxFrame. Наша задача – добавить в окно wxFrame визуальный элемент со статическим текстом (метка) и вывести в нем текст приветствия. Рассмотрим сначала базовый конструктор wxFrame. Первый параметр конструктора – указатель на класс родительского окна. Мы передаем в этом параметре значение NULL, так как главное окно не имеет родителя. Во втором параметре конструктора передается идентификатор окна. Идентификатор представляет собой целое число, которое идентифицирует окно в процессе обработки сообщений. Все окна, использующие один и тот же цикл обработки сообщений (например, главное окно и его дочерние виджеты), должны иметь уникальные идентификаторы (это не относится к некоторым типам окон, не предназначенных для получения «персональных» сообщений). В программе hwapp мы не обрабатываем сообщения, поэтому нам все равно, какой будет идентификатор у главного окна программы. В конструкторе wxFrane мы передаем константу wxID_ANY, которая указывает, что конструктор может сам выбрать идентификатор для создаваемого окна (мы пользуемся константой wxID_ANY всякий раз, когда идентификатор окна нас не интересует). В последнем задействованном нами параметре конструктора передается заголовок создаваемого окна (у конструктора wxFrame есть и другие параметры, для которых мы оставляем значения, присвоенные им по умолчанию). В самом конструкторе мы создаем объект label класса wxStaticText (метка). Первые два параметра конструктора wxStaticText имеют тот же смысл, что и первые параметры конструктора wxFrame. В третьем параметре мы передаем строку текста для отображения. Обратите внимание, что в качестве идентификатора окна визуального элемента «метка» мы воспользовались константой wxID_STATIC. Этот идентификатор используется при создании статических визуальных элементов, которые не обрабатывают пользовательский ввод.
Рисунок 4. Наша первая программа, - пока что не Audacity
Вот, собственно, и все (рис. 4). Ах, да, вы, наверное, обратили внимание, что в программе не определена функция main(). Дело в том, что разработчики wxWidgets избавили нас от хлопот по написанию главной функции программы. Все необходимые определения содержит макрос IMPLEMENT_APP(), которому мы передаем имя класса приложения в качестве параметра. Теперь программу можно скомпилировать, воспользовавшись вспомогательной утилитой wx-config:
g++ hwapp.cpp `wx-config --libs` `wx-config --cxxflags` -o hwapp
Трудно представить себе программу проще той, что мы написали. В продолжении серии мы рассмотрим процесс создания «настоящего» приложения – программы для мониторинга событий Skype.
Исходный текст программыСтатья впервые опубликована в журнале Linux Format
© 2009 Андрей Боровский anb@symmetrica.net