Журнал LinuxFormat - перейти на главную

LXF98:wxWidgets

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

wxWidgets: живая история

ЧАСТЬ 1 Что объединяет Audacity, poEdit и FlameRobin? Все они работают на нескольких платформах, используя wxWidgets – и вы тоже так сможете, если прочтете учебник Андрея Боровского!

Что такое wxWidgets? Для многих разработчиков это просто набор виджетов, занимающий на платформе Linux почетное третье место после вечных соперников Qt и GTK, и не все знают, что wxWidgets – это еще и история открытых графических интерфейсов. Набор визуальных элементов управления wxWidget появился на свет в 1992 году, тогда же, когда и XFree86. Все началось с того, что сотруднику Университета Эдинбурга Джулиану Смарту [Julian Smart] потребовалась кросс-платформенная библиотека для создания графических интерфейсов на платформах Sun и PC. У университета не нашлось средств на покупку кросс-платформенной библиотеки виджетов (да, были и такие времена), и тогда Смарт поступил так, как и подобает настоящему хакеру – он начал писать собственную библиотеку, которую назвал wxWindows. Проект бесплатной кросс-платформенной библиотеки быстро привлек внимание других программистов в университете, а вскоре – и за его пределами. Первая версия wxWindows поддерживала XView (набор виджетов, созданный Sun Microsystems) и MFC 1.0. В 2003 году представители компании Microsoft обратили внимание на wxWindows и вежливо попросили Дж. Смарта изменить название (слово "Windows" является зарегистрированной торговой маркой Microsoft в Великобритании) [подобные просьбы высказывались и в адрес закрытых проектов, в частности, Windows Commander, – прим. ред.]. Переговоры продолжались долго; в качестве ответной уступки американская компания предложила материальную компенсацию (полученные от Microsoft деньги было решено потратить на развитие проекта), и в августе 2004 года библиотека wxWindows была официально переименована в wxWidgets.

Почему же wxWidgets не стала предпочтительным выбором для Linux-программистов? Причина проста: в те далекие времена, когда решалась судьба графических оболочек для Linux, wxWidgets (тогда еще wxWindows) не поддерживала X11. На платформе Unix/Linux wxWidgets использовала виджеты Motif/Lesstif, а позднее – GTK. Версия библиотеки, способная работать с X11 без посторонней помощи, появилась только в 2002 году, когда территорию графических оболочек для Linux уже застолбили другие библиотеки.

Библиотека wxWidgets распространяется на условиях wxWindows License (которую должна заменить wxWidgets License, отличающаяся от первой только названием). По сути своей, wxWindows License – это «смягченный вариант» LGPL, позволяющий распространять производные продукты wxWidgets в бинарной форме на ваших собственных условиях без отчислений разработчикам wxWidgets. Фактически, лицензия wxWindows предоставляет разработчику больше возможностей, чем лицензия GTK+, которая обязывает разработчика распространять производные библиотеки на условиях LGPL, и лицензия Qt, которая требует платить за коммерческое использование библиотеки. Приложения, использующие wxWidgets, можно программировать не только на родном для этой библиотеки – C++, но и на других языках программирования (Java, Perl, Python). Существуют также интерфейсы wxWidgets для Microsoft .NET и Mono. На основе wxWidgets создано немало программ, самой известной из которых является, пожалуй, аудиоредактор Audacity.

Список платформ, поддерживаемых 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 таким образом, чтобы библиотека использовала STL, а не собственные реализации этих структур данных.

В wxWidgets реализованы сразу два способа определения обработчиков событий. Более старый способ, разработанный под влиянием MFC, основан на статических таблицах событий (event tables). Однако, он не позволяет манипулировать обработчиками событий во время выполнения программы. Второй (более новый) вариант основан на использовании метода connect() и больше похож на динамический способ определения обработчиков событий, используемый в Qt.

Инструменты разработчика

