Glade & Gtk. В последнее время операционная система Linux, становится все более популярной среди пользователей. Для нее написано уже достаточно много программ, улучшен графический интерфейс, конечно, сложность в конфигурирование еще существует, но это не значительное неудобство для пользователя, особенно для опытного. Ядро системы обновляется несколько раз в год, приложения пишутся сообществами пользователей, благодаря этому Linux очень быстро развивается, что ж и мы не будем стоять в сторонке, а попробуем научиться писать полноценные программы, для этой цели создано множество инструментов, сегодня, мы обойдемся Glade. Библиотекой GTK, компилятором gcc и текстовым редактором, хотя вместо него я советую использовать Geany, во-первых, он выдает подсказки, во-вторых, основываясь на отчетах компилятора, подчеркивает ошибки, и строки для которых есть предупреждения, что очень удобно. Писать будем на языке СИ. Если у вас нет выше перечисленных программ, то скачайте и установите (можно воспользоваться менеджером пакетов). Итак, сначала начнем разработку интерфейса программы, для этого откроем Glade. Нам сразу же предлагают задать параметра проекта: Формат файла проекта – GtkBuilder. Требуемая версия библиотек – здесь можно выбрать из нескольких версий библиотек, но я рекомендую использовать самую последнюю. Теперь нужно создать главное окно программы, для этого слева на панели выбираем виджет «окно», но с добавлением других компонентов не спешите. Если вы программировали в среде Delphi или Visual Basic, то знаете что компоненты там можно бросать на форму и произвольно перетаскивать с помощью мыши, здесь дело обстоит несколько иначе. Для расположения компонентов используются так называемые контейнеры, существуют разные типы контейнеров, но все они похожи. Добавим на форму компонент «вертикальный контейнер», появится окно, в котором нужно выбрать «число элементов» выбираем три. Форма разделилась на три части, добавим в самую верхнюю контейнер «строка меню». В среднюю виджет «область текста», и в оставшуюся виджет «строка состояния». У нас получилось форма напоминающая «текстовый редактор», да сегодня мы с вами напишем простой блокнот, почти такой же, как под Windows. Выделим главное окно и впишем в свойство «заголовок окна» - «Блокнот». Также посмотрите другие свойства, поэкспериментируйте с ними. В контейнере «строка меню», удалите заголовок вид, для этого просто выделите его и нажмите клавишу «delete». Для виджета «область текста», создадим текстовый буфер, для этого в свойствах найдем «буфер», после создания назначим ему имя «buffer». А также нам нужен диалог «о программе», выбираем его на панели виджетов среди окон и добавляем в проект. Редактируем нужные вам свойства, там все интуитивно понятно. Сохраняем как GuiNotepad.xml, расширение обязательно должно быть xml, а не glade. Вот что получилось у меня. Пришло время взглянуть на нашу программу в откомпилированном виде. Создаем текстовый файл с расширением C, в том же каталоге что и файл Glade. И пишем туда следующий код: Листинг: код программы. PHP: #include <gtk/gtk.h> // Подключаем gtk /*Виджеты программы*/ GtkTextBuffer *buffer; // Текстовый буфер GtkBuilder *builder; // GtkBuilder объект GtkWidget *textview; // Область текста GtkWidget *mainwindow; // Главное окно GtkWidget *aboutdialog; // Диалог "о программе" GtkWidget *menubar; // Строка меню GtkClipboard *cb; // Буфер обмена int main (int argc, char **argv) { gtk_init(&argc, &argv); // инициализируем gtk builder = gtk_builder_new (); // создаем новый GtkBuilder объект /*загружаем описание интерфейса из XML файла*/ gtk_builder_add_from_file(builder, "GuiNotepad.xml", NULL); /* связываем наше окно с окном из файла */ mainwindow = GTK_WIDGET(gtk_builder_get_object(builder, "window1")); /*Остальные виджеты тоже связываем*/ aboutdialog = GTK_WIDGET(gtk_builder_get_object(builder, "aboutdialog1")); textview = GTK_WIDGET(gtk_builder_get_object(builder, "textview1")); menubar = GTK_WIDGET(gtk_builder_get_object(builder, "menubar1")); buffer = GTK_TEXT_BUFFER(gtk_builder_get_object(builder, "buffer")); /*Включаем обработку сигналов*/ gtk_builder_connect_signals (builder, NULL); gtk_widget_show_all (mainwindow); //Показываем форму и виджеты на ней gtk_main(); // запускаем главный цикл приложения return 0; } /*Обработчики событий*/ /*событие назначено по умолчанию*/ void on_mainwindow_destroy (GtkObject *object, gpointer user_data) { //События для кнопки «закрыть» gtk_main_quit (); } Листинг: код программы. Теперь взглянем на плоды наших трудов, для этого откомпилируем код и запустим приложение. Если вы меня послушались и вместо текстового редактора используете Geany, то прежде чем компилировать зайдите в меню «сборка|параметры сборки» и изменить поле «собрать» на следующее: Все это конечно очень замечательно, но толку от такого приложения? Надо его доделать, чтобы кнопки реагировали на определенные события и выполняли определенные действия. «Оживим» наши кнопки, заставим их работать. Возвращаемся в Glade, и назначаем кнопкам события, начнем с кнопки «создать», выбираем ее и в свойствах переходим на вкладку сигналы. В GtkMenuItem, находим сигнал activate, в обработчике выбираем on_imagemenuitem1_activate, в «После» ставим галочку. А в наш текстовый файл, в котором хранится код, дописываем (в самый конец): PHP: void on_imagemenuitem1_activate (GtkObject *object, gpointer user_data) { //Создать /*Название окна – «Блокнот»*/ gtk_window_set_title((GtkWindow *)mainwindow, (gchar*)"Блокнот"); gtk_text_buffer_set_text (buffer, "\0", -1); //Очищаем текстовый буфер } Функция gtk_window_set_title устанавливает заголовок окна, для этого ей нужно передать указатель типа GtkWindow на само окно, и указатель типа gchar на строку, которая станет его заголовком. При передаче параметров функции мы преобразовываем типы (GtkWindow *) и (gchar*), этого делать не обязательно и без преобразования все прекрасно работает, но мы ведь с вами приличные программисты, потому будем соблюдать правила хорошего тона. Функция gtk_text_buffer_set_text, присваивает «текстовому буферу» определенный текст, для этого ей нужно передать указатель на буфер, на сам текст в данном случае мы передаем "\0" – ноль байт, что эквивалентно очистке текстового поля, и последним параметром служит длина передаваемой строки, ставьте -1 для не определенной длины. Следующая кнопка – «Открыть», точно так же, как и для кнопки «создать» назначаем ей обработчик события, и дописываем такой код: Листинг: обработчик для кнопки «Открыть» PHP: void on_imagemenuitem2_activate (GtkObject *object, gpointer user_data) { //Открыть файл gchar *result; //Путь к открываемому файлу GtkWidget *filedlg; //Новый виджет, «диалог выбора файлов» /*Создаем «диалог открытия файлов»*/ filedlg = gtk_file_chooser_dialog_new("Выбор файла", (GtkWindow*)mainwindow, GTK_FILE_CHOOSER_ACTION_OPEN,GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); /*Если нажата клавиша «Открыть», то… */ if (gtk_dialog_run (GTK_DIALOG(filedlg)) == GTK_RESPONSE_ACCEPT) {//Сохраняем путь к файлу в переменную result = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (filedlg)); gtk_widget_destroy(filedlg); //Уничтожаем виджет диалог gtk_window_set_title((GtkWindow *)mainwindow, result); //Меняем название окна /*Вызываем функцию открытия файла*/ OpenFile(result); } else gtk_widget_destroy(filedlg); //Уничтожаем окно } Листинг: обработчик для кнопки «Открыть» Весь предыдущий код, нужен лишь для того чтобы вызвать и создать окно выбора файлов, которое реализовано объектом GtkFileChooserDialog и получить путь до файла, я специально не описывал этот виджет в glade, потому, что такая реализация более удачная, виджет создается на «лету». Функция gtk_file_chooser_dialog_new – создает новый диалог, в качестве первого параметра ей передается заголовок окна, второй параметр указывает на родительский объект, в нашем случае это mainwindow. Третий параметр определяет, какое окно создавать (для открытия, или для сохранения), остальные параметры отвечают за кнопки, которые будет расположены на окне. Функция gtk_dialog_run, делает окно видимым и реагирует на нажатие кнопок, как параметр ей нужно передать ранее созданный диалог типа GtkDialog, функция GTK_DIALOG нужна для преобразования типа, кстати говоря, мы могли использовать и стандартное C-ишное преобразование (GtkDialog*). Если пользователь нажмет кнопку «открыть» функция gtk_dialog_run вернет значение GTK_RESPONSE_ACCEPT. Тогда мы должны определить имя и путь к выбранному файлу, для этого и существует функция gtk_file_chooser_get_filename, она возвращает значение типа gchar в котором содержаться нужные нам данные, а как параметр ей передается созданный ранее диалог, причем мы должны преобразовать его тип в GtkFileChooser. Далее мы уничтожаем диалог за его ненадобностью. Выше приведенный код самостоятельно не открывает файл, функцию OpenFile, мы должны написать сами именно ей мы и будем передавать путь к файлу: Листинг: Наша процедура, которая открывает файл PHP: void OpenFile(char *filename) { GtkTextIter iter; FILE *f; gint i=0; gchar line[256]; f=fopen(filename,"r"); //открываем файл while(!feof(f)) { fgets(line,256,f); gtk_text_buffer_get_iter_at_line(buffer,&iter,i); //итер на строку с номером i gtk_text_buffer_insert(buffer, &iter, line, -1); //вставляем строку в позицию итера i++; //увеличиваем счетчик строк } fclose(f); } Листинг: Наша процедура, которая открывает файл Эту функцию нужно глобально объявить void OpenFile(char *filename); и добавить дополнительные библиотеки, для ее корректной работы: stdio.h, stdlib.h, string.h. После того как fgets копирует строку в переменную line. Мы вызываем функцию gtk_text_buffer_get_iter_at_line, она устанавливает позицию для вставки текста на строку с номером i (при первом проходе цикла, это 0). А первые два параметра это, наше текстовое поле (буфер) и итер (так называются адреса в буфере, в данном случае мы будем устанавливать адрес на i-тую строку). Затем мы непосредственно вставляем текст функцией gtk_text_buffer_insert, первый параметр которой, мы уже знаем, второй- это адрес (итер) начиная с которого мы будем вставлять текст (его мы установили предыдущей функцией), третий - строка для вставки, последний длина строки (-1 для не определенной длины). Код для кнопки «сохранить как» очень похож на код кнопки «открыть» потому мы пока перепрыгнем кнопку «сохранить», заранее объявим и создадим функцию SaveFile, void SaveFile(char *filename). Листинг: Наша процедура, сохранение файла PHP: void SaveFile(char *filename) { FILE *f; gchar *save_text; GtkTextIter start,end; gtk_text_buffer_get_start_iter(buffer, &start); //начальный итер gtk_text_buffer_get_end_iter(buffer, &end); //конечный итер save_text=gtk_text_buffer_get_text (buffer,&start,&end, FALSE); //Берем текст из буфера f=fopen(filename,"w"); //открываем файл fprintf(f,"%s",save_text); fclose(f); } Листинг: Наша процедура, сохранение файла Функция gtk_text_buffer_get_start_iter устанавливает итер в начало буфера, а функция gtk_text_buffer_get_end_iter в конец, для этого в обоих случаях функциям нужно передать указатель на буфер и указатель на заранее объявленные итеры. Функция gtk_text_buffer_get_text возвращает текст из буфера, при этом ей передается указатель на буфер, на итеры между которыми расположен забираемый текст и переменная или значение булева типа (TRUE, FALSE), оно говорит о том отображать ли в буфере скрытые символы или нет. Процедура для сохранения в файл готова, теперь напишем, обработчик для кнопки «сохранить как», вызывающий эту самую функцию, он практически идентичен обработчику для кнопки «открыть», потому я не буду на нем подробно останавливаться. Листинг: обработчик для кнопки «Сохранить как» PHP: void on_imagemenuitem4_activate(GtkObject *object, gpointer user_data) { //Сохранить как... gchar *result; //Путь к открываемому файлу GtkWidget *filedlg; //Новый виджет, «диалог выбора файлов» /*Создаем «диалог сохранения файлов»*/ filedlg = gtk_file_chooser_dialog_new("Сохранить как...", (GtkWindow*)mainwindow, GTK_FILE_CHOOSER_ACTION_SAVE,GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); /*Если нажата клавиша «Сохранить как», то… */ if (gtk_dialog_run (GTK_DIALOG(filedlg)) == GTK_RESPONSE_ACCEPT) {{//Сохраняем путь к файлу в переменную result = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filedlg)); gtk_widget_destroy(filedlg); //Уничтожаем окно gtk_window_set_title((GtkWindow *)mainwindow, result); //Меняем название окна /*Вызываем функцию открытия файла*/ SaveFile(result); } else gtk_widget_destroy(filedlg); //Уничтожаем окно } Листинг: обработчик для кнопки «Сохранить как» Теперь вернемся к кнопке «сохранить», ее обработчик очень прост. PHP: void on_imagemenuitem3_activate(GtkObject *object, gpointer user_data) { //Сохранить if (strcmp(gtk_window_get_title((GtkWindow *)mainwindow),(gchar*)"Блокнот")) //Просто вызываем процедуру сохранения SaveFile((gchar *)gtk_window_get_title((GtkWindow *)mainwindow)); Else //Вызываем процедуру «Сохранить как» on_imagemenuitem4_activate((GtkObject*)mainwindow,""); } Функция strcmp, сравнивает две строки, если строки равны, возвращает ноль. В примере заголовок окна сравнивается со словом «Блокнот», и если заголовок равен этому слову, то вызывается процедура кнопки «сохранить как» (так мы определяем, что файл еще ни разу не сохраняли и не открывали с диска). Если заголовок не равен слову «Блокнот», тогда мы берем заголовок окна (в нем содержится путь к сохраненному или открытому ранее файлу см. код выше) и отправляем функции SaveFile. Функция gtk_window_get_title(); возвращает название текущего окна. Остальное я считаю итак понятно. Код для кнопки выход, очень просто и вызывает всего одну функцию. PHP: void on_imagemenuitem5_activate (GtkObject object, gpointer user_data) { // Закрываем программу gtk_main_quit (); } ]Пора заняться вторым меню. Начнем по порядку, с кнопки «Вырезать»: PHP: void on_imagemenuitem6_activate(GtkObject *object, gpointer user_data) { //Вырезать gchar *atom = gdk_atom_name((GdkAtom)"CLIPBOARD"); Clipboard = gtk_clipboard_get((GdkAtom)atom); g_free(atom); gtk_text_buffer_cut_clipboard(buffer, Clipboard , TRUE); } Мы получаем указатель на строку с именем буфера обмена с помощью функции gdk_atom_name. Затем вызываем gtk_clipboard_get, и передаем этой функции указатель на полученную ранее строку, буфера обмена. Функция gtk_text_buffer_cut_clipboard предназначена, для операции «вырезать», первым параметром ей передается «буфер», вторым буфер обмена, а третьим значение типа boolean, которое отвечает за редактируемость буфера. PHP: void on_imagemenuitem7_activate (GtkObject *object, gpointer user_data) { //Копировать gchar * atom = gdk_atom_name((GdkAtom)"CLIPBOARD"); Clipboard = gtk_clipboard_get((GdkAtom)atom); g_free(atom); gtk_text_buffer_copy_clipboard(buffer, Clipboard ); } gtk_text_buffer_copy_clipboard позволяет скопировать текст в буфер обмена. PHP: void on_imagemenuitem8_activate(GtkObject *object, gpointer user_data) { //Вставить gchar * atom = gdk_atom_name((GdkAtom)"CLIPBOARD"); Clipboard = gtk_clipboard_get((GdkAtom)atom); g_free(atom); gtk_text_buffer_paste_clipboard(buffer, Clipboard , NULL, TRUE); } Функция gtk_text_buffer_paste_clipboard вставляет содержимое буфера обмена в точку вставки, третий параметр отвечает за точку вставки, ставьте NULL, если хотите, чтобы текст был вставлен в позицию курсора. И последний параметр за редактируемость буфера по умолчанию. PHP: void on_imagemenuitem9_activate(GtkObject *object, gpointer user_data) { //Удалить gtk_text_buffer_delete_selection (buffer, TRUE, TRUE); } Ну, и как можно догадаться функция gtk_text_buffer_delete_selection, удаляет выделенный текст из буфера, в качестве первого параметра она получает сам буфер, в качестве второго значения булева типа, которое говорит, вызвано ли удаление пользователем. Последний параметр говорит о том можно ли редактировать буфер по умолчанию, если нельзя, то пользователь не сможет удалить текст. Также функция возвращает значение true или false, по нему можно определить, было ли выделенное для удаления поле пустым. Осталось тока отобразить окно диалога «о программе». Это проще чем переслать два байта: PHP: void on_imagemenuitem10_activate (GtkObject *object, gpointer user_data) { //О программе gtk_widget_show (aboutdialog); g_signal_connect_swapped (aboutdialog,"response", G_CALLBACK (gtk_widget_destroy), aboutdialog); } Функция gtk_widget_show, предназначена для отображения переданного ей виджета, именно она показывает на экране наш диалог. Но нужно еще и обрабатывать сигналы, поступающие от кнопок, функция g_signal_connect_swapped призвана помочь нам. Первым параметром она получает объект, от которого исходит сигнал. Вторым описание сигнала, третьим мы связываем сигнал с фактическим действием, в данном случае уничтожаем объект. Четвертым параметром идет объект, на который мы влияем, то есть в данном случае объект, который мы уничтожим. Как вы могли догадаться, это обработка от кнопки закрыть на виджете. Осталось только откомпилировать наш проект и, запустив, наконец, порадоваться проделанной работе. Библиотека Gtk очень мощный зверь (но не единственный), для изучения которого нужна не одна неделя и сегодняшний обзор лишь маленькая толика, не способная полностью открыть перед нами завесу, за которой возможно скрывается истинна. Множество приложений написанных для Linux используют эту библиотеку. Надеюсь, все усвоили, принципы работы с gtk. Главное понять, какие функции и в каком порядке нужно использовать, здесь буквально все программирование строиться на использование функций gtk. Потому совместно с ней, можно использовать множество языков программирования, а не только C, так что дерзайте, пробуйте, экспериментируйте. 30.01.10 ©Kerny
Последняя версия рассматриваемой в статье программы, с исходниками разумеется: Автор: Kerny ОС: Linux, Windows Зависимости: gtk 2.0 Название: Маленький блокнот для Linux Сайт разработчика: http://kerny-auroras.blogspot.com Описание: В ОС Linux существует множество текстовых редакторов, но все они многофункциональные, тяжеловесные, иногда нужно сохранить текст, на время, что бы, например, потом вставить в строку поиска и т.п. Этот редактор практически аналогичен Блокноту в системе Windows и как раз подходит для такой задачи, хочу сказать что код полностью кроссплатформенный, его можно откомпилировать практически под любую систему. Файлы: GuiNotepad.xml - описание графического интерфейса программы Notepad.c - исходный код программы Notepad - откомпилированная версия программы readme.txt - этот файл Установка: Если файл Notepad не запускается, то нужно заново откомпилировать программы под вашу систему, делается это так: "gcc notepad.c -o notepad -export-dynamic `pkg-config --cflags --libs gtk+-2.0`" Для правильной компиляции нужна библиотека gtk 2.0. Лог: Версия 1.2 +Исправлен недочет с открытием файлов, характерный лишь для версии 1.1 +Исправлен недочет с дублированием последней строки Версия 1.1 + Исправленны критические ошибки, в частности "переполнение буфера" при чтение из файла + Исправленн ряд мелких недочетов, связанный с преобразованием типов + Полность переписан код для открытия файла Скачать