Пишем приложение на GTK+ используя C++ и GTKMM библиотеку
Однажды попросила меня жена написать ей простенькую программку, которая сможет вычислять площади фигур, периметры, и другие параметры при наличии достаточных данных. Например, нужна площадь треугольника, указаны его стороны. Вводим стороны нажимаем кнопочку и получаем площадь. Или указана только сторона и два угла. В общем любые данные, достаточные для того чтобы вычислить остальное.
Стоит отметить, что я являюсь последние лет 5 только веб-разработчиком, в основном PHP, хотя конечно иногда что-то нужно сделать и на ruby и на perl. В общем язык для меня особо не проблема, главное понять смысл процессов в компьютере, а дальше хоть Assembler (когда-то даже занимался дизасемблированием и небольшим патчингом приложений под Windows). Но все-таки когда писал десктопные приложения уже и не помню. Но тут решил написать именно десктопное, чтобы жене было удобно им пользоваться при отсутствии интернета и не нужно было на ее ноутбук ставить вебсервер с PHP. Кроме того уже давно хотел попробовать себя в использовании языка C++. Ну что ж. У жены стоит на ноутбуке Linux Ubuntu. Графическая система — Unity, основанная на Gnome3. А там где Gnome, там GTK+.
Вот так и было решено написать десктопное приложение под Linux используя Gtk+. Интересно? Добро пожаловать под кат!
Понимание библиотеки GTK+
Стоит заметить, что большинство библиотек и приложений в Linux выполнены с использованием процедурного подхода. У меня же настолько укоренилось объектно ориентированное мышление что без полноценного использования ООП мне жутко не комфортно. Решил я обернуть функции вызовы GTK+ в свои самописные классы. Понятное дело, сделал я это абсолютно непрофессионально, без использования специфических для C++ конструкций. Что и говорить, Страуструп был прочитан менее чем на половину, а весь мой ООП исходит оттуда, где я его начал использовать — из PHP. Некрасиво, но сработало. Легче получилось нарисовать элементы, отобразить окошко, обработать события. Но все же не то. Реализовав таким образом приложение я оставил его на год. Заморозил. Но вдруг на глаза мне попалась библиотека GTKMM — официальный C++-интерфейс для GUI-библиотеки GTK+. И вот взял я это заброшенное приложение и начал его преобразовывать. Понятное дело основные самописные классы я выкинул, так как заменил их классами GTKMM. Вот своим опытом знакомства с этой замечательной библиотекой я хочу с вами поделиться, мои дорогие читатели. Итак, приступим.
Сразу прошу прощения, что опущу создание интерфейса нашего приложения в начале, хотя, считаю что именно с создания внешнего вида (точнее расположения элементов управления на форме, для того чтобы в дальнейшем оживлять их), но все же приложение было написано и отрисовка в первой версии происходила с помощью вызовов определенных функций. Теперь же было решено использовать GtkBuilder, но об этом немного позднее.
Точка входа
Точкой входа в приложение на C++ является функция main() . В нашем приложении она будет загружать из GtkBuilder файла нашу форму, отображать ее и запускать цикл обработки сообщений приложением.
Инициализируем приложение
Glib::RefPtr app = Gtk::Application::create(argc, argv, "org.apmpc.geometry");
builder = Gtk::Builder::create_from_file(UI_FILE);
Привяжем форму к классу — обработчику (класс используется собственный, который является наследником от Gtk::Window , поэтому будем использовать метод get_widget_derived .
builder->get_widget_derived("MainWindow", appwindow);
Для справки, если вы хотите описывать обработчики в виде функций и не хотите плодить классы для небольшого приложения, небольшой формы или по религиозным причинам, вы можете использовать объект класса Gtk::Window , как указано в документации и в таком случае будет использоваться метод get_widget .
Ну и конечный пункт — запуск цикла сообщений приложения
Приложение
Поздно, но все же немного подробнее опишу составные части приложения. Оно состоит из двух форм. Первая — главное окно. На нем расположены кнопки фигур.
Нажимаем на кнопку — выбираем фигуру. Далее открывается вторая форма — окно фигуры.
Слева находятся поля ввода значений и результата, справа — рисунок выбранный фигуры. При проектировании учитывалось расширение функциональности, добавление фигур, поэтому кнопки выбора фигур создаются при запуске главного окна.
Для окна главной формы создан класс MainWindow, наследующийся от Gtk::Window . В конструкторе этого класса происходит создание кнопок фигур. Хранилищем указателей на кнопки является вектор m_pvShapeButtons .
m_pvShapeButtons.push_back(new Gtk::Button(CShape::getShapeName(i+1)));
m_pvShapeButtons.at(i)->signal_clicked().connect( sigc::bind (sigc::mem_fun( *this, &MainWindow::onShapeButtonClick), (i+1) ) );
Мне необходимо передавать индекс фигуры в метод обработки события clicked, так как существует единственный класс обработки фигуры CShape . В нем находится информация о остальных классах фигур, которые наследуются от класса CShape . В них уже информация о полях для ввода данных, названия этих полей и формулы для расчета.
Ну и не забываем прикрепить кнопку к контейнеру и отобразить
m_pShapeButtonBox->pack_end(*m_pvShapeButtons.at(i)); m_pvShapeButtons.at(i)->show();
При нажатии на кнопку мы открываем другое окно. По индексу мы определяем фигуру и выводим поля для ввода данных. Поле для ввода состоит из трех частей — контейнера Gtk::Box , содержащего метку Gtk::Label для вывода названия поля ввода и поле для ввода текста Gtk::Entry . Все это было решено объединить в один класс с названием CEditBox . Так же он будет содержать методы, необходимые для ввода и вывода данных с преобразованием типов.
В конструкторе ShapeWindow создадим необходимое количество CEditBox для выбранной фигуры. Отрисуем фигуру в правой стороне окна, где находится объект класса CCanvas , который наследуется от Gtk::DrawingArea . Отрисовка происходит по событию onDraw . Информация о фигуре содержится в классе фигуры. Метод draw класса фигуры вызывает методы класса CCanvas для отрисовки графических примитивов (Линии, окружности, текст)
Заключение
Вот и все что хотелось бы сообщить о моем первом приложении на GTK+ с использованием GTKMM, да и вообще на C++. Весь код находится на github. Дальнейшие планы — сделать реализацию расчета площади параллелограмма, добавить возможность сбора пакета debian и выложить на ppa. Буду рад ответить на все вопросы и комментарии к данной статье.
UPD. Исправил функциональный подход на процедурный. Как оказалось, словосочетание функциональный подход, или точнее функциональное программирование используется в случаях когда происходит вычисление функций в математическом понимании, а не функций как подпрограмм, как я думал изначально. Для примера, языки которые используют функциональный подход: LISP, Erlang, Scala. C и C++ — это процедурные языки.
Создаем приложение используя GTK в Линуксе
Разрабатывать приложения в Линуксе можно с помощью фреймворка GTK. Эта библиотека предназначенная для построения графического интерфейса пользователя. GTK — это свободное программное обеспечение, которое распространяется по лицензии GNU LGPL, что позволяет создавать как бесплатные так и коммерческие приложения.
С документациею и описанием библиотеки, Вы можете ознакомиться по следующей ссылке: https://www.gtk.org
Перед написанием приложения необходимо подготовить среду разработки. Для этих целей буду использовать ОС Ubuntu 22.04. Так как приложение будет разрабатываться на языке программирования Си необходимо установить необходимые пакеты и компилятор.
Откройте терминал от имени пользователя root и введите следующую команду:
sudo apt-get install build-essential
Также нам необходимо установить пакеты GTK для разработки приложения. Вводим следующую команду в терминале:
sudo apt-get install -y libgtk-3-dev
Теперь все готово для написания первого приложения. Открываем любой текстовый редактор и пишем следующий код:
#include int main (int argc, char *argv[])
Этот код создает окно. Но прежде чем выполнить приложение, давайте разберемся немного с кодом.
#include — здесь мы подключаем библиотеку gtk. Без этой библиотеки не будет работать графическое окно и мы не сможем запустить приложение;
GtkWidget *window; — задаем имя/метку окна;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL); — эта строка создает стандартное окно с рамкой;
gtk_widget_show(window); — отображаем окно на дисплее.
Теперь давайте запустим приложение. Я сохранил код приложения в файле ex1.c в домашней папки.
Открывает терминал и вводим следующие команды:
gcc ex1.c -o ex1 `pkg-config —cflags —libs gtk+-3.0
Теперь давайте улучшим код и создадим текстовую метку.
#include int main (int argc, char *argv[])
GtkWidget *label1; — задаем имя текстовой метки;
label1 = gtk_label_new(«Hello! My first Program»); — создаем текстовую метку;
gtk_container_add(GTK_CONTAINER(window), label1); — эта строка кода добавляет метку в контейнер;
gtk_widget_show(label1); — отображаем метку в окне.
Надеюсь эта публикация была полезная. Так как фреймворк GTK имеет очень много полезных и интересных функций, что не дает возможности объяснить все в одной публикации.