Сегодня практически каждый набор виджетов сопровождается средствами визуального программирования и другими вспомогательными инструментами. Библиотека wxWidgets не является исключением из этого правила. Вспомогательных средств разработки для wxWidgets существует немало, больше, пожалуй, чем для Qt и GTK, но при близком знакомстве с этими средствами заядлый линуксоид может испытать разочарование. Мы привыкли к тому, что базовые средства разработки для библиотек виджетов доступны нам на тех же условиях, что и сами библиотеки, однако с wxWidgets дело обстоит иначе. Наиболее функциональные системы визуального программирования для wxWidgets либо являются полностью коммерческими продуктами, либо распространяются бесплатно, но без исходных текстов.

Интегрированная среда разработки DialogBlocks, доступная на сайте www.anthemion.co.uk/dialogblocks, претендует на роль официального средства разработки приложений wxWidgets на C++.

DialogBlocks включает визуальный редактор окон wxWidgets, многооконный текстовый редактор, встроенную справочную систему. Интегрированная среда умеет генерировать Make-файлы для проектов wxWidgets и пересобирать саму библиотеку. Поддерживается и интерактивная отладка приложений (с использованием внешнего отладчика). Среда DialogBlocks – коммерческий продукт, который фактически распространяется как shareware. Вы можете работать с программой бесплатно, однако, до тех пор, пока вы не оплатите регистрацию, функциональность DialogBlocks будет ограничена. Диалоговые окна в незарегистрированной версии могут включать не более 30 визуальных элементов, специальное окно периодически напоминает вам о необходимости регистрации, а многие визуальные элементы оказываются недоступны. При весьма солидных расценках (за новейшую версию разработчик требует $85, «студенческий» вариант обойдется вам на $40 дешевле) DialogBlocks отнюдь не поражает воображение работы. Редактор исходных текстов лишен тех приятных мелочей (вроде автоматического завершения кода и подсказок для заголовков вызываемых функций), к которым мы давно привыкли в других коммерческих IDE. При первой попытке собрать проект система предлагает указать место расположения компилятора, но ввести путь к файлу gcc в открытом для этого диалоговом окне не удается (окно просто не реагирует на нажатие клавиш). Конечно, пользователя Linux такими мелочами не испугаешь, я тут же полез в окно настроек, где вручную сконфигурировал проект для сборки под wxX11. Далее выяснилось, что для того, чтобы собрать тестовое приложение надо сначала пересобрать wxWidgets, при этом некоторые поддиректории wxWidgets пришлось переименовывать вручную. После всех этих доработок тестовое приложение скомпилировалось и запустилось. Впрочем, и разочарования на этом не кончились. В текстовом редакторе DialogBlocks отсутствуют функции быстрого перехода между реализацией функции и ее объявлением, перехода к выбранному заголовочному файлу и им подобные. Фактически встроенный редактор DialogBlocks не намного лучше, чем KWrite.

Те, кто готов платить за средство разработки для wxWidgets, могут обратить внимание на еще одну коммерческую IDE – wxDesigner.

Среда разработки wxDesigner – полностью коммерческий продукт (вы можете бесплатно скачать пакет дистрибутива с сайта программы – www.roebling.de, но незарегистрированная версия не позволит вам сохранять проекты). Работает wxDesigner не очень стабильно, попытка создать диалоговое окно в режиме визуального программирования несколько раз заканчивалась ошибкой сегментации и аварийным завершением программы. Учитывая, что сам проект wxDesigner давно не обновлялся, я не рекомендовал бы вам тратить на него свои деньги. В общем и целом следует признать, что по уровню функциональности и удобства коммерческие IDE для wxWidgets не только не опережают, но даже несколько отстают от открытых IDE, предназначенных для других библиотек. Так, по крайней мере, обстоит дело на платформе Linux. Для тех, кто ведет разработку приложений wxWidgets на платформе Windows, ситуация складывается более благоприятно. Библиотека wxWidgets давно уже умеет интегрироваться с Microsoft Visual Studio, а с недавнего времени – и с Borland/CodeGear C++Builder. При этом для «билдера» есть специальный мастер создания форм wxWidgets. Некоторые разработчики нашли выход из положения благодаря кроссплатформенности wxWidgets – они ведут разработку приложений в одной из Windows IDE, а конечный продукт компилируют в Linux (не рассказывайте об этом фанатам Emacs и Eclipse, они побьют вас камнями, если узнают).

