Последние записи
- Преобразовать массив байт в вещественное число (single)
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
- Определить текущую ОС
- Автоматическая смена языка (раскладки клавиатуры)
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
28th
Апр
Кроссплатформенный осциллограф на GTK+/Cairo
Posted by admin under Журнал, Статьи
Моей основной операционной системой является Linux, а основным окружением рабочего стола – Gnome. Предыдущий мой урок «Анимированный осциллограф на WinAPI в С++» [1] вызвал интерес форумчан, поэтому я решил сделать кроссплатформенную версию этого импровизированного осциллографа и полностью переписал программу на GTK+. Данная статья может быть полезна всем, кто только начал или собирается изучать этот «тулкит». Параллельно в уроке рассматривается использование некоторых функций мощной библиотеки двухмерной графики Cairo.
Автор: Олег Кутков
Но, в начале немного теории
GTK+ – это кроссплатформенная библиотека, предназначенная для создания приложений с графическим интерфейсом пользователя, предоставляет богатый набор функций на все случаи жизни. Изначально библиотека разрабатывалась для программы Gimp. Библиотека написана на языке С, но тем не менее сохраняет принципы объектно-ориентированного программирования. Для использования возможностей библиотеки, в Вашей системе, должна быть установлена как сама библиотека, так и заголовочные файлы (dev, devel пакеты) к ней, все это Вы можете сделать с помощью менеджера пакетов Вашего дистрибутива.
После установки всех недостающих компонентов можно подключать заголовочные файлы библиотеки в свою программу:
#include
Основными объектами библиотеки, с которыми нам придется иметь дело, являются так называемые виджеты. Каждый элемент управления (кнопка, метка, поле ввода) является виджетом. Само окно так же является виджетом. Еще одно очень важное понятие GTK+ – сигналы. Их можно считать, своего рода, аналогами сообщений в Windows. Сигналы посылают различные элементы управления (нажата кнопка, передвинуто окно), а так же функции, при наступлении некоторого события (сработал таймер). Для обработки сигналов необходимо создавать функции-обработчики. Но функция-обработчик не будет взаимодействовать с сигналом сама по себе, для этого их необходимо связать. Это делается с помощью специальных методов GTK+. Обратите, так же, внимание на явное приведение типов аргументов функций.
Настало время рассмотрения программы
В самом начале программы подключаем необходимые заголовочные файлы, объявляем необходимые переменные и функции. Комментарии, думаю, все поясняют.
#include
#include
#include
// объявляем функцию, обработчик сигнала закрытия окна
static void destroy(GtkWidget*, gpointer);
// объявляем функцию, обработчки сигнала перерисовки окна
static void draw(GtkWidget*, GdkEventExpose*, gpointer);
// объявляем функцию, объявляющую область перерисовки недействительной
static gboolean time_handler(GtkWidget*);
// две переменные, смещение синусоиды и текущее положение точки графика
gint count = 0, t = 0;
double radianPerx = 2 * 3.14 / 90; // угол радиан, определяет расстояние между гребнями волн
const double uAmplit = 900; // амплитуда синусоиды
После объявлений – главная функция main, в самом объявлении функции нет ничего необычного. Рассмотрим что твориться в ней. Первым делом мы должны объявить два виджета, это наше будущее окно и область, в которой мы будем рисовать
GtkWidget *window, *drawing;
Пока что это просто виджеты, объекты, без указания конкретного типа. Далее идет инициализация GTK:
gtk_init(&argc, &argv);
Функции передаются два аргумента – аргументы функции main. Теперь создаем новое окно и задаем ему необходимые параметры:
// создаем новое, обычное окно
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// задаем текст заголовка окна
gtk_window_set_title(GTK_WINDOW(window), "Осциллограф");
// запрещаем изменение размеров окна
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
// задаем размер окна
gtk_widget_set_size_request(window, 600, 400);
// задаем стартовую позицию - центр экрана
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
// задаем отступы контейнера окна
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
Тут стоит сказать, что в GTK элемент управления не может существовать произвольно в любом месте. Его может содержать в себе некий не визуальный объект-контейнер. Окно приложения может выполнять функцию контейнера, но может содержать только один объект, для расположения в окне множества элементов в окне – следует расположить в контейнера окна другой контейнер. Сам контейнер может содержать сколь угодно много элементов. Функция gtk_container_set_border_width задает “поля” контейнера окна. Если не вызывать эту функцию – контейнер, содержащий в себе, в данном случае, поле рисования, будет равен размеру самого окна, без заголовка.
Следующей функцией мы создаем новый таймер, с интервалом срабатывания – 100 миллисекунд и привязываем к таким срабатываниями функцию time_handler:
g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window);
Далее мы создаем новую область для рисования и помещаем ее в контейнер окна:
// создаем новую область для рисования
drawing = gtk_drawing_area_new();
// помещаем ее в контейнер окна
gtk_container_add(GTK_CONTAINER(window), drawing);
И самое интересное, в главной функции – связываневизувиниие сигналов с обработчиками делается с помощью функции g_signal_connect:
// связываем сигнал уничтожения окна и функцию-обработчик
g_signal_connect(GTK_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);
// связываем сигнал перерисовки области рисования и функцию-обработчик
g_signal_connect(G_OBJECT(drawing), "expose_event", G_CALLBACK(draw), NULL);
Первым параметром функции является объект, который мониторится, следующий параметр – название сигнала, третий параметр – функция-обработчик сигнала. Последний параметр, который мы не используем, в данном случае – дополнительный параметр, который может быть передан в функцию-обработчик.
Оставшиеся две функции выполняют отображение всех виджетов на экране и запуск основного цикла программы:
gtk_widget_show_all(window);
gtk_main();
Теперь рассмотрим собственно функции-обработчики сигналов. Пойдем от простого к сложному. Функция-обработчик сигнала закрытия окна:
static void destroy(GtkWidget* window, gpointer data)
{
// завершаем главный цикл приложения
gtk_main_quit();
}
Здесь все просто, при вызове этой функции происходит вызов gtk_main_quit() и завершение работы программы. Стоит заметить, что если не написать этот обработчик – после закрытия окна, процесс бы оставался висеть в памяти и ожидать сигналов.
Функция-обработчик сигнала таймера:
static gboolean time_handler(GtkWidget* widget)
{
// если передан нулевой параметр - возвращаем ложь
if (widget->window == NULL) return FALSE;
// увеличиваем значение переменной смещения графика
count++;
// обнуляем точку положения на графике
t = 0;
// заставляем окно перерисоваться
gtk_widget_queue_draw(widget);
return TRUE;
}
Здесь так же нет ничего замудренного. Сначала выполняется корректность переданного параметра, если он нулевой – функция завершается, вернув FALSE. Если функция не завершилась – происходит увлечение значения переменной, задающей смещение рисуемого графика. Так же обнуляется вспомогательная переменная, указывающая текущую точку на графике. Следующий вызов gtk_widget_queue_draw(widget) объявляет, переданный ему виджет, визуально недействительным и требующим перерисовки.
И наконец, самое интересное – функция, выполняющая отрисовку координатной сетки и синусоиды. Для рисования используется библиотека Cairo, “встроенная” в Gtk. Мы будем использовать следующие методы:
- gdk_cairo_create(GtkWidget* ) – данная функция создает, на основе переданного ей виджета, новый контекст рисования Cairo;
- cairo_rectangle (cr, x1, y1, x2, y2) – данная функция рисует, в контексте рисования Cairo прямоугольник по заданным координатам;
- cairo_set_line_width (cr, 3) – данная функция задает ширину линии, которой производится рисования в указанном контексте;
- cairo_set_source_rgb (cr, r, g, b) – данная функция задает цвет линии, которой производится рисования в указанном контексте, параметры r, g, b определяют, соответственно, интенсивность красного, зеленого и синего цветов;
- cairo_move_to(cr, x, y) – с помощью этой функции мы переходим в новую исходную точку, из которой будем рисовать;
- cairo_line_to (cr, x, y) – задаем точку, куда следует нарисовать линию;
- cairo_stroke (cr) – собственно выполнение рисования в контексте;
- cairo_show_text(cr, buf*) – рисование текста в заданном контексте, в точке, предварительно заданной cairo_move_to.
Собственно, это все функции, которые выполняют всю работу в данном обработчике. Рисование самой синусоиды производится методом, знакомым по предыдущему уроку. Ниже приведен полный исходный код функции, с комментариями:
static void draw(GtkWidget* drawarea, GdkEventExpose* event, gpointer data)
{
int ya, xa; // переменные для хранения X и Y координат рисования
char *buf; //текстовый буфер для хранения текста метки
// создаем область для рисования Cairo
cairo_t *cr = gdk_cairo_create(drawarea->window);
// рисуем рамку, размером с область рисования
cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
// задаем толщину линии
cairo_set_line_width (cr, 3);
// задачем черный цвет
cairo_set_source_rgb (cr, 0, 0, 0);
// рисуем линию оси Y
cairo_move_to(cr, 40, 0);
cairo_line_to (cr, 40, 350);
cairo_stroke (cr);
// рисуем линию оси X
cairo_move_to (cr, 30, 340);
cairo_line_to (cr, 600, 340);
cairo_stroke (cr);
// задаем толщину линии
cairo_set_line_width (cr, 0.7);
// рисуем риски на оси X, линии сетки и текстовые метки
xa = 40;
while(xa 0)
{
// задаем черный цвет
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_move_to (cr, 33, ya);
cairo_line_to (cr, 47, ya);
cairo_stroke (cr);
// преобразовывем значение координаты риски в строку
sprintf(buf, "%i", 340-ya);
// рисуем текст метки в нужной точке
cairo_move_to (cr, 12, ya+2);
cairo_show_text(cr, buf);
// задаем зеленый цвет
cairo_set_source_rgb (cr, 0, 0.5, 0);
// рисуем линии сетки
cairo_move_to (cr, 47, ya-40);
cairo_line_to (cr, 600, ya-40);
cairo_stroke (cr);
ya -= 40;
}
// рисуем синусоиду
// задаем красный цвет и толщину линии
cairo_set_source_rgb (cr, 1, 0, 0);
cairo_set_line_width (cr, 4);
// устанавливаем начальную точку синусоиды
cairo_move_to(cr, t+40, ((uAmplit * sin(t * radianPerx + count)) * 0.1 + 160));
// рисуем синусоиду
while(t < 600)
{
cairo_line_to(cr, t+40, ((uAmplit * sin(t * radianPerx + count)) * 0.1 + 160));
t++;
}
cairo_stroke (cr);
}
Компиляция исходного текста в файле выполняется командой:
gcc -Wall -g osc.c -o osc `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0
Здесь происходит подключение всех необходимых библиотек, с помощью pkg-config (у вас должен быть установлена эта утилита). Если компилятор будет ругаться, значит у вас не установлены необходимые пакеты с заголовочными файлами.
Скриншот итогового приложения (см. рисунок):
Рис. 1. Скриншот окна кроссплатформенного осциллографа
Заключение
Все упомянутые в статье исходные коды приведены в виде ресурсов непосредственно в архиве с журналом.
Ресурсы
- Кроссплатформенная библиотека элементов интерфейса GTK+ http://www.gtk.org/download.html
- Кроссплатформенный компилятор PureBasic http://pure-basic.narod.ru
Похожие статьи
Купить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)