Библиотека сайта rus-linux.net
Программирование с использованием gtkmm 3. Базовые сведения
Оригинал: Programming with gtkmm 3
Авторы: Murray Cumming, Bernhard Rieder, Jonathon Jongsma, Ole Laursen, Marko Anastasov, Daniel Elstner, Chris Vine, David King, Pedro Ferreira, Kjell Ahlstedt
Перевод: А.Панин
3. Базовые сведения
В данной главе описаны основные аспекты процесса разработки приложений с использованием gtkmm. Для их демонстрации приведен простой работоспособный пример кода. Однако, это всего лишь вводное описание, поэтому вам придется рассмотреть остальные главы для получения более подробной информации.
Ваши знания из области языка программирования C++ помогут вам в работе с gtkmm, также, как и при работе с любой другой библиотекой. Если мы не предупреждаем об обратном, вы можете быть уверенными в том, что классы gtkmm ведут себя аналогично любому другому классу языка C++, причем вы можете использовать известные вам техники работы с классами языка C++ для работы с классами gtkmm.
3.1. Простой пример
В начале нашего вводного описания gtkmm рассмотрим простейшую из возможных программ. Эта программа создает пустое окно размером 200 x 200 пикселей.
Файл base.cc
(Предназначен для использования совместно с gtkmm 3, а не с gtkmm 2):
#include <gtkmm.h>
Каждая из использующих gtkmm программ должна подключать определенные заголовочные файлы gtkmm; подключение заголовочного файла gtkmm.h
позволяет подключить все заголовочные файлы из состава gtkmm. Обычно использование этого заголовочного файла не является удачной идеей, так как происходит подключение около мегабайта заголовочных файлов, но в случае простых программ это допустимо.
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
позволяет создать объект Gtk::Application
, сохраняемый с помощью умного указателя RefPtr
. Это действие должно выполняться во всех приложениях на основе gtkmm. Метод create()
этого объекта производит инициализацию gtkmm и проверку аргументов, передаваемых вашей программе в командной строке с одновременным поиском таких стандартных параметров, как --display
. Он извлекает стандартные параметры из списка параметров, после чего в списке остаются только неизвестные параметры, которые могут быть использованы или проигнорированы на уровне приложения. Такой алгоритм работы позволяет быть уверенным в том, что все приложения на основе gtkmm будут принимать один и тот же набор стандартных аргументов.
Gtk::Window window; window.set_default_size(200, 200);
main()
сможет вернуть соответствующий код удачного завершения работы приложения или ошибки.
return app->run(window);
simple.cc
вы сможете скомпилировать описанную выше программу с помощью компилятора gcc, воспользовавшись следующей командой:
g++ simple.cc -o simple `pkg-config gtkmm-3.0 --cflags --libs`
Следует отметить, что вы должны экранировать вызов pkg-config
с помощью обратных кавычек. Обратные кавычки позволяют командной оболочке выполнить команду внутри них и использовать вывод этой команды в качестве части основной команды. Также следует отметить, что имя файла simple.cc
должно быть расположено перед вызовом pkg-config
в рамках основной команды.
3.2. Заголовочные файлы и связывание
Хотя мы и показали команду компиляции простого примера, при разработке реальных приложений вам следует использовать инструменты automake и autoconf таким образом, как это описано в работе G. V. Vaughan и других авторов под названием "Autoconf, Automake, Libtool". Исходный код примеров, использованных в данной книге, включен вместе с соответствующими файлами сборки в пакет gtkmm, поэтому впредь мы не будем приводить команды сборки. Для осуществления сборки примера вам всего лишь потребуется найти соответствующую директорию и выполнить в ней команду make
.
Для упрощения компиляции мы используем инструмент pkg-config
, вспомогательные файлы для которого присутствуют во всех (корректных) установках gtkmm. Эта программа "знает" о том, какие параметры компилятора необходимы для компиляции программ, использующих gtkmm. Параметр --cflags
сообщает pkg-config
о необходимости вывода списка директорий с подключаемыми заголовочными файлами для того, чтобы компилятор имел возможность найти необходимые файлы; с помощью параметра --libs
осуществляется запрос списка библиотек которые будут связываться приложением, а также директорий, в которых компилятор сможет найти их. Испытайте соответствующие команды в командой оболочке для того, чтобы узнать результаты их выполнения в вашей системе.
PKG_CHECK_MODULES()
в стандартном файле configure.ac
в случае работы с autoconf и automake. Пример использования:
PKG_CHECK_MODULES([MYAPP], [gtkmm-3.0 >= 3.0.0])
Это объявление позволяет проверить наличие gtkmm в системе и установить значения макросов MYAPP_LIBS
и MYAPP_CFLAGS
, которые будут использоваться в ваших файлах Makefile.am
.
Текущий стабильный API носит имя gtkmm-3.0. Ранее использовалась устаревшая на данный момент версия API с именем gtkmm-2.4, которая сейчас может быть установлена параллельно с другими версиями. Существовало несколько версий gtkmm-2.4, таких как gtkmm-2.10 и при этом также будет выпущено несколько версий API gtkmm-3.0. Следует учесть, что имя API не меняется при выпуске каждой версии, так как в противном случае каждая из версий должна быть несовместимой с другими на уровне API и ABI. Теоретически в будущем будет выпущена версия API gtkmm-4.0, которая сможет устанавливаться параллельно с gtkmm-3.0 без оказания влияния на существующие приложения.
Следует отметить, что в том случае, если вы используете дополнительные модули в дополнение к gtkmm-3.0, они должны быть отделены пробелами, а не запятыми.
На ресурсе opensimus можно найти документ с базовой информацией об инструментах automake и autoconf.
3.3. Виджеты
Приложения на основе gtkmm состоят из окон, содержащих такие виджеты, как кнопки и поля ввода текста. В некоторых других системах виджеты называются "элементами управления". Каждому из виджетов в окнах вашего приложения соответствует объект языка программирования C++ в коде вашего приложения. Таким образом, вам нужно всего лишь использовать метод класса вашего виджета для воздействия на состояние видимого виджета.
Gtk::Grid
не являются видимыми - они существуют только для того, чтобы была возможность размещения других виджетов. Ниже приведен пример кода, предназначенного для добавления 2 виджетов-кнопок, представленных классом Gtk::Button
, в контейнерный виджет, представленный классом Gtk::Box
:
m_box.pack_start(m_Button1); m_box.pack_start(m_Button2);
Gtk::Box
и содержащего эти кнопки, в контейнерный виджет, представленный классом Gtk::Frame
и имеющий видимую рамку и заголовок:
m_frame.add(m_box);
В большей части глав данной книги рассматриваются определенные виджеты. Обратитесь к главе "Контейнерные виджеты" для получения более подробной информации о методах упаковки виджетов в контейнерные виджеты.
Хотя вы и можете задать параметры размещения и отображения виджетов на уровне кода на языке программирования C++, проектирование интерфейсов с использованием инструмента Glade
и загрузка их в процессе работы приложения с помощью класса Gtk::Builder
, скорее всего, покажется вам более удобным методом разработки пользовательских интерфейсов. Поэтому вам также следует обратить внимание на главу "Glade и Gtk::Builder".
Хотя экземпляры классов виджетов gtkmm и характеризуются такими же временами жизни и областями действия, как экземпляры других классов языка программирования C++, gtkmm предоставляет дополнительную возможность для экономии времени, применение которой вы можете увидеть в некоторых примерах. Метод Gtk::manage()
позволяет сообщить механизму управления памятью о том, что виджетом владеет контейнер, в котором он размещен. Эта возможность позволяет вам создать виджет с помощью оператора new
, поместить его в контейнер и забыть о его удалении. Подробнее о техниках управления памятью в gtkmm вы можете узнать, обратившись к главе "Управление памятью".
3.4. Сигналы
gtkmm, как и большинство тулкитов для построения графического пользовательского интерфейса управляется событиями. В момент наступления события, такого, как нажатие кнопки мыши, виджет, в области которого было осуществлено нажатие, осуществит генерацию соответствующего сигнала. Каждый виджет имеет отдельный набор сигналов, которые он может генерировать. Для того, чтобы в результате нажатия на кнопку выполнялось действие мы устанавливаем обработчик сигнала, который будет захватывать сигнал "clicked" ("нажата") от кнопки.
Gtk::Button
виджета с обработчиком сигнала с названием "on_button_clicked":
m_button1.signal_clicked().connect( sigc::mem_fun(*this, &HelloWorld::on_button_clicked) );
Для получения более подробной информации о сигналах, следует обратиться к приложению.
Если же вас интересует информация о способе реализации ваших собственных сигналов вместо простого соединения с уже существующими в рамках gtkmm сигналами, вам следует обратиться к следующему приложению.
3.5. Glib::ustring
Вы можете удивиться, когда узнаете, что gtkmm не использует класс std::string
в своих интерфейсах. Вместо него используется класс Glib::ustring
, который является настолько похожим и устроен аналогично упомянутому классу, что вы на самом деле можете считать, что каждый класс Glib::ustring
на самом деле является классом std::string
и проигнорировать оставшуюся часть данного раздела. Но вам все же следует прочитать раздел до конца в том случае, если вы планируете использовать в своем приложении языки, отличные от английского.
Класс std::string
использует 8 бит для кодирования каждого символа, но 8 бит не достаточно для кодирования символов таких языков, как арабский, китайский и японский. Хотя методы кодирования символов таких языков и были описаны Консорциумом Unicode, языки программирования C и C++ до сегодняшнего дня не предоставляют каких-либо стандартизированных механизмов для работы с кодировкой Unicode. Разработчики GTK+ и GNOME решили реализовать поддержку кодировки Unicode, реализовав механизмы для работы с кодировкой UTF-8, которые и используются классом Glib::ustring
. Он предоставляет практически тот же интерфейс, что и класс std::string
с дополнительными функциями для конвертирования данных из представления и в представление класса std::string
.
Одно из достоинств кодировки UTF-8 заключается в том, что вы можете не использовать ее тогда, когда не хотите, поэтому вам не придется модифицировать весь ваш код одномоментно. Класс std::string
будет работать и с 7-битными строками в кодировке ASCII. Но после начала локализации вашего приложения для поддержки таких языков, как, например, китайский, вы столкнетесь со странными ошибками и, возможно, с аварийными завершениями работы приложения. В этом случае все, что вам потребуется - это начать использовать класс Glib::ustring
вместо стандартного.
Следует учесть, что кодировка UTF-8 не является совместимой с такими 8-битными кодировками, как ISO-8859-1. К примеру, немецкие умляуты не находятся в диапазоне символов ASCII и требуют более одного байта для кодирования с использованием кодировки UTF-8. Если ваш код содержит строку с 8-битными символами, вам придется преобразовать их в кодировку UTF-8 (т.е., баварское приветствие "Grüß Gott" будет преобразовано к виду "Gr\xC3\xBC\xC3\x9F Gott").
Вам следует избегать использования арифметических действий с указателями в стиле языка программирования C, а также использования таких функций, как strlen()
. В кодировке UTF-8 для кодирования символа может потребоваться от 1 до 6 байт, поэтому невозможно точно сказать, будет ли следующий байт представлять следующий символ. Класс Glib::ustring
заботится об обработке данных символьных последовательностей, поэтому вы можете использовать такие методы, как Glib:ustring::substr()
, производя расчеты в символах вместо байт.
В отличие от используемого в Windows решения, заключающегося в применении кодировки Unicode UCS-2, описанная кодировка не требует каких-либо специальных параметров компиляции для обработки символов строк и ее применение не приводит к появлению исполняемых файлов и библиотек с поддержкой кодировки Unicode, которые не совместимы с программными компонентами, поддерживающими кодировку ASCII.
Обратитесь к разделу "Интернационализация" для получения более подробной информации о работе с символами строк, использующих кодировку UTF-8.
3.6. Промежуточные типы
Некоторые связанные с gtkmm API используют промежуточные контейнеры для данных, такие, как Glib::StringArrayHandle
вместо таких специальных контейнеров из стандартной библиотеки языка программирования C++, как std::vector
или std::list
, хотя на уровне gtkmm начиная с gtkmm 3.0 используется исключительно класс std::vector
.
- Вместо
Glib::StringArrayHandle
илиGlib::ArrayHandle<Glib::ustring>
следует использоватьstd::vector<Glib::ustring>
,std::list<Glib::ustring>
,const char*[]
и.т.д. - Вместо
Glib::ListHandle<Gtk::Widget*>
следует использоватьstd::vector<Gtk::Widget*>
,std::list<Gtk::Widget*>
и.т.д. - Вместо
Glib::SListHandle<Gtk::Widget*>
следует использоватьstd::vector<Gtk::Widget*>
,std::list<Gtk::Widget*>
и.т.д.
3.7. Смешивание API языков C и С++
Вы можете использовать API языка C, которые на данный момент не имеют соответствующих интерфейсов на уровне языка C++. Обычно использование API языка C из приложения, разработанного с использованием языка C++, не является большой проблемой и gtkmm способствует этому, предоставляя доступ к расположенному уровнем ниже объекту языка C, а также предоставляя простой способ создания обертки для объекта на языке C++, предназначенной для работы с рассматриваемым объектом языка C, благодаря тому, что API языка C также основывается на системе GObject.
gobj()
, позволяющую получить указатель на расположенный уровнем ниже экземпляр класса GObject. Например:
Gtk::Button* button = new Gtk::Button("пример"); gtk_button_do_something_new(button->gobj());
Glib::wrap()
. Например:
GtkButton* cbutton = get_a_button(); Gtk::Button* button = Glib::wrap(cbutton);
3.8. Приложение Hello World на основе gtkmm
Сейчас мы обладаем достаточными знаниями для рассмотрения реального примера. В соответствии со старой традицией из области компьютерных наук, мы будем исследовать приложение Hello World, созданное на основе gtkmm:
Файл helloworld.h
(Для использования совместно с gtkmm 3, а не с gtkmm 2)
Файл helloworld.cc
(Для использования совместно с gtkmm 3, а не с gtkmm 2)
Файл main.cc
(Для использования совместно с gtkmm3, а не с gtkmm 2)
Попробуйте скомпилировать и запустить приложение перед тем, как двигаться дальше. Вы должны увидеть что-то похожее на это:
Рисунок 3-1: Hello World
class HelloWorld : public Gtk::Window { public: HelloWorld(); virtual ~HelloWorld(); protected: //Обработчики сигналов: virtual void on_button_clicked(); //Используемые виджеты: Gtk::Button m_button; };
Gtk::Window
и имеет единственный дочерний класс Gtk::Button
. Мы решили использовать конструктор для выполнения всей работы по инициализации окна, включая установку обработчиков сигналов. Ниже приведен этот код с исключенными комментариями:
HelloWorld::HelloWorld() : m_button ("Hello World") { set_border_width(10); m_button.signal_clicked().connect(sigc::mem_fun(*this, &HelloWorld::on_button_clicked)); add(m_button);. m_button.show(); }
Заметьте, что мы использовали объявление инициализации для вывода надписи "Hello World" на кнопке, представленной объектом m_button
.
После мы вызвали метод set_border_width()
класса Gtk::Window
. Этот метод позволяет установить ширину пространства между сторонами окна и виджетом, находящимся в окне.
После этого мы установили обработчик для сигнала "clicked"
объекта m_button
. Он выводит дружественное приветствие в поток стандартного вывода (stdout
).
Впоследствии мы использовали метод add()
класса Gtk::Window
для упаковки виджета, представленного объектом m_button
, в виджет, представленный объектом окна helloworld
. (Метод add()
помещает виджет, представленный экземпляром класса Gtk::Widget
, в виджет, представленный экземпляром класса Gtk::Window
, но не позволяет показать виджет. Виджеты в gtkmm обычно невидимы сразу же после создания - для их показа необходимо вызывать метод show()
, что мы и делаем в следующей строке.
main()
нашей программы. Эта функция с исключенными комментариями приведена ниже:
int main(int argc, char** argv) { Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); HelloWorld helloworld; return app->run(helloworld); }
Для начала мы создаем объект с именем app
, хранимый в умном указателе RefPtr
. Это экземпляр класса Gtk::Application
. Каждая программа на основе gtkmm должна иметь один такой объект. Мы передаем наши аргументы командной строки в качестве параметров его метода create()
. Он обрабатывает те аргументы, которые пожелает, и оставляет вам все остальные, как мы и говорили ранее.
После этого мы создаем объект на основе нашего класса HelloWorld
, конструктор которого не принимает никаких аргументов, но пока это не заметно. Когда мы вызываем метод Gtk::Application::run()
, передавая экземпляр класса Gtk::Window
приложения HelloWorld, происходит показ окна и запуск цикла обработки событий gtkmm. В процессе работы цикл обработки событий gtkmm ожидает действий от пользователя и отвечает на них соответствующим образом. В момент, когда пользователь закрывает окно, метод run()
возвращает управление, что приводит к исполнению последней строки кода нашей функции main()
. После этого работа приложения завершается.
Следующий раздел : 4. Изменения в gtkmm 3.