На платформе Linux частичная (без средств визуального редактирования) поддержка wxWidgets интегрирована в KDevelop и Anjuta. Эти интегрированные среды разработки умеют создавать заготовки приложений wxWidgets а также генерировать скрипты configure и make. Специально для редактирования графических интерфейсов wxWidgets создается программа wxGlade. Название проекта говорит само за себя – wxGlade претендует на роль Glade для wxWidgets. Редактор wxGlade написан на языке Python и в настоящем своем виде (версия 0.6) подходит больше для тех, кто пишет программы, используя именно этот язык (хотя поддержка C++ и Perl также присутствует).

Собственно говоря, все вышесказанное не должно вселять в вас пессимизм. Если вы можете обойтись без визуального программирования и интерактивных подсказок, для написания приложений wxWidgets подойдет любой текстовый редактор, хоть тот же KWrite.

Приступаем к работе

Хотя в вашем дистрибутиве Linux наверняка есть пакет разработчика для wxWidgets, я рекомендую собрать библиотеку из исходных текстов, доступных на сайте проекта (wxwidgets.org – просто на всякий случай). Скомпилировав wxWidgets, вы не только получаете новейшую версию библиотеки, но и более гибкие средства конфигурирования. Например, для этих статей я решил использовать wxX11, тогда как пакет wxWidgets из моего дистрибутива (OpenSUSE) сконфигурирован для 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 мы не обрабатываем сообщения, поэтому нам все равно, какой будет идентификатор у главного окна программы. В конструкторе wxFrame мы передаем константу wxID_ANY, которая указывает, что конструктор может сам выбрать идентификатор для создаваемого окна (константой wxID_ANY можно пользоваться всякий раз, когда идентификатор окна вас не интересует). В последнем задействованном нами параметре конструктора передается заголовок создаваемого окна (у конструктора wxFrame есть и другие параметры, для которых мы оставляем значения по умолчанию). В самом конструкторе мы создаем объект label класса wxStaticText (метка). Первые два параметра конструктора wxStaticText имеют тот же смысл, что и первые параметры конструктора wxFrame. В третьем параметре мы передаем строку текста для отображения. Обратите внимание, что в качестве идентификатора окна визуального элемента «метка» мы воспользовались константой wxID_STATIC. Этот идентификатор используется при создании статических визуальных элементов, которые не обрабатывают пользовательский ввод.

Вот, собственно, и все. Ах, да, вы, наверное, обратили внимание, что в программе не определена функция main(). Дело в том, что разработчики wxWidgets избавили нас от хлопот по написанию главной функции программы. Все необходимые определения содержит макрос IMPLEMENT_App(), которому мы передаем имя класса приложения в качестве параметра. Теперь программу можно скомпилировать, воспользовавшись вспомогательной утилитой wx-config:

g++ hwApp.cpp `wx-config --libs` `wx-config --cxxflags` -o hwApp

Трудно представить себе программу проще той, что мы написали. В продолжении серии мы рассмотрим процесс создания «настоящего» приложения – программы для записи разговоров Skype.

Врезки

Как должны выглядеть ваши программы?

Разработчиков кроссплатформенных наборов виджетов можно разделить на два лагеря: одни стремятся к тому, чтобы визуальные компоненты выглядели по возможности одинаково на всех платформах (обычно это люди с твердыми убеждениями относительно того, каким должен быть правильный графический интерфейс). Наборы визуальных компонентов этого типа радуют глаз единством фирменного стиля. Разработчики из другого лагеря настаивают на том, чтобы на каждой платформе внешний вид графических элементов управления максимально соответствовал тому, что принято на данной платформе. К достоинствам этого подхода относят то, что единообразие внешнего вида приложений упрощает, якобы, освоение новых программ. Лично я с этим аргументом не согласен. На мой взгляд, кнопки в стиле Aqua нисколько не мешают освоению Safari для Windows.

