В первой части мы учились ловить события и создавать виджеты. В этой части мы посмотрим, как создать меню и создадим небольшой текстовый редактор.
Приступим. Для создания меню нам в большинстве случаем понадобятся следующие функции:
GtkWidget *gtk_menu_bar_new( void );
GtkWidget *gtk_menu_new( void );
GtkWidget *gtk_menu_item_new( void );
GtkWidget *gtk_menu_item_new_with_label( const char *label );
GtkWidget *gtk_menu_item_new_with_mnemnonic( const char *label );
void gtk_menu_item_set_submenu(
GtkMenuItem *menu_item,
GtkWidget *submenu );
void gtk_menu_bar_append(
GtkMenuBar *menu_bar,
GtkWidget *menu_item );
void gtk_menu_item_right_justify( GtkMenuItem *menu_item );
Расскажу о них по порядку.
GtkWidget *gtk_menu_bar_new( void );
Создает меню бар куда собственно и крепится наше меню.
GtkWidget *gtk_menu_new( void );
Создает меню в которое инкапсулируются остальные элементы меню.
GtkWidget *gtk_menu_item_new( void );
GtkWidget *gtk_menu_item_new_with_label( const char *label );
GtkWidget *gtk_menu_item_new_with_mnemnonic( const char *label );
Создают элементы меню. С первой функцией всё понятно просто элемент без видимой метки. Вторая с видимой меткой, а треть я подчеркиванием мнемоники.
Пока ничего сложного и интересного.
void gtk_menu_item_set_submenu(
GtkMenuItem *menu_item,
GtkWidget *submenu );
Привязываем к элементу меню подменю.
void gtk_menu_bar_append(
GtkMenuBar *menu_bar,
GtkWidget *menu_item );
Далее крепим элемент меню к меню бару.
void gtk_menu_item_right_justify( GtkMenuItem *menu_item );
Если очень хочется можно элемент меню выровнять в меню баре по правому краю.
При нажатии на элемент меню вызывается событие
activate .
Для создания сложных меню с минимальным вызовом функций существует фабрика элементов, но об этом в другой раз.
Пример кода из нашей программы:
/* Меню бар. */
menu_bar = gtk_menu_bar_new ();
/* Пакуем меню бар в коробку. И показываем его всем. */
gtk_box_pack_start (GTK_BOX (ver_box), menu_bar, FALSE, TRUE, 0);
gtk_widget_show (menu_bar);
/* Собственно меню "Файл". */
file_menu = gtk_menu_new();
/* Создаем элементы меню "Файл". */
open_item = gtk_menu_item_new_with_label ("Open");
save_item = gtk_menu_item_new_with_label ("Save");
save_as_item = gtk_menu_item_new_with_label ("Save as");
quit_item = gtk_menu_item_new_with_label ("Quit");
/* Пакуем элементы меню "Файл" в меню "Файл". */
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), open_item);
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), save_item);
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), save_as_item);
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), quit_item);
/* Подключаем к ним события. */
g_signal_connect (G_OBJECT (open_item), "activate",
G_CALLBACK (open_event), NULL);
g_signal_connect (G_OBJECT (save_item), "activate",
G_CALLBACK (save_event), NULL);
g_signal_connect (G_OBJECT (save_as_item), "activate",
G_CALLBACK (save_event),
(gpointer) "as");
g_signal_connect_swapped (G_OBJECT (quit_item), "activate",
G_CALLBACK (gtk_main_quit),
G_OBJECT(window));
/* Показываем элементы меню "Файл". */
gtk_widget_show(open_item);
gtk_widget_show(save_item);
gtk_widget_show(save_as_item);
gtk_widget_show(quit_item);
/* Создаем верхний элемент меню "Файл". */
file_item = gtk_menu_item_new_with_label ("File");
gtk_widget_show (file_item);
/* Подключаем к верхниму элементу меню. А его в меню бару. */
gtk_menu_item_set_submenu (GTK_MENU_ITEM (file_item), file_menu);
gtk_menu_bar_append (GTK_MENU_BAR(menu_bar), file_item);
Так же в программе мы использовали диалог открытия/создания(выбора) файла.
(КАРТИНКА)GtkWidget *gtk_file_selection_new( const gchar *title );
Создает диалог выбора файла с заголовком
title.
void gtk_file_selection_set_filename(
GtkFileSelection *filesel,
const gchar *filename );
Устанавливаем название файла в диалоге например при нажатии "Сохранить как".
gchar *gtk_file_selection_get_filename( GtkFileSelection *filesel );
Берем из диалога имя файла.
Кроме того диалог содержит следующие элементы к которым тоже можно обращатся:
dir_list
file_list
selection_entry
selection_text
main_vbox
ok_button
cancel_button
help_button
Пример использования из программы:
GtkWidget *file_open;
file_open = gtk_file_selection_new ("Save file as...");
/* Подключаем обработчик нажатия кнопки "Ok" в диалоге. */
g_signal_connect (
G_OBJECT(GTK_FILE_SELECTION(file_open)->ok_button),
"clicked", G_CALLBACK(save_ok_button_event),
NULL);
g_signal_connect_swapped (
G_OBJECT (GTK_FILE_SELECTION(file_open)->ok_button),
"clicked", G_CALLBACK(gtk_widget_destroy),
G_OBJECT (file_open));
g_signal_connect_swapped (
G_OBJECT (GTK_FILE_SELECTION(file_open)->cancel_button),
"clicked", G_CALLBACK(gtk_widget_destroy),
G_OBJECT (file_open));
gtk_widget_show (file_open);
Ну и самое недокументированное это
text_view. Это некоторый аналог RichEdit из Windows только возможностей у
text_view гораздо больше. Опишу только, то, что сам делал. Вот небольшой кусок кода использованного при создании
text_view:
/* Создаем окно с прокруткой. */
scroll_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (
GTK_SCROLLED_WINDOW (scroll_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
/* Создаем окно редактирования. */
text_view = gtk_text_view_new ();
/* Вытаскием из редактора буфер. */
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
/* Создаем тэг для текста. Моноширный, жирный и крупный. */
gtk_text_buffer_create_tag (
buffer, "monospace_big_bold",
"family", "monospace",
"size", 20 * PANGO_SCALE,
"weight", PANGO_WEIGHT_BOLD,
NULL);
/* Засовываем текстовое окно в окно с прокруткой. */
gtk_container_add (GTK_CONTAINER (scroll_window), text_view);
gtk_widget_show (text_view);
Что проиходит в этом коде? Думаю здесь понятно:
scroll_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (
GTK_SCROLLED_WINDOW (scroll_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
Создаем окно с прокруткой и устанавливаем ему политику для ползунков, а именно отображаться по необходимости. Далее создаем
text_view и грабим с него буфер. А зачем? А затем, что мы работаем с текстовым буфером, а не с окном . К тому же его можно подсунуть другому
text_view в этом случае они будут отображать состояние одного буфера. Делается вот так:
text_view2 = gtk_text_view_new (buffer);
Функция
gtk_text_buffer_create_tag создает тэг для текста. Т.е. стиль отображения текста. Далее запихиваем
text_view в окно с прокруткой.
С этим надеюсь понятно возникает вопрос, а как нам текст из файла запихать в редактор. Делаем это так
gtk_text_buffer_insert_with_tags_by_name (
buffer,&iter,cbuffer,rval,
"monospace_big_bold", NULL);
или так:
gtk_text_buffer_insert (buffer, &iter, cbuffer, rval);
Параметры:
- buffer - буфер текста;
- iter - итератор определяет позицию в тексте;
- cbuffer - текст gchar *;
- rval - количество символов для вставки, если -1, то вся строка. Должна заканчиваться нулём;
- "monospace_big_bold" - название тега;
- NULL - говорит, что тегов больше не будет.
В случае
gtk_text_buffer_insert - используется тэг установленный для данной позиции.
Теперь возьмем текст из text_view:
gtk_text_buffer_get_bounds (buffer, &start, &end);
cbuffer = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
Первой строкой берем начало и конец буфера. Второй сохраняем текст буфера в
gchar *. TRUE означает, что грабит скрытые символы.
И напоследок. Весь текст в
text_view в UTF-8 вот так. Так, что перед записью не английских символов в буфер убедитесь, что они в UTF-8. Вот что получилось.
(КАРТИНКА)
Листинг CoolEdit.c:
#include <gtk/gtk.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
GtkWidget *window; // Собственно окно.
GtkWidget *scroll_window; // Другое окно с прокруткой для редактора.
GtkWidget *ver_box; // Вертикальная коробка.
GtkWidget *text_view; // Наш виджет для текста.
GtkWidget *file_open; // Диалог открытия/сохранения файла.
GtkWidget *menu_bar; // И так ясно.
GtkWidget *file_menu; // Отсюда элементы меню.
GtkWidget *file_item;
GtkWidget *open_item;
GtkWidget *save_item;
GtkWidget *save_as_item;
GtkWidget *quit_item;
GtkWidget *help_menu;
GtkWidget *help_top_item;
GtkWidget *help_item;
GtkWidget *about_item; // Всё элементы меню кончились.
GtkTextBuffer *buffer; // Текстовый буфер для редактора текста
// его можно подсунуть еще кому нибудь
// и редактировать один буфер в двух и более окнах
// одновременно.
gchar *file_name = NULL; // Храним имя файла мало ли понадобится.
int workfile = -1; // Дескриптор открытого файла.
// Закрывает главное окно в принципе
void destroy(GtkWidget *widget, gpointer data)
{ // не нужна.
gtk_main_quit();
}
// Пишем всякую чушь по событию
void hello(GtkWidget *widget, gpointer data)
{
g_print ("%s was pressedn", (char *) data);
}
// Обратываем попытку
// Закрытия окна нажатием "крестика" в заголовке
gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data)
{
g_print ("delete event occurredn");
return FALSE;
}
/* Обрабатываем нажатие "Ok" в диалоге выбора файла для открытия. */
gint open_ok_button_event(
GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
int rval;
void *cbuffer = calloc(512,sizeof(char));
GtkTextIter iter; // Итератор текстового буфера
pid_t child_pid;
if (file_name != NULL) g_free(file_name);
/* Грабим имя файла из диалога. */
file_name = gtk_file_selection_get_filename (
GTK_FILE_SELECTION (file_open));
/* Сообщаем чего награбили. */
g_print ("Selected file %sn", file_name);
/* Открываем файл. */
workfile = open (file_name, O_RDWR);
if(workfile == -1) {
g_print ("Error open %sn", file_name);
return FALSE;
}
/* Устанавливаем итератор на начало текста. */
gtk_text_buffer_get_start_iter (buffer, &iter);
/* Читаем из файла пишем в буфер. */
while ((rval = read(workfile, cbuffer, 512)) != 0)
{
gtk_text_buffer_insert_with_tags_by_name (
buffer, &iter, cbuffer, rval,
"monospace_big_bold",
NULL);
}
free (cbuffer);
return FALSE;
}
/* Обрабатываем нажатие кнопки "Открыть" меню. */
gint open_event( GtkWidget *widget, GdkEvent *event, gpointer data)
{
g_print ("Open file widget call.n");
/* Открываем диалог. */
file_open = gtk_file_selection_new ("Open file...");
/* Подключаем обработчик нажатия кнопки "Ok" в диалоге. */
g_signal_connect (
G_OBJECT(GTK_FILE_SELECTION(file_open)->ok_button),
"clicked", G_CALLBACK(open_ok_button_event),
NULL);
g_signal_connect_swapped (
G_OBJECT (GTK_FILE_SELECTION(file_open)->ok_button),
"clicked", G_CALLBACK(gtk_widget_destroy),
G_OBJECT (file_open));
g_signal_connect_swapped (
G_OBJECT (GTK_FILE_SELECTION(file_open)->cancel_button),
"clicked", G_CALLBACK(gtk_widget_destroy),
G_OBJECT (file_open));
gtk_widget_show (file_open);
return FALSE;
}
/* Обрабатываем нажатие кнопки "Ok" в диалоге "Сохранить как". */
gint save_ok_button_event(
GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
gchar *cbuffer;
GtkTextIter start, end;
if (file_name != NULL) g_free(file_name);
/* Грабим имя файла. */
file_name = gtk_file_selection_get_filename(
GTK_FILE_SELECTION (file_open));
/* Выводим чего награбили. */
g_print ("Selected file %sn", file_name);
/* Создаем файлик. */
workfile = open (file_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if(workfile == -1) {
g_print ("Error open %sn", file_name);
return FALSE;
}
/* Определяем начало и конец текста. Копируем текст в буффер. */
gtk_text_buffer_get_bounds (buffer, &start, &end);
cbuffer = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
/* Курсор на начало файла. Отрезаем лишнее. Сохраняем. */
lseek(workfile,0,0);
truncate(workfile, 0);
write(workfile, cbuffer, strlen(cbuffer));
/* Сколько на сохраняли. */
g_print("Writed %d bytes.n", strlen(cbuffer));
g_free (cbuffer);
return FALSE;
}
/* Обрабатываем нажатие "Сохранить" и "Сохранить как". */
gint save_event( GtkWidget *widget, GdkEvent *event, gpointer data)
{
gchar *cbuffer;
GtkTextIter start, end;
if ((workfile == -1) || ((char *) data)=="as") {
/* Открываем диалог. */
file_open = gtk_file_selection_new ("Save file as...");
/* Подключаем обработчик нажатия кнопки "Ok" в диалоге. */
g_signal_connect (
G_OBJECT(GTK_FILE_SELECTION(file_open)->ok_button),
"clicked", G_CALLBACK(save_ok_button_event),
NULL);
g_signal_connect_swapped (
G_OBJECT (GTK_FILE_SELECTION(file_open)->ok_button),
"clicked", G_CALLBACK(gtk_widget_destroy),
G_OBJECT (file_open));
g_signal_connect_swapped (
G_OBJECT (GTK_FILE_SELECTION(file_open)->cancel_button),
"clicked", G_CALLBACK(gtk_widget_destroy),
G_OBJECT (file_open));
gtk_widget_show (file_open);
return FALSE;
}
/* Определяем начало и конец текста. Копируем текст в буфер. */
gtk_text_buffer_get_bounds (buffer, &start, &end);
cbuffer = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
/* Курсор на начало файла. Отрезаем лишнее. Сохраняем. */
lseek(workfile,0,0);
truncate(workfile, 0);
write(workfile, cbuffer,strlen(cbuffer));
/* Сколько на сохраняли. */
g_print("Writed %d bytes.n", strlen(cbuffer));
g_free (cbuffer);
return FALSE;
}
/* Создаем окно и основные виджеты. */
int make_main_window()
{
/* Создаем окно. Устанавливаем размер. */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window),400,400);
/* Устанавливаем заголовок окна. И бордюр. */
gtk_window_set_title (GTK_WINDOW (window),
"Out Cool Text Editor");
gtk_container_set_border_width (GTK_CONTAINER (window), 5);
/* Подключаем сигналы. */
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK (delete_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK (destroy), NULL);
/* Вертикальная коробка. */
ver_box = gtk_vbox_new(FALSE, 0);
/* Меню бар. */
menu_bar = gtk_menu_bar_new ();
/* Пакуем меню бар в коробку. И показываем его всем. */
gtk_box_pack_start (GTK_BOX (ver_box), menu_bar, FALSE, TRUE, 0);
gtk_widget_show (menu_bar);
/* Собственно меню "Файл". */
file_menu = gtk_menu_new();
/* Создаем элементы меню "Файл". */
open_item = gtk_menu_item_new_with_label ("Open");
save_item = gtk_menu_item_new_with_label ("Save");
save_as_item = gtk_menu_item_new_with_label ("Save as");
quit_item = gtk_menu_item_new_with_label ("Quit");
/* Пакуем элементы меню "Файл" в меню "Файл". */
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), open_item);
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), save_item);
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), save_as_item);
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), quit_item);
/* Подключаем к ним события. */
g_signal_connect (G_OBJECT (open_item), "activate",
G_CALLBACK (open_event), NULL);
g_signal_connect (G_OBJECT (save_item), "activate",
G_CALLBACK (save_event), NULL);
g_signal_connect (G_OBJECT (save_as_item), "activate",
G_CALLBACK (save_event), (gpointer) "as");
g_signal_connect_swapped (G_OBJECT (quit_item), "activate",
G_CALLBACK (gtk_main_quit), G_OBJECT(window));
/* Показываем элементы меню "Файл". */
gtk_widget_show(open_item);
gtk_widget_show(save_item);
gtk_widget_show(save_as_item);
gtk_widget_show(quit_item);
/* Создаем верхний элемент меню "Файл". */
file_item = gtk_menu_item_new_with_label ("File");
gtk_widget_show (file_item);
/* Подключаем к верхниму элементу меню. А его в меню бару. */
gtk_menu_item_set_submenu (GTK_MENU_ITEM (file_item), file_menu);
gtk_menu_bar_append (GTK_MENU_BAR(menu_bar), file_item);
/*Повторяем для меню "Помощь" теже действия,что и для меню "Файл"*/
help_menu = gtk_menu_new();
help_item = gtk_menu_item_new_with_label ("Help");
about_item = gtk_menu_item_new_with_label ("About");
gtk_menu_shell_append (GTK_MENU_SHELL (help_menu), help_item);
gtk_menu_shell_append (GTK_MENU_SHELL (help_menu), about_item);
g_signal_connect (G_OBJECT (help_item), "activate",
G_CALLBACK (hello), (gpointer) "Help Menu/Help");
g_signal_connect (G_OBJECT (about_item), "activate",
G_CALLBACK (hello), (gpointer) "Help Menu/About");
gtk_widget_show(help_item);
gtk_widget_show(about_item);
help_top_item = gtk_menu_item_new_with_label ("Help");
gtk_widget_show (help_top_item);
gtk_menu_item_set_submenu (
GTK_MENU_ITEM (help_top_item),
help_menu);
gtk_menu_bar_append (GTK_MENU_BAR(menu_bar), help_top_item);
/* Создаем окно с прокруткой. */
scroll_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (
GTK_SCROLLED_WINDOW (scroll_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
/* Создаем окно редактирования. */
text_view = gtk_text_view_new ();
/* Вытаскием из редактора буфер. */
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
/* Создаем тэг для текста. Моноширный, жирный и крупный. */
gtk_text_buffer_create_tag (buffer, "monospace_big_bold",
"family", "monospace",
"size", 20 * PANGO_SCALE,
"weight", PANGO_WEIGHT_BOLD,
NULL);
/* Засовываем текстовое окно в окно с прокруткой. */
gtk_container_add (GTK_CONTAINER (scroll_window), text_view);
gtk_widget_show (text_view);
/* Пакуем окно с прокруткой в коробку. */
gtk_box_pack_start (GTK_BOX (ver_box),scroll_window,TRUE,TRUE,0);
gtk_widget_show (scroll_window);
/* Коробку пакуем в главное окно. */
gtk_container_add(GTK_CONTAINER (window), ver_box);
/* Показываем, что наваяли. */
gtk_widget_show(ver_box);
gtk_widget_show(window);
return 0;
}
int main(int argc, char *argv[])
{
gtk_init (&argc, &argv);
make_main_window();
gtk_main();
return 0;
}
Для тех кто это дочитал до конца:описание APIтутор GTK+ 2среда разработки графических интерфейсов на GTK.Снимок Glade:
(КАРТИНКА)