Самое сложное в освоении новой программы – согласовать подход разработчиков к решению поставленной задачи со своим собственным видением. Как бы там ни было, wxWidgets придерживается второго подхода, причем следует ему в гораздо большей степени, чем, скажем, Qt. Визуальные элементы wxWidgets не только выглядят на каждой платформе «как родные» (фактически, во многих случаях, классы wxWidgets – это просто обертки вокруг фирменных элементов управления), но и используют специфические возможности каждой платформы. Например, на платформе Win32 wxWidgets поддерживает метафайлы, которые отсутствуют в GTK. Если вы программируете интерфейсы с помощью wxWidgets, вы должны сами устанавливать баланс использования платформо-специфичных и кроссплатформенных возможностей библиотеки.

Центральная проблема GUI-программирования

Проблема обработки сообщений возникает во всех объектно-ориентированных графических библиотеках, написанных на C++. Представьте себе объект, реализующий кнопку (пусть это будет объект класса Button). Во время выполнения программы кнопка – объект button1 класса Button получает информацию о событиях графической системы (например, о щелчке мышью). В ответ на щелчок мышью для объекта button1 вызывается метод класса Button (назовем его OnClick()), который должен оповестить остальные компоненты программы о внешнем событии.

Как заставить кнопку делать именно то, что нам нужно? Можно создать класс-потомок Button, например, MyButton, и переопределить в нем метод OnClick() базового класса. Этот способ прост, но неудобен. В вашей программе наверняка будет несколько кнопок и при таком подходе вам придется создавать собственный класс для каждой из них. Необходимость создавать собственный класс для каждой кнопки не только сделает ваш код громоздким и трудным в управлении (представьте себе, что вы сначала использовали для создания кнопок базовый класс Button, а потом захотели воспользоваться другой реализацией кнопки), но и, в некотором смысле, противоречит принципам ООП. Ведь кнопка остается кнопкой, независимо от того, как она реагирует на события. Зачем же создавать новый класс для каждой конкретной кнопки?

Разработчики библиотек визуальных элементов отдают предпочтение модели, в которой для задания собственных обработчиков событий необходимо переопределить только один класс. Предположим, что в нашем гипотетическом наборе виджетов есть класс Window, который реализует главное окно. Мы определяем класс-потомок класса Window (например, MyWindow) и определяем обработчики всех событий всех дочерних элементов главного окна как методы этого класса. Все кнопки, используемые в окне класса MyWindow, могут быть объектами класса Button. Обработчиком события OnClick каждой кнопки будет один из методов MyWindow. Нам остается только сообщить каждому объекту Button, какой из методов объекта класса MyWindow является обработчиком его события. Тут-то и возникает проблема. Мы должны «прикрепить» методы MyWindow к объектам Button (при этом класс Button ничего не знает о классе MyWindow). В некоторых языках программирования (Objective-C, C#) эта проблема была решена на уровне самого языка. В C++ стандартного решения не существует, и разработчики наборов виджетов реализуют механизмы обработки сообщений по своему разумению. Все используемые сегодня решения основаны, так или иначе, на функциях обратного вызова. Фактически, разные библиотеки виджетов различаются только способами оформления этого подхода.

Полезные ссылки

  • Викифицированный справочник по всем классам, переменным, функциям и макросам библиотеки wxWidgets можно найти на сайте проекта по адресу: wiki.wxwidgets.org/docbrowse.html.
  • Хорошим источником информации о программировании в wxWidgets может служить книга «Cross-Platform GUI Programming with wxWidgets», написанная Джулианом Смартом [Julian Smart], Кевином Хоком [Kevin Hock] и Стефаном Шомором [Stefan Csomor]. Книга вышла в 2005 году, но не утратила актуальности до сих пор. Правила серии Bruce Perens Open Source Series, к которой принадлежит это издание, позволяют абсолютно легально загрузить его электронную версию (на английском языке) с сайта www.phptr.com/perens. Бумажный вариант можно приобрести на Amazon.com по весьма умеренной цене.
Персональные инструменты
купить
подписаться
Яндекс.Метрика