Версия для печати

Архив документации на OpenNet.ru / Раздел "Программирование, языки" (Многостраничная версия)

Перевод руководства по GTK+ 2.0<

Tony Gale

Ian Main

& the GTK team

замечания по поводу перевода присылать sergeyvp@gmail.com


Оглавление
Доступность данного руководства (Tutorial Availability)
Введение (Introduction)
Начальные сведения (Getting Started)
Первая программа на GTK (Hello World in GTK)
Сборка первой программы (Compiling Hello World)
Теория сигналов и обратных вызовов (Theory of Signals and Callbacks)
События (Events)
Пошаговое рассмотрение "Hello World" (Stepping Through Hello World)
Подробнее (Moving On)
Типы данных (Data Types)
Подробнее об обработчиках сигналов (More on Signal Handlers)
Обнавлённый "Hello World" (An Upgraded Hello World)
Упаковка виджетов (Packing Widgets)
Теория упаковочных контейнеров (Theory of Packing Boxes)
Детально о контейнерах (Details of Boxes)
Демонстрационная программа упаковки (Packing Demonstration Program)
Упаковка с использованием таблиц (Packing Using Tables)
Пример табличной упаковки (Table Packing Example)
Краткий обзор виджетов (Widget Overview)
Преобразование (Casting)
Иерархия виджетов (Widget Hierarchy)
Виджеты без окон (Widgets Without Windows)
Кнопки (The Button Widget)
Обычные кнопки (Normal Buttons)
Переключатели (Toggle Buttons)
Кнопки контроля (Check Buttons)
Кнопки выбора (Radio Buttons)
Регуляторы (Adjustments)
Создание регуляторов (Creating an Adjustment)
Использование регуляторов (Using Adjustments the Easy Way)
Внутреннее устройство регуляторов (Adjustment Internals)
Виджеты регулировки диапазона (Range Widgets)
Полоса прокрутки (Scrollbar Widgets)
Виджет масштабирования (Scale Widgets)
Создание виджетов масштабирования (Creating a Scale Widget)
Функции и Сигналы (Functions and Signals (well, functions, at least))
Основные функции диапазона (Common Range Functions)
Установка "политики обнавления" (Setting the Update Policy)
Получение и установка регулировок (Getting and Setting Adjustments)
Привязки клавиатуры и мышки (Key and Mouse bindings)
Пример (Example)
Разные виджеты (Miscellaneous Widgets)
Ярлыки (Labels)
Стрелки (Arrows)
Всплывающие подсказки (The Tooltips Object)
Шкала степени выполнения (Progress Bars)
Диалоги (Dialogs)
Линейки (Rulers)
Строка состояния (Statusbars
Ввод текста (Text Entries)
Кнопки "карусель" (Spin Buttons)
Ввод со списком (Combo Box)
Календарь (Calendar)
Выбор цвета (Color Selection)
Выбор файла (File Selections)
Виджеты контейнеры (Container Widgets)
Контейнер событий (The EventBox)
Виджет Выравнивания (The Alignment widget)
Неподвижный контейнер (Fixed Container)
Контейнер Размещения (Layout Container)
Рамки (Frames)
Пропорциональная рамка (Aspect Frames)
Разделённые окна (Paned Window Widgets)
Окно просмотра (Viewports)
Окно прокручивания (Scrolled Windows)
Контейнеры кнопок (Button Boxes)
Панель инструментов (Toolbar)
Вкладки (Notebooks)
Меню (Menu Widget)
Пошаговое создание меню (Manual Menu Creation)
Пример создания меню (Manual Menu Example)
Использование ItemFactory (Using ItemFactory)
ItemFactory entries
Creating an ItemFactory
Making use of the menu and its menu items
Пример (Item Factory Example)
Недокументированные виджеты (Undocumented Widgets)
Accel Label
Option Menu
Menu Items
Check Menu Item
Radio Menu Item
Separator Menu Item
Tearoff Menu Item
Curves
Drawing Area
Font Selection Dialog
Message Dialog
Gamma Curve
Image
Plugs and Sockets
Tree View
Text View
Установки атрибутов виджетов (Setting Widget Attributes)
Время ожидания, IO и Пустая функция (Timeouts, IO and Idle Functions)
Время ожидания (Timeouts)
Контроль IO (Monitoring IO)
Пустая функция (Idle Functions)
Дополнительно о Событиях и Обработчиках Сигнала (Advanced Event and Signal Handling)
Функции Сигнала (Signal Functions)
Подключение и Отключение Обработчика (Connecting and Disconnecting Signal Handlers)
Блокировка и разблокирование Обработчика (Blocking and Unblocking Signal Handlers)
Воспроизведение и остановка Сигнала (Emitting and Stopping Signals)
Эмиссия Сигнала и Распространение (Signal Emission and Propagation)
Управление Выделенными областями (Managing Selections)
Краткий обзор (Overview)
Поиск выделения (Retrieving the selection
Замещение выделения (Supplying the selection)
Drag-and-drop (DND)
Краткий обзор (Overview)
Свойства (Properties)
Функции (Functions)
Установки исходного виджета (Setting up the source widget)
Сигналы исходного виджета (Signals on the source widget:)
Установки виджета назначения (Setting up a destination widget:)
Сигналы виджета назначения (Signals on the destination widget:)
GLib
Формулировка (Definitions)
Списки двойной связи (Doubly Linked Lists)
Односвязные списки (Singly Linked Lists)
Управление памятью (Memory Management)
Таймеры (Timers)
Обработка строки (String Handling)
Утилиты и функции ошибок (Utility and Error Functions)
Файлы настройки GTK (GTK's rc Files)
Функции для файлов настроек (Functions For rc Files)
Формат файла настройки GTK (GTK's rc File Format)
Пример файла настройки (Example rc file)
Создание собственных виджетов (Writing Your Own Widgets)
Краткий обзор (Overview)
Анатомия виджета (The Anatomy Of A Widget)
Создание сложного виджета (Creating a Composite widget)
Введение (Introduction)
Выбор родительского класса (Choosing a parent class)
Заголовочный файл (The header file)
Функция _get_type() (The _get_type() function)
Функция class_init() (The _class_init() function)
Функция _init()  (The _init() function)
И остальные ... (And the rest...)
Создание виджета с нуля (Creating a widget from scratch)
Введение (Introduction)
Отображение виджета на экране (Displaying a widget on the screen)
Истоки виджета циферблата (The origins of the Dial Widget)
Основы (The Basics)
gtk_dial_realize()
Определение размера (Size negotiation)
gtk_dial_expose()
Обработка событий (Event handling)
Возможные расширения (Possible Enhancements)
Дальнейшее изучение (Learning More)
Каракули, простой пример программы для рисования (Scribble, A Simple Example Drawing Program)
Краткий обзор (Overview)
Обработка событий (Event Handling)
Виджет DrawingArea и рисование (The DrawingArea Widget, And Drawing)
Добавление поддержки XInput (Adding XInput support)
Расширение возможностей устройства (Enabling extended device information)
Использование устройства с расширенными возможностями (Using extended device information)
Выяснение дополнительной информации об устройстве (Finding out more about a device)
Дальнейшие исследования (Further sophistications)
Подсказки по созданию GTK приложений (Tips For Writing GTK Applications)
Содействие (Contributing)
Благодарности (Credits)
Авторские права на данное руководство и замечания по распространению (Tutorial Copyright and Permissions Notice)
Сигналы GTK (GTK Signals)
GtkObject
GtkWidget
GtkData
GtkContainer
GtkCalendar
GtkEditable
GtkNotebook
GtkList
GtkMenuShell
GtkToolbar
GtkButton
GtkItem
GtkWindow
GtkHandleBox
GtkToggleButton
GtkMenuItem
GtkCheckMenuItem
GtkInputDialog
GtkColorSelection
GtkStatusBar
GtkCurve
GtkAdjustment
Типы событий GDK (GDK Event Types)
Примеры кода (Code Examples)
Tictactoe
tictactoe.h
tictactoe.c
ttt_test.c
GtkDial
gtkdial.h
gtkdial.c
dial_test.c
Scribble
scribble-simple.c
scribble-xinput.c

GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Cигналы GTK

Поскольку GTK - объектно ориентированный набор виджетов, то он имеет иерархию наследования. Этот механизм наследования применяет сигналы. Поэтому, вы должны обращаться к дереву иерархии виджета, используя сигналы, перечисленные в этом разделе.

GtkObject

void GtkObject::destroy (GtkObject *,
                         gpointer);

<<< Previous

Home

Next >>>

Tutorial Copyright and Permissions Notice

 

GtkWidget




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Типы событий GDK

Следующие типы данных передают в обработчики событий GTK+. Для каждого перечисленного типа данных, перечислены сигналы, которые используют этот тип данных.

Тип данных GdkEventType - специальный тип данных, который используется всеми другими типами данных как индикатор типа данных, который передают в обработчик сигнала. Как вы увидите ниже, каждая из структур данных события имеет члена этого типа. Это определено как перечисление типов следующим образом:

typedef enum
{
  GDK_NOTHING           = -1,
  GDK_DELETE            = 0,
  GDK_DESTROY           = 1,
  GDK_EXPOSE            = 2,
  GDK_MOTION_NOTIFY     = 3,
  GDK_BUTTON_PRESS      = 4,
  GDK_2BUTTON_PRESS     = 5,
  GDK_3BUTTON_PRESS     = 6,
  GDK_BUTTON_RELEASE    = 7,
  GDK_KEY_PRESS         = 8,
  GDK_KEY_RELEASE       = 9,
  GDK_ENTER_NOTIFY      = 10,
  GDK_LEAVE_NOTIFY      = 11,
  GDK_FOCUS_CHANGE      = 12,
  GDK_CONFIGURE         = 13,
  GDK_MAP               = 14,
  GDK_UNMAP             = 15,
  GDK_PROPERTY_NOTIFY   = 16,
  GDK_SELECTION_CLEAR   = 17,
  GDK_SELECTION_REQUEST = 18,
  GDK_SELECTION_NOTIFY  = 19,
  GDK_PROXIMITY_IN      = 20,
  GDK_PROXIMITY_OUT     = 21,
  GDK_DRAG_BEGIN        = 22,
  GDK_DRAG_REQUEST      = 23,
  GDK_DROP_ENTER        = 24,
  GDK_DROP_LEAVE        = 25,
  GDK_DROP_DATA_AVAIL   = 26,
  GDK_CLIENT_EVENT      = 27,
  GDK_VISIBILITY_NOTIFY = 28,
  GDK_NO_EXPOSE         = 29,
  GDK_OTHER_EVENT       = 9999  /* Не рекомендуемый, используйте вместо этого фильтры */
} GdkEventType;

Другой тип события отличается от этих - GdkEvent. Это объединение всех других типов данных, которое позволяет быть приведенным к определенному типу данных события в пределах обработчика сигнала.

Типы данных события определены следующим образом:

struct _GdkEventAny
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
};
struct _GdkEventExpose
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkRectangle area;
  gint count; /* Если ненулевое, следует больше событий. */
};
struct _GdkEventNoExpose
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  /* XXX: любой нуждается в X major_code или minor_code областях? */
};
struct _GdkEventVisibility
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkVisibilityState state;
};
struct _GdkEventMotion
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble pressure;
  gdouble xtilt;
  gdouble ytilt;
  guint state;
  gint16 is_hint;
  GdkInputSource source;
  guint32 deviceid;
  gdouble x_root, y_root;
};
struct _GdkEventButton
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble pressure;
  gdouble xtilt;
  gdouble ytilt;
  guint state;
  guint button;
  GdkInputSource source;
  guint32 deviceid;
  gdouble x_root, y_root;
};
struct _GdkEventKey
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  guint state;
  guint keyval;
  gint length;
  gchar *string;
};
struct _GdkEventCrossing
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkWindow *subwindow;
  GdkNotifyType detail;
};
struct _GdkEventFocus
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  gint16 in;
};
struct _GdkEventConfigure
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  gint16 x, y;
  gint16 width;
  gint16 height;
};
struct _GdkEventProperty
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkAtom atom;
  guint32 time;
  guint state;
};
struct _GdkEventSelection
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkAtom selection;
  GdkAtom target;
  GdkAtom property;
  guint32 requestor;
  guint32 time;
};
/* Этот тип события используется довольно редко. Это важно только
   для XInput програм которые рисуют свой курсор */
struct _GdkEventProximity
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  GdkInputSource source;
  guint32 deviceid;
};
struct _GdkEventDragRequest
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 requestor;
  union {
    struct {
      guint protocol_version:4;
      guint sendreply:1;
      guint willaccept:1;
      guint delete_data:1; /* Do *not* delete if link is sent, only
                              if data is sent */
      guint senddata:1;
      guint reserved:22;
    } flags;
    glong allflags;
  } u;
  guint8 isdrop; /* This gdk event can be generated by a couple of
                    X events - this lets the app know whether the
                    drop really occurred or we just set the data */
  GdkPoint drop_coords;
  gchar *data_type;
  guint32 timestamp;
};
struct _GdkEventDragBegin
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  union {
    struct {
      guint protocol_version:4;
      guint reserved:28;
    } flags;
    glong allflags;
  } u;
};
struct _GdkEventDropEnter
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 requestor;
  union {
    struct {
      guint protocol_version:4;
      guint sendreply:1;
      guint extended_typelist:1;
      guint reserved:26;
    } flags;
    glong allflags;
  } u;
};
struct _GdkEventDropLeave
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 requestor;
  union {
    struct {
      guint protocol_version:4;
      guint reserved:28;
    } flags;
    glong allflags;
  } u;
};
struct _GdkEventDropDataAvailable
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 requestor;
  union {
    struct {
      guint protocol_version:4;
      guint isdrop:1;
      guint reserved:25;
    } flags;
    glong allflags;
  } u;
  gchar *data_type; /* MIME type */
  gulong data_numbytes;
  gpointer data;
  guint32 timestamp;
  GdkPoint coords;
};
struct _GdkEventClient
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkAtom message_type;
  gushort data_format;
  union {
    char b[20];
    short s[10];
    long l[5];
  } data;
};
struct _GdkEventOther
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkXEvent *xevent;
};

<<< Previous

Home

Next >>>

GtkAdjustment

 

Code Examples




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Примеры кода

Ниже приведены примеры кода использованные в данном руководстве, которые не были полностью включены в другом месте.

Tictactoe

tictactoe.h

/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#ifndef __TICTACTOE_H__
#define __TICTACTOE_H__
#include <gdk/gdk.h>
#include <gtk/gtkvbox.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define TICTACTOE(obj)          GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
#define TICTACTOE_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
#define IS_TICTACTOE(obj)       GTK_CHECK_TYPE (obj, tictactoe_get_type ())
typedef struct _Tictactoe       Tictactoe;
typedef struct _TictactoeClass  TictactoeClass;
struct _Tictactoe
{
  GtkVBox vbox;
  GtkWidget *buttons[3][3];
};
struct _TictactoeClass
{
  GtkVBoxClass parent_class;
  void (* tictactoe) (Tictactoe *ttt);
};
GtkType        tictactoe_get_type        (void);
GtkWidget*     tictactoe_new             (void);
void           tictactoe_clear           (Tictactoe *ttt);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __TICTACTOE_H__ */

tictactoe.c

/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include "gtk/gtksignal.h"
#include "gtk/gtktable.h"
#include "gtk/gtktogglebutton.h"
#include "tictactoe.h"
enum {
  TICTACTOE_SIGNAL,
  LAST_SIGNAL
};
static void tictactoe_class_init          (TictactoeClass *klass);
static void tictactoe_init                (Tictactoe      *ttt);
static void tictactoe_toggle              (GtkWidget *widget, Tictactoe *ttt);
static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
GType
tictactoe_get_type ()
{
  static GType ttt_type = 0;
  if (!ttt_type)
    {
      static const GTypeInfo ttt_info =
      {
        sizeof (TictactoeClass),
        NULL,
        NULL,
        (GClassInitFunc) tictactoe_class_init,
        NULL,
        NULL,
        sizeof (Tictactoe),
        0,
        (GInstanceInitFunc) tictactoe_init,
      };
      ttt_type = g_type_register_static (GTK_TYPE_VBOX, "Tictactoe", &ttt_info, 0);
    }
  return ttt_type;
}
static void
tictactoe_class_init (TictactoeClass *class)
{
  GtkObjectClass *object_class;
  object_class = (GtkObjectClass*) class;
  tictactoe_signals[TICTACTOE_SIGNAL] = g_signal_new ("tictactoe",
                                         G_TYPE_FROM_CLASS (object_class),
                                         G_SIGNAL_RUN_FIRST,
                                         0,
                                         g_cclosure_marshal_VOID__VOID,
                                         G_TYPE_NONE, 0, NULL);
  class->tictactoe = NULL;
}
static void
tictactoe_init (Tictactoe *ttt)
{
  GtkWidget *table;
  gint i,j;
  table = gtk_table_new (3, 3, TRUE);
  gtk_container_add (GTK_CONTAINER (ttt), table);
  gtk_widget_show (table);
  for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j++)
      {
        ttt->buttons[i][j] = gtk_toggle_button_new ();
                                   i, i+1, j, j+1);
        g_signal_connect (G_OBJECT (ttt->buttons[i][j]), "toggled",
                          G_CALLBACK (tictactoe_toggle), (gpointer) ttt);
        gtk_widget_set_size_request (ttt->buttons[i][j], 20, 20);
        gtk_widget_show (ttt->buttons[i][j]);
      }
}
GtkWidget*
tictactoe_new ()
{
  return GTK_WIDGET (g_object_new (tictactoe_get_type (), NULL));
}
tictactoe_clear (Tictactoe *ttt)
{
  int i,j;
  for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j++)
      {
                                         NULL, ttt);
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
                                      FALSE);
                                           NULL, ttt);
      }
}
static void
tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
{
  int i,k;
  static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
                             { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
                             { 0, 1, 2 }, { 0, 1, 2 } };
  static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
                             { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
                             { 0, 1, 2 }, { 2, 1, 0 } };
  int success, found;
  for (k = 0; k < 8; k++)
    {
      success = TRUE;
      found = FALSE;
      for (i = 0; i < 3; i++)
        {
            GTK_TOGGLE_BUTTON (ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
          found = found ||
            ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
        }
      if (success && found)
        {
                         tictactoe_signals[TICTACTOE_SIGNAL], 0);
          break;
        }
    }
}

ttt_test.c

#include <stdlib.h>
#include <gtk/gtk.h>
#include "tictactoe.h"
void win( GtkWidget *widget,
          gpointer   data )
{
  g_print ("Yay!\n");
  tictactoe_clear (TICTACTOE (widget));
}
int main( int   argc,
          char *argv[] )
{
  GtkWidget *window;
  GtkWidget *ttt;
  gtk_init (&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (exit), NULL);
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  ttt = tictactoe_new ();
  gtk_container_add (GTK_CONTAINER (window), ttt);
  gtk_widget_show (ttt);
  g_signal_connect (G_OBJECT (ttt), "tictactoe",
                    G_CALLBACK (win), NULL);
  gtk_widget_show (window);
  gtk_main ();
  return 0;
}

<<< Previous

Home

Next >>>

GDK Event Types

 

GtkDial




GTK+ 2.0 Tutorial

Tony Gale

Ian Main

& the GTK team

замечания по поводу перевода присылать sergeyvp@gmail.com


Table of Contents
Доступность данного руководства (Tutorial Availability)
Введение (Introduction)
Начальные сведения (Getting Started)
Первая программа на GTK (Hello World in GTK)
Сборка первой программы (Compiling Hello World)
Теория сигналов и обратных вызовов (Theory of Signals and Callbacks)
События (Events)
Пошаговое рассмотрение "Hello World" (Stepping Through Hello World)
Подробнее (Moving On)
Типы данных (Data Types)
Подробнее об обработчиках сигналов (More on Signal Handlers)
Обновленный "Hello World" (An Upgraded Hello World)
Упаковка виджетов (Packing Widgets)
Теория упаковочных контейнеров (Theory of Packing Boxes)
Детально о контейнерах (Details of Boxes)
Демонстрационная программа упаковки (Packing Demonstration Program)
Упаковка с использованием таблиц (Packing Using Tables)
Пример табличной упаковки (Table Packing Example)
Краткий обзор виджетов (Widget Overview)
Преобразование (Casting)
Иерархия виджетов (Widget Hierarchy)
Виджеты без окон (Widgets Without Windows)
Кнопки (The Button Widget)
Обычные кнопки (Normal Buttons)
Переключатели (Toggle Buttons)
Контроль-кнопка или «флажок» (Check Buttons)
Кнопки выбора или «радио-кнопки» (Radio Buttons)
Регуляторы (Adjustments)
Создание регуляторов (Creating an Adjustment)
Использование регуляторов (Using Adjustments the Easy Way)
Внутреннее устройство регуляторов (Adjustment Internals)
Виджеты регулировки диапазона (Range Widgets)
Полоса прокрутки (Scrollbar Widgets)
Виджет масштабирования (Scale Widgets)
Создание виджетов масштабирования (Creating a Scale Widget)
Функции и Сигналы (Functions and Signals (well, functions, at least))
Основные функции диапазона (Common Range Functions)
Установка "политики обновления" (Setting the Update Policy)
Получение и установка регулировок (Getting and Setting Adjustments)
Привязки клавиатуры и мышки (Key and Mouse bindings)
Пример (Example)
Разные виджеты (Miscellaneous Widgets)
Ярлыки (Labels)
Стрелки (Arrows)
Всплывающие подсказки (The Tooltips Object)
Шкала степени выполнения (Progress Bars)
Диалоги (Dialogs)
Линейки (Rulers)
Строка состояния (Statusbars
Ввод текста (Text Entries)
Кнопки "карусель" (Spin Buttons)
Ввод со списком (Combo Box)
Календарь (Calendar)
Выбор цвета (Color Selection)
Выбор файла (File Selections)
Виджеты контейнеры (Container Widgets)
Контейнер событий (The EventBox)
Виджет Выравнивания (The Alignment widget)
Неподвижный контейнер (Fixed Container)
Контейнер Размещения (Layout Container)
Рамки (Frames)
Пропорциональная рамка (Aspect Frames)
Разделённые окна (Paned Window Widgets)
Окно просмотра (Viewports)
Окно прокручивания (Scrolled Windows)
Контейнеры кнопок (Button Boxes)
Панель инструментов (Toolbar)
Вкладки (Notebooks)
Меню (Menu Widget)
Пошаговое создание меню (Manual Menu Creation)
Пример создания меню (Manual Menu Example)
Использование ItemFactory (Using ItemFactory)
ItemFactory entries
Creating an ItemFactory
Making use of the menu and its menu items
Пример (Item Factory Example)
Недокументированные виджеты (Undocumented Widgets)
Accel Label
Option Menu
Menu Items
Check Menu Item
Radio Menu Item
Separator Menu Item
Tearoff Menu Item
Curves
Drawing Area
Font Selection Dialog
Message Dialog
Gamma Curve
Image
Plugs and Sockets
Tree View
Text View
Установки атрибутов виджетов (Setting Widget Attributes)
Время ожидания, IO и Пустая функция (Timeouts, IO and Idle Functions)
Время ожидания (Timeouts)
Контроль IO (Monitoring IO)
Пустая функция (Idle Functions)
Дополнительно о Событиях и Обработчиках Сигнала (Advanced Event and Signal Handling)
Функции Сигнала (Signal Functions)
Подключение и Отключение Обработчика (Connecting and Disconnecting Signal Handlers)
Блокировка и разблокирование Обработчика (Blocking and Unblocking Signal Handlers)
Воспроизведение и остановка Сигнала (Emitting and Stopping Signals)
Эмиссия Сигнала и Распространение (Signal Emission and Propagation)
Управление Выделенными областями (Managing Selections)
Краткий обзор (Overview)
Поиск выделения (Retrieving the selection
Замещение выделения (Supplying the selection)
Drag-and-drop (DND)
Краткий обзор (Overview)
Свойства (Properties)
Функции (Functions)
Установки исходного виджета (Setting up the source widget)
Сигналы исходного виджета (Signals on the source widget:)
Установки виджета назначения (Setting up a destination widget:)
Сигналы виджета назначения (Signals on the destination widget:)
GLib
Формулировка (Definitions)
Списки двойной связи (Doubly Linked Lists)
Односвязные списки (Singly Linked Lists)
Управление памятью (Memory Management)
Таймеры (Timers)
Обработка строки (String Handling)
Утилиты и функции ошибок (Utility and Error Functions)
Файлы настройки GTK (GTK's rc Files)
Функции для файлов настроек (Functions For rc Files)
Формат файла настройки GTK (GTK's rc File Format)
Пример файла настройки (Example rc file)
Создание собственных виджетов (Writing Your Own Widgets)
Краткий обзор (Overview)
Анатомия виджета (The Anatomy Of A Widget)
Создание сложного виджета (Creating a Composite widget)
Введение (Introduction)
Выбор родительского класса (Choosing a parent class)
Заголовочный файл (The header file)
Функция _get_type()(The _get_type() function)
Функция class_init()(The _class_init() function)
Функция _init()(The _init() function)
И остальные ... (And the rest...)
Создание виджета с нуля (Creating a widget from scratch)
Введение (Introduction)
Отображение виджета на экране (Displaying a widget on the screen)
Истоки виджета циферблата (The origins of the Dial Widget)
Основы (The Basics)
gtk_dial_realize()
Определение размера (Size negotiation)
gtk_dial_expose()
Обработка событий (Event handling)
Возможные расширения (Possible Enhancements)
Дальнейшее изучение (Learning More)
Каракули, простой пример программы для рисования (Scribble, A Simple Example Drawing Program)
Краткий обзор (Overview)
Обработка событий (Event Handling)
Виджет DrawingArea и рисование (The DrawingArea Widget, And Drawing)
Добавление поддержки XInput (Adding XInput support)
Расширение возможностей устройства (Enabling extended device information)
Использование устройства с расширенными возможностями (Using extended device information)
Выяснение дополнительной информации об устройстве (Finding out more about a device)
Дальнейшие исследования (Further sophistications)
Подсказки по созданию GTK приложений (Tips For Writing GTK Applications)
Содействие (Contributing)
Благодарности (Credits)
Авторские права на данное руководство и замечания по распространению (Tutorial Copyright and Permissions Notice)
Сигналы GTK (GTK Signals)
GtkObject
GtkWidget
GtkData
GtkContainer
GtkCalendar
GtkEditable
GtkNotebook
GtkList
GtkMenuShell
GtkToolbar
GtkButton
GtkItem
GtkWindow
GtkHandleBox
GtkToggleButton
GtkMenuItem
GtkCheckMenuItem
GtkInputDialog
GtkColorSelection
GtkStatusBar
GtkCurve
GtkAdjustment
Типы событий GDK (GDK Event Types)
Примеры кода (Code Examples)
Tictactoe
tictactoe.h
tictactoe.c
ttt_test.c
GtkDial
gtkdial.h
gtkdial.c
dial_test.c
Scribble
scribble-simple.c
scribble-xinput.c

 

 

Next >>>

 

 

Tutorial Availability




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Виджеты контейнеры

Контейнер событий (The EventBox)

Некоторые виджеты GTK не имеют собственных окон (X windows), поэтому используют родительские. Из-за этого, они не могут получать события и в результате могут иметь неправильный размер и т.д. Если вы хотите большего от этих виджетов, EventBox - для вас.

На первый взгляд виджет EventBox кажется бесполезным. Он не создает ничего на экране и не отвечает ни на какие события. Однако, он  выполняет функцию обеспечения дочернего виджета окном. Это важно потому, что многие виджеты GTK не имеют ассоциированных окон (X window). Отсутствие окна экономит память и улучшает работу, но и имеет некоторые недостатки. Виджет без окна X не может получить события и не имеет привязки к его содержимому. Хотя название EventBox подчеркивает его функцию обработки событий (event-handling), его можно также использовать для прерывания (подробнее смотрите пример ниже).

Создание нового виджета EventBox:

        GtkWidget *gtk_event_box_new( void );

Добавляем в виджет EventBox дочерний виджет:

    gtk_container_add (GTK_CONTAINER (event_box), child_widget);

Следующий пример демонстрирует обе возможности EventBox - в нём создаётся длинный ярлык прикрепленный к маленькому контейнеру, при нажатии кнопкой мыши на ярлыке происходит выход из программы. Изменение размера окна раскрывает полное содержимое ярлыка.

#include <stdlib.h>
#include <gtk/gtk.h>
int main( int argc,
          char *argv[] )
{
    GtkWidget *window;
    GtkWidget *event_box;
    GtkWidget *label;
    
    gtk_init (&argc, &argv);
    
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    gtk_window_set_title (GTK_WINDOW (window), "Event Box");
    
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (exit), NULL);
    
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    
    /* Создаём контейнер события и добавляем его в основное окно */
    
    event_box = gtk_event_box_new ();
    gtk_container_add (GTK_CONTAINER (window), event_box);
    gtk_widget_show (event_box);
    
    /* Создаём длинный ярлык */
    
    label = gtk_label_new ("Click here to quit, quit, quit, quit, quit");
    gtk_container_add (GTK_CONTAINER (event_box), label);
    gtk_widget_show (label);
    
    /* Делаем усечение */
    gtk_widget_set_size_request (label, 110, 20);
    
    /* И связываем с этим действие */
    gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
    g_signal_connect (G_OBJECT (event_box), "button_press_event",
                      G_CALLBACK (exit), NULL);
    
 
    gtk_widget_realize (event_box);
    gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
    
    gtk_widget_show (window);
    
    gtk_main ();
    
    return 0;
}

<<< Previous

Home

Next >>>

File Selections

 

The Alignment widget




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Меню

Есть два способа создать меню - легкий и сложный. Оба способа могут использоваться, но обычно используется Itemfactory (лёгкий путь). "Сложный" путь состоит в том, чтобы создать все меню, используя вызовы непосредственно. "Лёгкий" путь заключается в том, что используется вызов gtk_item_factory. В каждом подходе есть свои преимущества и недостатки.

Itemfactory («фабрика» пунктов меню) простой способ создания и добавления пунктов в меню, при создании нескольких функций оболочек для создания меню. Используя ручной метод можно пойти длинным путём для удобства и простоты использования. С Itemfactory, не возможно добавить изображения или символ '/' в меню.

Пошаговое создание меню

Сначала мы рассмотрим сложный путь создания меню.

Есть три виджета, которые входят в создание строки меню (menubar) и суб меню (submenus):

Всё немного усложнено тем фактом, что виджеты пункта меню используются для двух различных целей. Они являются виджетами упакованными в меню и виджетами упакованными в строку меню, которая при выборе активизирует встроенное меню.

Рассмотрим функции для создания меню и строк меню. Первая функция используется для создания нового меню.

GtkWidget *gtk_menu_bar_new( void );

 Можно использовать gtk_container_add() для упаковки в окно, или box_pack функцию для упаковки в контейнер - как некоторые кнопки.

GtkWidget *gtk_menu_new( void );

Эта функция возвращает указатель на вновь созданное меню; оно никогда не показывается (gtk_widget_show()), а служит контейнером для пунктов меню. Я надеюсь что это прояснит ниже приведённый пример.

Следующие три запроса используются, чтобы создать пункты меню, которые упакованы в меню и строки меню.

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 );

Эти вызовы функций используются для создания пунктов меню, которые должны быть показаны. Помните о различиях между "меню" созданного с использованием функции gtk_menu_new() и "пунктом меню" созданным с использованием функции gtk_menu_item_new(). Пункт меню является фактически кнопкой со связанным действием, а меню фактически является контейнером для пунктов меню.

Функции gtk_menu_item_new_with_label() и gtk_menu_item_new() создают ярлык для пункта меню и новый пункт меню соответственно, причём ярлык естественно упаковывается в пункт меню.

Как только пункт меню создан, его необходимо поместить в меню. Это делается с помощью функции gtk_menu_shelll_append. Для перехвата события "выбор пользователя", нужно пункт меню соединить с сигналом activate. Вот пример создания меню "файл" со стандартными пунктами открыть (Open), сохранить (Save), и выход (Quit):

    file_menu = gtk_menu_new ();    /* меню не нужно отображать */
    /* Создаём новые пункты меню */
    open_item = gtk_menu_item_new_with_label ("Open");
    save_item = gtk_menu_item_new_with_label ("Save");
    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), quit_item);
    /* присоединяем функцию к сигналу выбора */
    g_signal_connect_swapped (G_OBJECT (open_item), "activate",
                              G_CALLBACK (menuitem_response),
                              (gpointer) "file.open");
    g_signal_connect_swapped (G_OBJECT (save_item), "activate",
                              G_CALLBACK (menuitem_response),
                              (gpointer) "file.save");
    /* прикрепляем пункт меню Quit к функции выхода */
    g_signal_connect_swapped (G_OBJECT (quit_item), "activate",
                              G_CALLBACK (destroy),
                              (gpointer) "file.quit");
    /* отображаем пункты меню */
    gtk_widget_show (open_item);
    gtk_widget_show (save_item);
    gtk_widget_show (quit_item);

Это наше меню. Теперь мы должны создать строку меню и пункт меню для входа «файл», к которому мы добавляем наше меню. Код примерно такой:

    menu_bar = gtk_menu_bar_new ();
    gtk_container_add (GTK_CONTAINER (window), menu_bar);
    gtk_widget_show (menu_bar);
    file_item = gtk_menu_item_new_with_label ("File");
    gtk_widget_show (file_item);

Теперь нужно ассоциировать меню с file_item (пунктом «файл»). Это делает функция:

void gtk_menu_item_set_submenu( GtkMenuItem *menu_item,
                                GtkWidget   *submenu );

Продолжение нашего примера:

    gtk_menu_item_set_submenu (GTK_MENU_ITEM (file_item), file_menu);

Добавляем меню к строке меню:

void gtk_menu_bar_append( GtkMenuBar *menu_bar,
                          GtkWidget  *menu_item );

В нашем случае выглядит так:

    gtk_menu_bar_append (GTK_MENU_BAR (menu_bar), file_item);

Если нам нужно меню справа, такое например как "помощь", то мы можем использовать следующую функцию (как и в текущем примере file_item ) после прикрепления к панели меню.

void gtk_menu_item_right_justify( GtkMenuItem *menu_item );

Вот список шагов для создания панели меню с прикрепленным пунктом панели меню.

Создание всплывающего меню - почти то же самое. Различие заключается в то, что меню не размещается автоматически ("automatically") в панели меню (menubar), а явно с помощью вызова функции gtk_menu_popup() при событии "нажатая кнопка". Вот шаги выполнения:


<<< Previous

Home

Next >>>

Notebooks

 

Manual Menu Example




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Как получить руководство?

Копия этого руководства распространяется с исходными текстами GTK+ в формате SGML и HTML. В случае бинарных поставок консультируйтесь у поставщиков. А также копия руководства доступна в интернет по адресу www.gtk.org/tutorial.

Версия данного руководства в разных форматах находится в упакованном виде на ftp://ftp.gtk.org/pub/gtk/tutorial. Это наилучший вариант для тех кто желает иметь локальную копию руководства или распечатать его на бумаге.


<<< Previous

Home

Next >>>

GTK+ 2.0 Tutorial

 

Introduction




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Недокументированные виджеты

Требуются авторы для описания работы всех виджетов перечисленных в этом разделе! :) Пожалуйста окажите помощь в дополнении и расширении данного руководства.

Если вам нужно использовать какиенибудь из недокументированных виджетов, то обратитесь к заголовочным файлам поставляемым вместе с дистрибутивом GTK. Имена функций GTK's очень описательные. Как работает виджет можно понять по его декларации и используя примеры похожих виджетов.

Когда вы хорошо разберётесь в работе недокументированного виджета и его функциях, рассмотрите возможность написать раздел в данное руководство, так вы окажете помощь другим людям и сэкономите их время.

Accel Label


<<< Previous

Home

Next >>>

Item Factory Example

 

Option Menu




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Установки атрибутов виджетов

Здесь описаны функции используемые в работе с виджетами. Они могут использоваться, чтобы установить стиль, дополнение, размер и т.д.

(Возможно, нужно сделать отдельный раздел по акселераторам)

void gtk_widget_activate( GtkWidget *widget );
void gtk_widget_set_name( GtkWidget *widget,
                          gchar     *name );
gchar *gtk_widget_get_name( GtkWidget *widget );
void gtk_widget_set_sensitive( GtkWidget *widget,
                               gboolean   sensitive );
void gtk_widget_set_style( GtkWidget *widget,
                           GtkStyle  *style );
                                           
GtkStyle *gtk_widget_get_style( GtkWidget *widget );
GtkStyle *gtk_widget_get_default_style( void );
void gtk_widget_set_size_request ( GtkWidget *widget,
                                   gint       width,
                                   gint       height );
void gtk_widget_grab_focus( GtkWidget *widget );
void gtk_widget_show( GtkWidget *widget );
void gtk_widget_hide( GtkWidget *widget );

<<< Previous

Home

Next >>>

Text View

 

Timeouts, IO and Idle Functions




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Timeouts, IO и Idle Functions

Время ожидания (Timeouts)

Вас наверное интересует как GTK выполняет работу в gtk_main. Для этого есть несколько опций.  Используя следующую функцию Вы можете создать функцию времени ожидания, которая будет вызываться каждую миле секунду (milliseconds).

gint gtk_timeout_add( guint32     interval,
                      GtkFunction function,
                      gpointer    data );

Первый аргумент кол-во миле секунд между вызовами вашей функции. Второй аргумент - ваша функция которую вы хотите вызывать, а третий - данные передаваемые в функцию обратного вызова.  Возвращаемое значение - целочисленное "tag", которое может использоваться, чтобы остановить время ожидания, вызвав:

void gtk_timeout_remove( gint tag );

Вы так же можете остановить время ожидания вернув 0 (zero) или FALSE из вашей функции обратного вызова. Очевидно, что для продолжения нужно вернуть не ноль (non-zero), т.е. TRUE.

Декларация вашего обратного вызова:

gint timeout_callback( gpointer data );

<<< Previous

Home

Next >>>

Setting Widget Attributes

 

Monitoring IO




Дополнительно о Событиях и Обработчиках сигналов

Функции Сигналов (Signal Functions)

Подключение и отключение Обработчиков

gulong g_signal_connect( GObject     *object,
const gchar *name,
GCallback func,
gpointer func_data );

gulong g_signal_connect_after( GObject *object,
const gchar *name,
GCallback func,
gpointer func_data );

gulong g_signal_connect_swapped( GObject *object,
const gchar *name,
GCallback func,
GObject *slot_object );

void g_signal_handler_disconnect( GObject *object,
gulong handler_id );

void g_signal_handlers_disconnect_by_func( GObject *object,
GCallback func,
gpointer data );

Блокировка и разблокирование Обработчиков сигнала

void g_signal_handler_block( GObject *object,
gulong handler_id);

void g_signal_handlers_block_by_func( GObject *object,
GCallback func,
gpointer data );

void g_signal_handler_unblock( GObject *object,
gulong handler_id );

void g_signal_handler_unblock_by_func( GObject *object,
GCallback func,
gpointer data );

Воспроизведение и остановка Сигнала

void g_signal_emit( GObject *object,
guint signal_id,
... );

void g_signal_emit_by_name( GObject *object,
const gchar *name,
... );

void g_signal_emitv( const GValue *instance_and_params,
guint signal_id,
GQuark detail,
GValue *return_value );

void g_signal_stop_emission( GObject *object,
guint signal_id,
GQuark detail );

void g_signal_stop_emission_by_name( GObject *object,
const gchar *detailed_signal );

GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Управление "Выделениями" (Managing Selections)

Краткий обзор

Один тип межпроцессорной коммуникации поддерживается X и GTK это "выделение" (selections). Выделение (selection) идентифицирует порцию данных, например часть текста, выбранных пользователем каким либо способом, например с помощью мыши. Только одно приложение в конкретный момент времени может быть владельцем (the owner) индивидуального выделения (particular selection), предыдущий приложение-владелец должен сообщить пользователю о том что выделение было оставлено.  Другие приложения могут просить содержание выделения в различных формах, вызовом targets.  Может быть любое число выделений, но большинство приложений X обрабатывает только одно - primary selection.

В большинстве случаев для приложения GTK не обязательно иметь дело с выделениями самостоятельно. Стандартные виджеты, типа виджета Ввода, уже имеют возможность требовать выделения (например, когда пользователь перемещается по тексту) и отыскивать содержимое выделения, принадлежащего другому виджету или другому приложению (например, когда пользователь щелкает второй кнопкой мыши). Однако, могут быть случаи, в которых вы хотите дать другим виджетам способность предоставлять выделение, или вы желаете отыскать адресатов, не поддерживаемых по умолчанию.

Фундаментальное понятие обработки выделений (selection) - atom.  Атом - целое число, которое однозначно определяет строку (на определенном дисплее).  Определенные атомы предопределены сервером X, и в некоторых случаях есть константы в gtk.h, соответствующие этим атомам. Например константа GDK_PRIMARY_SELECTION соответствует строке "PRIMARY". В других случаях, вы должны использовать функции gdk_atom_intern() - получить атом соответствующей строки и gdk_atom_name() - получить название атома. И selections и targets идентифицируются атомами (atoms).


<<< Previous

Home

Next >>>

Signal Emission and Propagation

 

Retrieving the selection




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Drag-and-drop (DND)

GTK+ имеет установки функций высокого уровня для межпроцессорных коммуникаций посредством системы drag-and-drop. GTK+ может  выполнять drag-and-drop поверх низкоуровневых  Xdnd и Motif drag-and-drop протоколов.

Краткий обзор

Приложения поддерживающие GTK+ drag-and-drop, сначала определяют и устанавливают виджеты GTK+ для drag-and-drop. Каждый виджет может быть предметом и/или местом назначения для drag-and-drop. Заметьте, что эти GTK+ виджеты должны иметь связанное с ними окно (X Window), проверьте используя GTK_WIDGET_NO_WINDOW(widget)).

Виджеты являющиеся предметом перетаскивания могут отправлять данные, позволяя пользователю перетаскивать их в другое место, а виджеты которые являются целью назначения для перетаскивания могут принимать эти данные. Drag-and-drop может ограничивать то от кого получаются данные, например то же самое приложение или любое приложение (включая себя).

Посылка и получение данных использует сигналы GTK+. Проброс элемента к виджету назначения требует и запрашиваемых данных (для виджета назначения) и посылаемых данных обработчика сигнала (для исходящего виджета). Дополнительный сигнал обработчиков используется если вы хотите знать когда начинается перетаскивание, когда заканчивается проброс завершая всю операцию (успешно или нет). 

Ваше приложение должно будет обеспечить данные для исходных виджетов когда требуется, это вовлекает, имеющий данные проброса, обработчик сигнала. Для виджета назначения они будут нуждаться в данных посылаемых обработчиком сигнала.

Итак, типичный цикл drag-and-drop выглядит так:

  1. Перетаскивание началось.

  2. Запрос данных перетаскивания (когда происходит проброс).

  3. Запрос данных проброса ( может быть на том же самом или другом приложении).

  4. Удаление перетаскиваемых данных ( если перетаскивание было перемещением).

  5. Процедура Drag-and-drop выполнена.

Есть несколько не значительных шагов, которые будут обсуждаться позже.


<<< Previous

Home

Next >>>

Supplying the selection

 

Properties




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


GLib

GLib - библиотека низшего уровня, которая обеспечивает много полезных определений и функций, доступных для использования при создании GDK и GTK приложений. Она включает в себя определение основных типов и их пределов, стандартные макросы, преобразования типов, последовательность байт, выделение памяти, предупреждения и утверждения, регистрацию сообщений, таймеры, строковые утилиты, методы функций, лексический сканер, динамическая загрузка модулей и автоматическое дополнение строк. А также множество структур данных (связанные с ними операции), включая участки памяти, списки двойной связи, списки одиночной связи, хэш таблицы, строки (имеющие динамический рост), части строк (группы строк), массивы (способные увеличиваться в размере по мере добавления элементов), сбалансированные двоичные деревья, N-ary деревья, кварки (двухсторонняя ассоциация строки и уникального целочисленного идентификатора), ключевые списки данных (список элементов данных, доступных строкой или целочисленным id), связи и группы связанных данных (таблицы данных, которые могут быть индексированы на любом числе областей), кэши.

Не каждая функция, структура данных, или операция GLib перечислена здесь. Для более полной информации о библиотеке GLib смотрите документацию. Один из источников документации о GLib  http://www.gtk.org/.

Если вы используете другой язык программирования (отличный от C), вам необходимо посмотреть документацию по привязкам к вашему языку (binding documentation). В некоторых случаях ваш язык может иметь эквивалентные встроенные функциональные возможности, в то время как в других случаях это может быть не возможно.

Формулировка

Определения для экстремальных значений многих из стандартных типов:

G_MINFLOAT
G_MAXFLOAT
G_MINDOUBLE
G_MAXDOUBLE
G_MINSHORT
G_MAXSHORT
G_MININT
G_MAXINT
G_MINLONG
G_MAXLONG

Кроме того определены следующие типы. Некоторые зависят от архитектуры процессора. Помните о разных размерах указателей если вам нужна портируемость. Например указатель на Alpha равен 8 bytes, а на Intel 80x86 family CPUs равен 4.

char   gchar;
short  gshort;
long   glong;
int    gint;
char   gboolean;
unsigned char   guchar;
unsigned short  gushort;
unsigned long   gulong;
unsigned int    guint;
float   gfloat;
double  gdouble;
long double gldouble;
void* gpointer;
gint8
guint8
gint16
guint16
gint32
guint32

<<< Previous

Home

Next >>>

Functions

 

Doubly Linked Lists




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Файлы настройки GTK's

GTK использует для настроек программ специальные файлы (rc files). Например они могут использоваться для установки цветовой схемы виджетов.

Функции для файла настроек

Во время старта вашего приложения вы должны включить вызов:

void gtk_rc_parse( char *filename );

Аргумент filename - ваш файл настроек (rc file). Это заставит GTK использовать настройки указанные в вашем файле для отрисовки стиля виджетов.

Если вы хотите использовать специальные настройки для отдельных виджетов, используйте функцию:

void gtk_widget_set_name( GtkWidget *widget,
                          gchar     *name );

В качестве первого аргумента передаётся ваш новый виджет, а в качестве второго его имя. Это позволит вам изменять настройки виджета, через файл настройки (rc file), используя его имя.

Если вы используете вызов:

button = gtk_button_new_with_label ("Special Button");
gtk_widget_set_name (button, "special button");

Тогда кнопке присваивается имя "special button" и вы можете обращаться к ней через файл настройки (rc file), как "special button.GtkButton". [<--- Verify ME!]

В файле примере устанавливаются свойства главного окна и позволяется всем дочерним виджетам наследовать стиль "main button". Вот код используемый приложением:

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name (window, "main window");

И затем стиль определяется в файле настроек (rc file):

widget "main window.*GtkButton*" style "main_button"

Поэтому все виджеты кнопок, находящихся в "main window", будут иметь стиль как у "main_buttons" определенный в файле настроек (rc file).

Как видите это очень мощный и гибкий инструмент настроек. Используйте своё воображение для более лучшего его применения.


<<< Previous

Home

Next >>>

Utility and Error Functions

 

GTK's rc File Format




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Создание собственных виджетов

Краткий обзор

Хотя в состав GTK входит достаточное количество виджетов для решения большинства стандартных задач программирования, может возникнуть ситуация когда вам понадобится создать свой собственный виджет. Зачастую для создания своего собственного виджета достаточно взять уже существующий и изменить или добавить несколько строк кода. Но перед тем как делать свой собственный виджет, лучше поискать, возможно ктото уже сделал то что вам нужно.  Это предотвратит дублирование виджетов и поможет держать код различных программ последовательным. После создания своего собственного виджета было бы неплохо сообщить об этом другим разработчикам, так вы поможете многим людям сэкономить время. Лучше всего размещать эти сообщения в gtk-list.

Полные исходные тексты примеров виджетов доступны по следующему адресу:

http://www.gtk.org/~otaylor/gtk/tutorial/


<<< Previous

Home

Next >>>

Example rc file

 

The Anatomy Of A Widget




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Введение

Данный набор библиотек лицензируется по LGPL, таким образом вы можете разрабатывать свободное программное обеспечение, программное обеспечение с открытым кодом или даже закрытые коммерческие программы не беспокоясь о лицензионных отчислениях или каких нибудь ограничениях в использовании GTK+. GTK (GIMP Toolkit) набор библиотек для построения графического интерфейса пользователя (GUI).

Первоначально библиотека разрабатывалась в рамках проекта GNU Image Manipulation Program (GIMP) по созданию программы редактирования изображений, но в данный момент написано огромное колличество программ с использованием GTK, включая GNOME (GNU Network Object Model Environment - Сетевая Объектная Среда GNU). GTK разрабатывается на основе GDK (GIMP Drawing Kit) которая в свою очередь является оболочкой для функций низкого уровня оконной среды (Xlib в случае X window system), и gdk-pixbuf, библиотека манипулирования изображением на стороне клиента.

Основные разработчики GTK:

GTK в настоящее время поддерживается:

GTK - по существу объектно ориентированный программный интерфейс приложения (API). Хотя написан полностью на языке программирования C, в нем заложены идеи классов и функций обратного вызова (сallback functions).

Еще есть третий компонент под названием GLib предназначенный для замены некоторых стандартных вызовов, а также содержит некоторые дополнительные функции для работы со связанными списками и т.д. Замененные функции предназначены для повышения мобильности GTK's, так некоторые функции реализованные здесь недоступны в некоторых системах (Unixes), например g_strerror(). Также внесены дополнения для libc, такие как g_malloc() для улучшения утилит отладки.

В версии 2.0, GLib собрала систему типов которая формирует фонд для иерархии классов GTK's, система сигналов которая используется повсюду в GTK, интерфейс библиотек, предоставляемый программам при компиляции (API), которые резюмируют различия родных нитей APIs разных платформ и средства для загрузки модулей.

И как последний компонент, GTK использует библиотеку Pango для интернационализации текстового вывода.

Есть привязки GTK (GTK bindings) для многих других языков программирования: C++, Guile, Perl, Python, TOM, Ada95, Objective C, Free Pascal, Eiffel, Java и C#. Это руководство описывает С интерфейс GTK. Если вы хотите использовать GTK с другим языком программирования, то вам следует в первую очередь ознакомится с документацией для привязки (GTK bindings) к этому языку. В ней может находится важная информация которая потребуется перед тем как изучать данное руководство. Есть кроссплатформенные APIs (такие как wxWindows и V) использующие GTK как основу, они также требуют изучения дополнительной документации.

Если вы разрабатываете ваше GTK приложение на языке С++, вам могут понадобится некоторые дополнения. Для С++ существует привязка GTK--, она предоставляет лучший C++-like интерфейс для GTK; вам лучше изучить её чем это руководство. Если вам не понравится этот подход то существует две альтернативы использования GTK. Во-первых вы можете применять для связи с GTK только C как подмножество C++ и затем использовать интерфейс С как описано в данном руководстве. Во-вторых, вы можете использовать GTK совместно с C++ объявляя функции обратного вызова (callbacks) как статические в C++ классах, и вызывать GTK используя C интерфейс. Если вы выберете последний вариант, то сможете использовать в качестве данных, передаваемых callback-функции, указатель на объект, с которым вы работаете (значение "this"). Выбор между тремя этими способами вопрос вкуса, в любом случае вы получаете C++ и GTK. Не один из этих вариантов не требует специального препроцессора и вы в любом случае получаете стандартный C++ для GTK.

Это руководство, попытка создать документацию по GTK, но оно не является полным. Это руководство для хорошего понимания языка программирования С и создания программ на нём. Для читателя было бы хорошо знать основы программирования в X среде, но это не является обязательным. Если вы изучаете GTK как первый свой инструмент для создания оконных интерфейсов, то прокомментируйте пожалуйста с какими трудностями вы столкнулись изучая данное руководство.

Этот документ находится в постоянной разработке. Вы можете посмотреть последние обновления на http://www.gtk.org/. Замечания по поводу перевода можно отправлять sergeyvp@gmail.com.

Если вы сообщите о проблемах при изучении GTK по данному документу, то вы примете участие в улучшении данного руководства. Пожалуйста посмотрите секцию Содействие для получения дополнительной информации.


<<< Previous

Home

Next >>>

Tutorial Availability

 

Getting Started




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Каракули, простой пример программы для рисования

Краткий обзор

В этом разделе, мы будем строить простую программу для рисования. В процессе мы исследуем как обрабатывать события при нажатии мышки, как рисовать в окне и как улучшить рисунок при использовании фоновой карты пикселей (backing pixmap).  После создания простой программы для рисования, мы расширим её, добавляя поддержку устройств XInput, таких как планшет для рисования (drawing tablets). GTK обеспечивает поддержку подпрограмм, которые делают получение расширенной информации, типа давления и наклона, от таких устройств весьма простой задачей.


<<< Previous

Home

Next >>>

Learning More

 

Event Handling




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Подсказки по созданию GTK приложений

Этот раздел содержит простые советы по созданию хороших GTK программ. В данный момент этот раздел очень короткий, но я надеюсь в будущем он будет дополнен и расширен вместе с данным руководством.

Используйте GNU autoconf и automake! Они - ваши друзья :) Automake исследует файлы C, определяет, как они зависят друг от друга, и производит Makefile, таким образом файлы могут быть собраны в правильном порядке. Autoconf позволяет автоматическую конфигурацию установки программного обеспечения, обращаясь с большим количеством специальных систем, увеличивая мобильность. Здесь планируется сделать короткий обзор этих утилит.

При создании кода на C, используйте только С комментарии (начинается как "/*" и заканчивается на "*/"). Хотя многие компиляторы С поддерживают С++ подобный стиль комментариев ("//"), стандарт ANSI C не поддерживает C++-стиль комментариев.


<<< Previous

Home

Next >>>

Adding XInput support

 

Contributing




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Содействие

Данное руководство, как и многое другое замечательное программное обеспечение было создано добровольцами. Если вы хорошо осведомлены о каких нибудь возможностях или свойствах GTK не упомянутых в данном руководстве, пожалуйста помогите дополнить и улучшить его.

Если вы действительно захотите сделать ваш вклад в развитие руководства, то вышлите ваш текст по почте Tony Gale, gale@gtk.org. Также вам необходимо знать что данное руководство является свободным, поэтому все ваши дополнения тоже должны быть таковыми. Тоесть все люди могут копировать, распространять, использовать примеры этого руководства в своих программах без всяких ограничений.

Спасибо вам за ваше участие!


<<< Previous

Home

Next >>>

Tips For Writing GTK Applications

 

Credits




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Благодарности

Мы хотели бы поблагодарить следующих людей за вклад в создание этого текста.

И всем вам, кто помогал редактировать данный документ.

Спасибо!


<<< Previous

Home

Next >>>

Contributing

 

Tutorial Copyright and Permissions Notice




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Авторские права на данное руководство

The GTK Tutorial is Copyright (C) 1997 Ian Main.

Copyright (C) 1998-2002 Tony Gale.

Разрешается делать и распространять дословные копии этого руководства, если объявление об авторском праве и это уведомление о разрешении сохраняются на всех копиях.

Разрешается копировать и распространять измененные версии этого документа при условиях для дословного копирования, при условии, что это объявление об авторском праве включено точно как в оригинале, и что полная версия работы распространена в соответствии с уведомлением о разрешении, идентичным этому.

Разрешается распространять копии трансляции этого документа на другие языки, при вышеупомянутых условиях для измененных версий.

Если вы намереваетесь включить этот документ в издаваемую вами работу, пожалуйста войдите в контакт с мантейнером (maintainer) и мы постараемся гарантировать, что вы имеете в наличии самую свежую информацию.

Нет никакой гарантии, что этот документ соответствует его намеченной цели. Этот документ существует как бесплатный информационный ресурс. Также, авторы и мантейнеры не дают никакой гарантии, что информация является точной.


<<< Previous

Home

Next >>>

Credits

 

GTK Signals




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Углубленное изучение

Типы данных

Вы наверное заметили некоторые моменты в предыдущих примерах, требующие объяснения. В системе GLib существуют собственные типы данных такие, как gint, gchar и т.д. эти типы являются аналогами типов в языке программирования С. Это сделано для преодоления зависимости от платформы при выполнении расчетов.

Хорошим примером является "gint32" который определяет тип целого числа размером 32 bit на любой платформе, например 64 bit alpha, или 32 bit i386. Определения типов являются интуитивными и прямолинейными. Они все определены в glib/glib.h (который включен в gtk.h).

Вы также заметили использование GtkWidget когда функция обращается к GtkObject. GTK имеет объектно ориентированный дизайн с использованием виджетов (widget) как объектов (object).


<<< Previous

Home

Next >>>

Stepping Through Hello World

 

More on Signal Handlers




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Упаковочные виджеты (Packing Widgets)

При создании графических интерфейсов необходимо размещать большое кол-во виджетов внутри диалоговых окон. Первый пример helloworld имел только один виджет, поэтому мы могли использовать вызов gtk_container_add() для размещения его внутри окна. Когда вам понадобится размещать в окне больше одного виджета, нужно будет каким-то образом контролировать их расположение. Для этого и нужны упаковочные контейнеры.

Теория упаковочных контейнеров (Packing Boxes)

Существует два вида упаковочных невидимых контейнеров для размещения виджетов, горизонтальный и вертикальный. Когда используется горизонтальный контейнер, объекты внутри него могут располагаться с права налево, или наоборот в зависимости от использованных вызовов. В вертикальном контейнере виджеты располагаются сверху вниз или снизу вверх. Вы можете использовать любые комбинации расположения контейнеров (рядом, внутри), для достижения желаемого результата.

Для создания нового горизонтального контейнера используется вызов gtk_hbox_new(), а для вертикального gtk_vbox_new(). Функции gtk_box_pack_start() и gtk_box_pack_end() используются для размещения объектов внутри этих контейнеров. Функция gtk_box_pack_start() размещает объекты сверху вниз в вертикальных контейнерах и слева на право в горизонтальных, а функция. gtk_box_pack_end() соответственно снизу вверх и справа на лево. Эти функции позволяют нам использовать правое или левое, а также верхнее или нижнее выравнивание виджетов и даже смешанное для достижения необходимого результата. В большинстве примеров мы используем gtk_box_pack_start(). Любой объект или виджет может выступать в роли контейнера. Кнопка фактически тоже является контейнером, но мы размещаем в ней только надпись для обозначения.

При использовании этих вызовов, GTK узнаёт где размещаются ваши виджеты и может автоматически регулировать размер, а также выполнять другие полезные действия. Есть и другие методы упаковки ваших виджетов, если вы думаете что данный метод даёт слишком мало гибкости при создании и размещении виджетов.


<<< Previous

Home

Next >>>

An Upgraded Hello World

 

Details of Boxes




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Краткий обзор виджетов

Основные действия для создания GTK виджетов:

  1. gtk_*_new() - одна из функций для создания виджетов. Все они детализированы в этой секции.

  2. Соединение всех сигналов и событий, которые мы собираемся использовать, с обработчиками.

  3. Установка атрибутов виджетов.

  4. Упаковка виджетов в контейнер, используя соответствующий запрос, типа gtk_container_add() или gtk_box_pack_start().

  5. Вывод виджета на экран gtk_widget_show().

gtk_widget_show() позволяет GTK выводить виджеты согласно установленным атрибутам. Для скрытия виджетов можно использовать gtk_widget_hide. Порядок вывода виджетов на экран не важен, но лучше выводить основное окно последним чтобы все виджеты в окне отображались одновременно, а не появлялись по мере их формирования. Дочерние виджеты не отображаются до тех пор, пока не показано основное для них окно с помощью функции gtk_widget_show().

Преобразование

GTK использует систему преобразования типов. Она всегда использует макросы для проверки преобразования, перед тем как выполнить его. Вот некоторые из них:

  G_OBJECT (object)
  GTK_WIDGET (widget)
  GTK_OBJECT (object)
  GTK_SIGNAL_FUNC (function)
  GTK_CONTAINER (container)
  GTK_WINDOW (window)
  GTK_BOX (box)

Все они используются как аргументы функций. Вы видели их в примерах при простом использовании в декларациях функций.

Как вы можете увидеть ниже в иерархии классов, все GtkWidgets образованы из базового класса GObject. Это значит, что в любом месте где функция запрашивает виджет типа объект, вы можете использовать макрос G_OBJECT().

Пример:

g_signal_connect( G_OBJECT (button), "clicked",
                  G_CALLBACK (callback_function), callback_data);

Здесь кнопка преобразуется в объект, и выполняется преобразование указателя функции в обратный вызов.

Многие виджеты являются контейнерами. Если вы посмотрите в иерархии классов ниже, то заметите, что много виджетов происходят из контейнерного класса. Любой из этих виджетов может использоваться с макросом GTK_CONTAINER, чтобы передать их в функции, которые запрашивают контейнеры.

К сожалению невозможно рассказать о всех макросах в данном руководстве, поэтому я рекомендую вам изучить заголовочные файлы GTK (header files) или GTK API reference manual. Это может быть очень познавательно. Фактически можно понять как работают виджеты изучая декларации функций.


<<< Previous

Home

Next >>>

Table Packing Example

 

Widget Hierarchy




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Виджет - кнопка

Обычные кнопки

Вы уже много раз видели процесс создания виджетов. Это довольно просто. Есть несколько способов создавать кнопки. Вы можете использовать gtk_button_new_with_label() или gtk_button_new_with_mnemonic() чтобы создать кнопку с ярлыком, или используйте gtk_button_new_from_stock (), чтобы создать кнопку, содержащую изображение и текст. Или gtk_button_new(), чтобы создать чистую кнопку, в которую вы сможете упаковать ярлык или pixmap(картинку). Для этого нужно создать контейнер, упаковать в этот контейнер необходимый объект используя gtk_box_pack_start(), и с помощью gtk_container_add() упаковать контейнер в кнопку.

Вот пример мспользования gtk_button_new(), для создания кнопки с изображением и текстом внутри. Я отделил код создания контейнера от остального, вы можете поступать также в своих программах. Дальше в этом руководстве будут приведены ещё примеры использования изображений.

#include <stdlib.h>
#include <gtk/gtk.h>
/* Создаём новый контейнер hbox содержащий изображение и текст 
 * и возвращаем контейнер. */
GtkWidget *xpm_label_box( gchar     *xpm_filename,
                          gchar     *label_text )
{
    GtkWidget *box;
    GtkWidget *label;
    GtkWidget *image;
    /* Создаём контейнер для изображения и текста */
    box = gtk_hbox_new (FALSE, 0);
    gtk_container_set_border_width (GTK_CONTAINER (box), 2);
    /* Определяем файл с изображением */
    image = gtk_image_new_from_file (xpm_filename);
    /* Создаём ярлык для кнопки */
    label = gtk_label_new (label_text);
    /* Упаковываем рисунок и текст в контейнер */
    gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 3);
    gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 3);
    gtk_widget_show (image);
    gtk_widget_show (label);
    return box;
}
/* Обычная функция вызова */
void callback( GtkWidget *widget,
               gpointer   data )
{
    g_print ("Hello again - %s was pressed\n", (char *) data);
}
int main( int   argc,
          char *argv[] )
{
    /* GtkWidget является типом хранения для виджетов */
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *box;
    gtk_init (&argc, &argv);
    /* Создаём новое окно */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
    /* Хорошая идея сделать это для всех окон. */
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (gtk_main_quit), NULL);
    g_signal_connect (G_OBJECT (window), "delete_event",
                      G_CALLBACK (gtk_main_quit), NULL);
    /* Устанавливаем окантовку для окна. */
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    /* Создаём новую кнопку */
    button = gtk_button_new ();
    /* Соединяем сигнал нажатия "clicked" кнопки с обратным вызовом */
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (callback), (gpointer) "cool button");
    /* Вызов функции для создания контейнера */
    box = xpm_label_box ("info.xpm", "cool button");
    /* Упаковываем и отображаем все виджеты */
    gtk_widget_show (box);
    gtk_container_add (GTK_CONTAINER (button), box);
    gtk_widget_show (button);
    gtk_container_add (GTK_CONTAINER (window), button);
    gtk_widget_show (window);
    /* Переходим в состояние ожидания событий! */
    gtk_main ();
    return 0;
}

Функция xpm_label_box() может быть использована для упаковки изображений и надписей в любой виджет способный выступать в роли контейнера.

Виджет кнопка, имеет следующие сигналы:


<<< Previous

Home

Next >>>

Widgets Without Windows

 

Toggle Buttons




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Начало

Первое что нужно сделать, это конечно скачать и установить GTK. Вы всегда можете найти последнюю версию на ftp.gtk.org. Вы также можете посмотреть информацию об исходном коде GTK на http://www.gtk.org/. GTK использует GNU autoconf для конфигурации. Для просмотра справки наберите ./configure --help.

Исходный код GTK содержит все примеры приведенные в данном руководстве, а также Makefiles для упрощенной компиляции.

Для начала мы рассмотрим пример самой простой программы на GTK. Эта программа создает окно размером 200x200 pixel и не совершает никаких действий кроме как выход после ввода соответствующей команды shell.

#include <gtk/gtk.h>
int main( int   argc,
          char *argv[] )
{
    GtkWidget *window;
    
    gtk_init (&argc, &argv);
    
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_show  (window);
    
    gtk_main ();
    
    return 0;
}

Вы можете скомпилировать программу используя компилятор gcc:

gcc base.c -o base `pkg-config --cflags --libs gtk+-2.0`

О возможных вариантах компиляции объясняется ниже в Компиляция Hello World.

Все программы будут конечно включать заголовочный файл gtk/gtk.h, который объявляет переменные, функции, структуры, и т.д. использующиеся в вашей GTK программе.

Следующая строка:

gtk_init(&argc, &argv);

вызов функции gtk_init(gint *argc, gchar ***argv) которая используется во всех GTK программах. Она создает установки визуальной и цветовой карты, а также запускает процесс вызова функции gdk_init(gint *argc, gchar ***argv). Эта функция инициализирует библиотеки для использования, устанавливает по умолчанию обработчики сигналов, и проверяет аргументы командной строки вашей программы, производя поиск одного из следующих:

Проходя по списку происходит поиск объявленных аргументов вашей программы для дальнейшего разбора или игнорирования. Так создаётся ряд стандартных аргументов для всех GTK программ.

Следующие две строчки кода выводят на дисплей окно программы.

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_show (window);

Аргумент GTK_WINDOW_TOPLEVEL определяет специфику расположения и оформления окна в менеджере окон. Чтобы не создавалось окно размером 0x0, по умолчанию принят размер 200x200 для всех дочерних окон, вы также можете манипулировать этими значениями.

Функция gtk_widget_show() позволяет GTK понять какие параметры мы установили для виджетов и правильно отобразить их.

Последняя строка вводит в главный цикл GTK процесса (main processing loop).

  gtk_main ();

gtk_main() ещё один вызов который используется в любой программе GTK. Когда программа доходит до этого вызова, GTK начинает ожидать X события (такого как нажатие кнопки мыши или клавиатуры), прерывания, или файлового ввода вывода. Однако в нашем простом примере никаких событий не ожидается.

Hello World в GTK

Еще одна программа в виде окна с кнопкой. Это классический "hello world" с использованием GTK.

#include <gtk/gtk.h>
/* Это функция обратного вызова. Аргументы в данном
 * примере отсутствуют. 
 * Подробней об обратных вызовах будет сказано ниже. */
void hello( GtkWidget *widget,
            gpointer   data )
{
    g_print ("Hello World\n");
}
gint delete_event( GtkWidget *widget,
                   GdkEvent  *event,
                   gpointer   data )
{
    /* Если вы вернете FALSE в "delete_event" обработчик сигналов,
     * GTK создаст "destroy" разрушающий сигнал. Возвращенный TRUE говорит
     * о нежелании закрывать окно.
     * Это полезно при всплывающих типах диалогов 
     *  'вы уверены что хотите выйти?'*/
    g_print ("delete event occurred\n");
    /* Измените TRUE на FALSE и главное окно будет закрыто
     * с "delete_event". */
    return TRUE;
}
/* Другой вызов */
void destroy( GtkWidget *widget,
              gpointer   data )
{
    gtk_main_quit ();
}
int main( int   argc,
          char *argv[] )
{
    /* GtkWidget является типом хранения для виджетов */
    GtkWidget *window;
    GtkWidget *button;
    
    /* Этот вызов используется во всех GTK программах. Аргументы из командной строки
     *   разбираются и возвращаются в приложение. */
    gtk_init (&argc, &argv);
    
    /* создается новое окно */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    /* Когда окну поступает "delete_event" сигнал (это делает
     * менеджер окон, обычно "close" опция в верхнем заголовке окна),
     * задаётся вопрос delete_event() функции, как обсуждалось выше.
     * Данные поступают в функцию обратного вызова являясь
     * NULL и игнорируются. */
    g_signal_connect (G_OBJECT (window), "delete_event",
                      G_CALLBACK (delete_event), NULL);
    
    /* Здесь мы соединяем "destroy" событие с обработчиком сигнала.  
     * Это делается вызовом gtk_widget_destroy() в окне,
     * или если возвращаем FALSE в обратном вызове "delete_event". */
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (destroy), NULL);
    
    /* Устанавливаем ширину окантовки окна */
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    
    /* Создаем новую кнопку с ярлыком "Hello World". */
    button = gtk_button_new_with_label ("Hello World");
    
    /* Когда кнопка получит "clicked" сигнал, то вызовет
     * функцию hello() с NULL аргументом. Функция hello()
     * определена выше. */
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (hello), NULL);
    
    /* Это закроет окно вызвав
     * gtk_widget_destroy(window) когда поступит "clicked". Снова сигнал 
     * закрытия мог поступить от сюда, или от менеджера окон. */
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (gtk_widget_destroy),
                              G_OBJECT (window));
    
    /* Помещаем кнопку внутрь окна */
    gtk_container_add (GTK_CONTAINER (window), button);
    
    /* Последний этап, вывод кнопки на экран. */
    gtk_widget_show (button);
    
    /* Вывод на экран окна. */
    gtk_widget_show (window);
    
    /* Все GTK приложения содержат gtk_main(). Здесь заканчивается контроль
     * и происходит ожидание(нажатие клавиши или мышки) */
    gtk_main ();
    
    return 0;
}

<<< Previous

Home

Next >>>

Introduction

 

Compiling Hello World




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Регуляторы (Adjustments)

GTK имеет различные виджеты для регулировки диапазона с использованием мыши или клавиатуры, они обсуждаются в секции Range Widgets. Есть также несколько виджетов для регулирования текстовых или информационных данных выходящих за рамки окна отображения, для вывода данных определёнными порциями.

Очевидно, что приложение должно уметь реагировать на действия пользователя связанные с виджетами регулировки. Один из способов добиться этого, заставить виджет создавать сигнал определенного типа в момент изменения регулировок, перехватывая этот сигнал с помощью обработчика или контролировать состояние значения в структуре данных виджета. Вы также можете соединить несколько регуляторов вместе, чтобы регулировки одного отражались на других. Самый очевидный пример такого соединения, это регулировочная планка (scrollbar) в области просмотра (viewport) или прокрутка области текста. Если каждый виджет имеет собственное значение регулировки, то программисту потребуется  написать для каждого свой собственный обработчик сигнала, для того, чтобы различать сигнал выхода одного от сигнала входа другого.

GTK решает эту проблему используя регулировочный объект (Adjustment object), это не виджет а метод передачи и хранения информации о регулировании в абстрактной и гибкой форме. Самое очевидное использование регулировочного объекта (Adjustment), это сохранение конфигурации параметров значения виджетов регулирования диапазона, таких как планка прокрутки (scrollbars) и  скользящий регулятор (scale controls). Однако, так как Регуляторы (Adjustments) получены из объектов (Object), они имеют некоторые специальные возможности в отличие от нормальных структурных данных. Самое главное, они могут создавать сигналы как обычные виджеты и эти сигналы могут не только использоваться вашей программой для реакции на ваш ввод или вывод, но и прозрачно распространяться между регулировочными виджетами.

Вы можете посмотреть сходство регуляторов с другими виджетами: Progress Bars, Viewports, Scrolled Windows, и т.д.

Создание регуляторов (Adjustment)

Многие виджеты используют объекты регулирования созданные автоматически, но в некоторых случаях, показанных позже, требуется самостоятельное создание виджетов. Пример создания:

GtkObject *gtk_adjustment_new( gdouble value,
                               gdouble lower,
                               gdouble upper,
                               gdouble step_increment,
                               gdouble page_increment,
                               gdouble page_size );

Аргумент value используется для установки по умолчанию значения положения регулятора, обычно это позиция "самый верхний" или "крайний левый". Аргумент lower определяет самое нижнее значение регулятора. Step_increment аргумент определяет "меньшие" из двух приращений, которыми пользователь может изменить значение, в то время как page_increment - "больший". Аргумент page_size обычно устанавливает видимую площадь для панорамного виджета. Аргумент upper используется для предоставления самых нижних или крайних справа координат дочернему панорамному виджету. Это не всегда самое меньшее значение value так как в этих виджетах page_size обычно не равно нулю.


<<< Previous

Home

Next >>>

Radio Buttons

 

Using Adjustments the Easy Way




GTK+ 2.0 Tutorial

<<< Previous

Next >>>


Виджеты регулировки диапазона

Категория виджетов диапазона включает вездесущий scrollbar (полоса прокрутки) и менее используемый scale widget (виджет масштабирования). Хотя эти два виджета используются для абсолютно разных целей, функционально они очень похожи. Все виджеты диапазона разделены на ряд графических элементов, каждый из которых имеет собственное окно и собственное событие оконной системы. Они все содержат "колею" ("trough") и "ползунок" ("slider") (их иногда называют "координатный манипулятор" "thumbwheel" в других средах разработки GUI). Перемещение "ползунка" указателем мыши допустимо в пределах колеи, щелчок мыши в любом месте колеи заставляет "ползунок" перемещаться в направлении щелчка на заранее определенное растояние.

Как упомянуто в главе о Регуляторах выше, все виджеты диапазона связаны с объектом регулирования, от которого они получают длину "ползунка" и его положение в пределах "колеи". Когда пользователь манипулирует ползунком, виджет диапазона изменяет значение регулировки.

Полоса прокрутки (Scrollbar Widgets)

Полоса прокрутки чаще всего используется с такими виджетами как списки (list), текстовые контейнеры (text box), или область просмотра (viewport) (и в стандартных оконных виджетах лучше всего использовать "прокручивание"). Для других целей лучше использовать виджеты масштабирования, они дружественны и более функциональны (featureful).

Есть отдельные типы для горизонтального и вертикального прокручивания (scrollbars). Они создаются при помощи отдельных функций:

GtkWidget *gtk_hscrollbar_new( GtkAdjustment *adjustment );
GtkWidget *gtk_vscrollbar_new( GtkAdjustment *adjustment );

 Аргумент adjustment может быть указателем  на Регулятор, или NULL, в зависимости от вашего выбора. Пустое значение используется в тех случаях, когда вновь созданный регулятор проходит через конструктор функции другого виджета который сконфигурирует его за вас, например текстовый виджет.


<<< Previous

Home

Next >>>

Adjustment Internals

 

Scale Widgets




GTK+ 2.0 Tutorial

<<< Previous

Разные виджеты (Miscellaneous Widgets)

Next >>>


Разные виджеты

Ярлыки

Ярлыки в GTK используются часто и они достаточно просты. Ярлыки не создают никаких сигналов ассоциированных с X window. Если вы хотите перехватить сигнал, то поместите ярлык в EventBox widget или Button widget.

Для создания нового ярлыка воспользуйтесь:

GtkWidget *gtk_label_new( const char *str );
GtkWidget *gtk_label_new_with_mnemonic( const char *str );

Единственным параметром является строка для отображения.

Для изменения текста ярлыка после создания, воспользуйтесь функцией:

void gtk_label_set_text( GtkLabel   *label,
                         const char *str );

Первый аргумент - предварительно созданный ярлык (приведение с использованием макрокоманды GTK_LABEL()), а второй - новая строка.

Пространство, необходимое для новой строки будет автоматически откорректировано если нужно. Вы можете использовать многострочные ярлыки, помещая в конец каждой строки знак перевода строки.

Для нахождения текущей строки, используйте:

const gchar* gtk_label_get_text( GtkLabel  *label );                    

Не делайте пустой возвращённую строку, это внутреннее использование GTK.

Текст ярлыка выравнивается с помощью:

void gtk_label_set_justify( GtkLabel         *label,
                            GtkJustification  jtype );

Значение jtype:

        GTK_JUSTIFY_LEFT
        GTK_JUSTIFY_RIGHT
        GTK_JUSTIFY_CENTER (the default)
        GTK_JUSTIFY_FILL

Виджет ярлык может автоматически создавать оболочку для линии текста. Это активизируется так:

void gtk_label_set_line_wrap (GtkLabel *label,
                              gboolean  wrap);

Аргумент wrap принимает значение TRUE или FALSE.

Если вы хотите подчеркнуть ваш ярлык, то вы можете установить образец:

void       gtk_label_set_pattern   (GtkLabel          *label,
                                    const gchar       *pattern);

Аргумент pattern указывает как должно выглядеть подчеркивание. Оно может состоять из символов подчеркивания и пробелов. Каждый символ подчеркивается согласно образцу. Например, строка "__ __" подчеркнула бы первые два символа и последние.

Note

Если вы просто хотите иметь подчеркнутый акселератор ("мнемонический") в вашем ярлыке, вы должны использовать gtk_label_new_with_mnemonic () или gtk_label_set_text_with_mnemonic (), а не gtk_label_set_pattern ().

Ниже дан короткий пример для иллюстрации этих функций. Этот пример использует виджеты рамки для лучшего стиля оформления ярлыков. Сейчас вы можете проигнорировать их,  но позже к ним прийдется вернуться в секции Frame.

В GTK + 2.0, тексты ярлыков могут содержать разметку для шрифта и другие атрибуты изменения текста, а также текст ярлыка может быть выделен (для копирования и вставки). Эти расширенные особенности здесь объясняться не будут.

#include <gtk/gtk.h>
int main( int   argc,
          char *argv[] )
{
  static GtkWidget *window = NULL;
  GtkWidget *hbox;
  GtkWidget *vbox;
  GtkWidget *frame;
  GtkWidget *label;
  /* Инициализируем GTK */
  gtk_init (&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit),
                    NULL);
  gtk_window_set_title (GTK_WINDOW (window), "Label");
  vbox = gtk_vbox_new (FALSE, 5);
  hbox = gtk_hbox_new (FALSE, 5);
  gtk_container_add (GTK_CONTAINER (window), hbox);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (window), 5);
  
  frame = gtk_frame_new ("Обычный ярлык");
  label = gtk_label_new ("Это обычный ярлык");
  gtk_container_add (GTK_CONTAINER (frame), label);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  
  frame = gtk_frame_new ("Многострочный ярлык");
  label = gtk_label_new ("Это многострочный ярлык.\nВторая строка\n" \
                         "Третья строка");
  gtk_container_add (GTK_CONTAINER (frame), label);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  
  frame = gtk_frame_new ("Ярлык с левым выравниванием");
  label = gtk_label_new ("Это многострочный\n" \
                         "ярлык с левым выравниванием.\nТретья      линия");
  gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
  gtk_container_add (GTK_CONTAINER (frame), label);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  
  frame = gtk_frame_new ("Ярлык с правым выравниванием");
  label = gtk_label_new ("Это многострочный ярлык\nс правым выравниванием.\n\n" \
                         "Четвёртая линия, (j/k)");
  gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_RIGHT);
  gtk_container_add (GTK_CONTAINER (frame), label);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  vbox = gtk_vbox_new (FALSE, 5);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
  frame = gtk_frame_new ("Ярлык с обертыванием линии");
  label = gtk_label_new ("Это пример ярлыка с обернутой линией(line-wrapped label).  Это " \
                         "не должно повлиять на вывод            " /*большой пробел, чтобы проверить интервал */\
                         "ширина распределяется автоматически" \
                         " слова обернуты чтобы соответствовать  " \
                         "Пришло время для всех хороших людей занять " \
                         "свою сторону.  " \
                         "Шесть больных овец шестого шейха\n" \
                         "     Корректно поддерживаются многократные параграфы, " \
                         "и корректно добавляются "\
                         "дополнительные          пробелы. ");
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  gtk_container_add (GTK_CONTAINER (frame), label);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  
  frame = gtk_frame_new ("Заполненный, обернутый ярлык");
  label = gtk_label_new ("Это пример line-wrapped, заполненного ярлыка.  " \
                         "Эта строчка занимает "\
                         "всю ширину              отведенную для неё.  " \
                         "Это предложение доказывает  "\
                         "мою особенность.  Это другое предложение. "\
                         "Here comes the sun, do de do de do.\n"\
                         "    Это новый параграф.\n"\
                         "    Это еще один новый " \
                         "параграф .  Всё заканчивается, "\
                         "к сожалению.");
  gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_FILL);
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  gtk_container_add (GTK_CONTAINER (frame), label);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  
  frame = gtk_frame_new ("Подчеркнутый ярлык");
  label = gtk_label_new ("Эта строчка подчёркнута!\n"
                         "Эта строчка подчёркнута прерывистым способом");
  gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
  gtk_label_set_pattern (GTK_LABEL (label),
                         "________________________ _ _____ ____ _ ____ __    __ __ _____ __");
  gtk_container_add (GTK_CONTAINER (frame), label);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  
  gtk_widget_show_all (window);
  gtk_main ();
  
  return 0;
}

<<< Previous

Home

Next >>>

Example

 

Arrows




GTK+ 2.0 Tutorial

<<< Previous

Miscellaneous Widgets

Next >>>


Комбинированный виджет ввода

Комбинированный ввод - ещё один довольно простой виджет, который реально является комбинацией двух других виджетов. Для пользователя этот виджет выглядит как поле для ввода текста и выпадающее меню содержащее возможные варианты ввода. Альтернативно, пользователь может вводить в список выбора дополнительные варианты.

В следующей структуре представлены компоненты комбинированного ввода:

struct _GtkCombo { 
        GtkHBox hbox; 
        GtkWidget *entry; 
        GtkWidget *button;
        GtkWidget *popup; 
        GtkWidget *popwin; 
        GtkWidget *list;
        ...  };

Комбинированный ввод имеет две основные части: entry и list.

Создаём комбинированный ввод:

GtkWidget *gtk_combo_new( void );

Теперь, при непосредственном манипулировании entry, создаём список доступных значений для ввода, выпадающий из строки ввода:

    gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (combo)->entry), "My String.");

Задаём значение для каждой строки выпадающего списка:

void gtk_combo_set_popdown_strings( GtkCombo *combo,
                                    GList    *strings );

Прежде всего вы должны собрать строки в GList. GList - связанное выполнение списка, входит в состав GLib и поддерживается библиотекой GTK. При беглом рассмотрении это выглядит так, вы создаёте указатель на GList равный NULL, затем прикладываете перечень:

GList *g_list_append( GList *glist, 
                      gpointer data );

Важно инициализировать указатель GList равным NULL. Значение возвращённое из функции g_list_append() должно использоваться как новый указатель на GList.

Пример создания списка вариантов:

    GList *glist = NULL;
    glist = g_list_append (glist, "String 1");
    glist = g_list_append (glist, "String 2");
    glist = g_list_append (glist, "String 3"); 
    glist = g_list_append (glist, "String 4");
    gtk_combo_set_popdown_strings (GTK_COMBO (combo), glist);
    
    /* glist освобождается, combo забирает копии */

Виджет combo создаёт копии строк переданных в структуру glist. Как результат, вы можете освободить память занимаемую списком если ваше приложение нуждается в этом.

В этом пункте рассматривается работающий комбинированный ввод. Есть несколько аспектов которые вы можете изменять. Это делается функциями:

void gtk_combo_set_use_arrows( GtkCombo *combo,
                               gboolean  val );
void gtk_combo_set_use_arrows_always( GtkCombo *combo,
                                      gboolean  val );
void gtk_combo_set_case_sensitive( GtkCombo *combo,
                                   gboolean  val );

gtk_combo_set_use_arrows() позволяет пользователю изменять значение ввода используя клавиши стрелки up/down. Сам список не поднимается, а скорее заменяет текущее значение ввода на следующее по списку (вверх или вниз, как указывает выбор вашей клавиши). Обычно в окне ввода клавиши стрелки используются для смещения фокуса (вы можете также использовать TAB). Заметьте, что при достижении последнего значения в списке и нажатии кнопки вниз происходит перемещение фокуса на самый верх для последующего продвижения, тоже самое происходит и в обратном направлении.

Если текущее значение во входе не находится в списке, то функция gtk_combo_set_use_arrows() - отключена.

gtk_combo_set_use_arrows_always() - также позволяет циклически проходить по списку выбора вверх или вниз используя клавиши стрелки, за исключением того, что полностью отключает использование клавиш  up и down для смещения фокуса.

gtk_combo_set_case_sensitive() - переключает GTK на поиск чувствительный или нет к регистру. Это используется когда нужно найти текущее значение строки ввода в списке значений.  Окончание может быть выполнено или чувствительным к регистру или нечувствительным способом, в зависимости от использования этой функции. Combo widget может просто прекратить текущий ввод используя комбинацию клавиш MOD-1 и "Tab". MOD-1 тоже самое что и клавиша "Alt".  Отметьте однако, что некоторые оконные менеджеры, также используют эту ключевую комбинацию, которая отменит ее использование в пределах GTK.

Теперь, когда мы имеем поле со списком, приспособленное для просмотра и действий, как мы этого хотим, все, что остается, это получить данные для поля со списком. В большинстве случаев, всё что вам нужно для получения данных - это ввод.  К вводу обращаются просто GTK_ENTRY (GTK_COMBO (combo)->entry). Два основных действия, которые вы захотите выполнять - соединяются с активизирующимся сигналом, который указывает, что пользователь нажал Return или ENTER клавишу и прочёл текст.

Первое делается так:

    g_signal_connect (G_OBJECT (GTK_COMBO (combo)->entry), "activate",
                      G_CALLBACK (my_callback_function), (gpointer) my_data);

Получение текста в любое произвольное время достигается, просто функцией ввода:

        gchar *gtk_entry_get_text( GtkEntry *entry );

Как нибудь так:

    gchar *string;
    string = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (combo)->entry));

Есть ещё функция:

void gtk_combo_disable_activate( GtkCombo *combo );

она отключает сигнал активизации в поле ввода в виджете комбинированного ввода, непонятно при каких обстоятельствах это может пригодится, но тем не менее эта функция существует.


<<< Previous

Home

Next >>>

Spin Buttons

Up

Calendar




GTK+ 2.0 Tutorial

<<< Previous

Разные виджеты (Miscellaneous Widgets)

Next >>>


Календарь

Виджет Calendar - эффективный способ работы с датами в помесячном формате информации. Это очень легкий в создании и работе виджет.

Создание GtkCalendar:

GtkWidget *gtk_calendar_new( void );

Следующие функции позволяют вносить многократные изменения в виджет Календарь производя незаметные для пользователя обновления.

void gtk_calendar_freeze( GtkCalendar *Calendar );
void gtk_calendar_thaw( GtkCalendar *Calendar );

Они работают точно также как и функции freeze/thaw в других виджетах.

Виджет Календарь имеет несколько опций позволяющих вам менять внешний вид виджета. Они используются в следующей функции:

void gtk_calendar_display_options( GtkCalendar               *calendar,
                                   GtkCalendarDisplayOptions  flags );

Аргумент flags может быть скомбинирован из пяти значений, используя операцию логическое ИЛИ (|):

GTK_CALENDAR_SHOW_HEADING
эта опция определяет, что нужно отобразить месяц и год при отрисовки календаря.
GTK_CALENDAR_SHOW_DAY_NAMES
эта функция позволяет отображать дни недели тремя буквами ( Mon,Tue, etc.).
GTK_CALENDAR_NO_MONTH_CHANGE
эта опция определяет, что пользователь не должен и неможет изменить в настоящее время отображаемый месяц. Это может быть полезным, если вы хотите отобразить 12 календарных помесячных виджетов в отдельном году.
GTK_CALENDAR_SHOW_WEEK_NUMBERS
эта опция позволяет отображать номер недели с левой стороны виджета. (eg. Jan 1 = Week 1,Dec 31 = Week 52).
GTK_CALENDAR_WEEK_START_MONDAY
эта опция изменяет начало недели на понедельник, тогда как по умолчанию неделя начинается с воскресенья.

Следующие функции используются, чтобы установить дату отображаемую в настоящее время:

gint gtk_calendar_select_month( GtkCalendar *calendar, 
                                guint        month,
                                guint        year );
void gtk_calendar_select_day( GtkCalendar *calendar,
                              guint        day );

Возвращаемое значение из gtk_calendar_select_month() - булево значение, указывающее, был ли выбор успешен. С помощью gtk_calendar_select_day() выбирается указанное число дней в течение текущего месяца, если это возможно.  Значение дня 0 снимет любое текущее выделение.

В дополнение к выбору дня, любой день может быть помечен "marked". Помеченный день подсвечивается в пределах календарного месяца. Следующая функция позволяет манипулировать с маркировками дней:

gint gtk_calendar_mark_day( GtkCalendar *calendar,
                            guint        day);
gint gtk_calendar_unmark_day( GtkCalendar *calendar,
                              guint        day);
void gtk_calendar_clear_marks( GtkCalendar *calendar);

В настоящее время отмечаемые дни сохраняются в массив в пределах структуры GtkCalendar. Этот массив имеет длину в 31 элемент, чтобы проверить отмечен день или нет, нужно обратиться к соответствующему элементу массива (не забывайте что в C нумерация элементов массива начинается с 0 до n-1). 

Пример:

    GtkCalendar *calendar;
    calendar = gtk_calendar_new ();
    ...
    /* 7 день отмечен? */
    if (calendar->marked_date[7-1])
       /* день отмечен */

Заметьте, что эти отметки постоянны с изменением месяца и года.

Последняя функция виджета Календарь используется, чтобы возвращать выделенную текущую дату, месяц и/или год.

void gtk_calendar_get_date( GtkCalendar *calendar, 
                            guint       *year,
                            guint       *month,
                            guint       *day );

Эта функция требует, чтобы Вы передали адреса guint переменных, в которые будет помещен результат. Переданный NULL как значение, вернет соответственный результат.

Календарный виджет может генерировать множество сигналов, указывающих выбор даты и изменение. Названия этих сигналов говорят сами за себя:

Ниже приведен код который включает почти всё выше сказанное о виджете Календарь.

/*
 * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson, Mattias GrЖnlund
 * Copyright (C) 2000 Tony Gale
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <gtk/gtk.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#define DEF_PAD 10
#define DEF_PAD_SMALL 5
#define TM_YEAR_BASE 1900
typedef struct _CalendarData {
  GtkWidget *flag_checkboxes[5];
  gboolean  settings[5];
  gchar     *font;
  GtkWidget *font_dialog;
  GtkWidget *window;
  GtkWidget *prev2_sig;
  GtkWidget *prev_sig;
  GtkWidget *last_sig;
  GtkWidget *month;
} CalendarData;
enum {
  calendar_show_header,
  calendar_show_days,
  calendar_month_change, 
  calendar_show_week,
  calendar_monday_first
};
/*
 * GtkCalendar
 */
void calendar_date_to_string( CalendarData *data,
                              char         *buffer,
                              gint          buff_len )
{
  struct tm tm;
  time_t time;
  memset (&tm, 0, sizeof (tm));
  gtk_calendar_get_date (GTK_CALENDAR (data->window),
                         &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
  tm.tm_year -= TM_YEAR_BASE;
  time = mktime (&tm);
  strftime (buffer, buff_len-1, "%x", gmtime (&time));
}
void calendar_set_signal_strings( char         *sig_str,
                                  CalendarData *data)
{
  const gchar *prev_sig;
  prev_sig = gtk_label_get_text (GTK_LABEL (data->prev_sig));
  gtk_label_set_text (GTK_LABEL (data->prev2_sig), prev_sig);
  prev_sig = gtk_label_get_text (GTK_LABEL (data->last_sig));
  gtk_label_set_text (GTK_LABEL (data->prev_sig), prev_sig);
  gtk_label_set_text (GTK_LABEL (data->last_sig), sig_str);
}
void calendar_month_changed( GtkWidget    *widget,
                             CalendarData *data )
{
  char buffer[256] = "month_changed: ";
  calendar_date_to_string (data, buffer+15, 256-15);
  calendar_set_signal_strings (buffer, data);
}
void calendar_day_selected( GtkWidget    *widget,
                            CalendarData *data )
{
  char buffer[256] = "day_selected: ";
  calendar_date_to_string (data, buffer+14, 256-14);
  calendar_set_signal_strings (buffer, data);
}
void calendar_day_selected_double_click( GtkWidget    *widget,
                                         CalendarData *data )
{
  struct tm tm;
  char buffer[256] = "day_selected_double_click: ";
  calendar_date_to_string (data, buffer+27, 256-27);
  calendar_set_signal_strings (buffer, data);
  memset (&tm, 0, sizeof (tm));
  gtk_calendar_get_date (GTK_CALENDAR (data->window),
                         &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
  tm.tm_year -= TM_YEAR_BASE;
  if (GTK_CALENDAR (data->window)->marked_date[tm.tm_mday-1] == 0) 
    {
      gtk_calendar_mark_day (GTK_CALENDAR (data->window), tm.tm_mday);
    } 
  else 
    { 
      gtk_calendar_unmark_day (GTK_CALENDAR (data->window), tm.tm_mday);
    }
}
void calendar_prev_month( GtkWidget    *widget,
                            CalendarData *data )
{
  char buffer[256] = "prev_month: ";
  calendar_date_to_string (data, buffer+12, 256-12);
  calendar_set_signal_strings (buffer, data);
}
void calendar_next_month( GtkWidget    *widget,
                            CalendarData *data )
{
  char buffer[256] = "next_month: ";
  calendar_date_to_string (data, buffer+12, 256-12);
  calendar_set_signal_strings (buffer, data);
}
void calendar_prev_year( GtkWidget    *widget,
                            CalendarData *data )
{
  char buffer[256] = "prev_year: ";
  calendar_date_to_string (data, buffer+11, 256-11);
  calendar_set_signal_strings (buffer, data);
}
void calendar_next_year( GtkWidget    *widget,
                            CalendarData *data )
{
  char buffer[256] = "next_year: ";
  calendar_date_to_string (data, buffer+11, 256-11);
  calendar_set_signal_strings (buffer, data);
}
void calendar_set_flags( CalendarData *calendar )
{
  gint i;
  gint options = 0;
  for (i = 0; i < 5; i++) 
    if (calendar->settings[i])
      {
        options=options + (1<<i);
      }
  if (calendar->window)
    gtk_calendar_display_options (GTK_CALENDAR (calendar->window), options);
}
void calendar_toggle_flag( GtkWidget    *toggle,
                           CalendarData *calendar )
{
  gint i;
  gint j;
  j = 0;
  for (i = 0; i < 5; i++)
    if (calendar->flag_checkboxes[i] == toggle)
      j = i;
  calendar->settings[j] = !calendar->settings[j];
  calendar_set_flags (calendar);
  
}
void calendar_font_selection_ok( GtkWidget    *button,
                                 CalendarData *calendar )
{
  GtkStyle *style;
  PangoFontDescription *font_desc;
  calendar->font = gtk_font_selection_dialog_get_font_name (
                        GTK_FONT_SELECTION_DIALOG (calendar->font_dialog));
  if (calendar->window)
    {
      font_desc = pango_font_description_from_string (calendar->font);
      if (font_desc) 
        {
          style = gtk_style_copy (gtk_widget_get_style (calendar->window));
          style->font_desc = font_desc;
          gtk_widget_set_style (calendar->window, style);
        }
    }
}
void calendar_select_font( GtkWidget    *button,
                           CalendarData *calendar )
{
  GtkWidget *window;
  if (!calendar->font_dialog) {
    window = gtk_font_selection_dialog_new ("Font Selection Dialog");
    g_return_if_fail (GTK_IS_FONT_SELECTION_DIALOG (window));
    calendar->font_dialog = window;
    
    gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE);
    
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (gtk_widget_destroyed),
                      (gpointer) &calendar->font_dialog);
    
    g_signal_connect (G_OBJECT (GTK_FONT_SELECTION_DIALOG (window)->ok_button),
                      "clicked", G_CALLBACK (calendar_font_selection_ok),
                      (gpointer) calendar);
    g_signal_connect_swapped (G_OBJECT (GTK_FONT_SELECTION_DIALOG (window)->cancel_button),
                              "clicked",
                              G_CALLBACK (gtk_widget_destroy), 
                              G_OBJECT (calendar->font_dialog));
  }
  window=calendar->font_dialog;
  if (!GTK_WIDGET_VISIBLE (window))
    gtk_widget_show (window);
  else
    gtk_widget_destroy (window);
}
void create_calendar()
{
  GtkWidget *window;
  GtkWidget *vbox, *vbox2, *vbox3;
  GtkWidget *hbox;
  GtkWidget *hbbox;
  GtkWidget *calendar;
  GtkWidget *toggle;
  GtkWidget *button;
  GtkWidget *frame;
  GtkWidget *separator;
  GtkWidget *label;
  GtkWidget *bbox;
  static CalendarData calendar_data;
  gint i;
  
  struct {
    char *label;
  } flags[] =
    {
      { "Show Heading" },
      { "Show Day Names" },
      { "No Month Change" },
      { "Show Week Numbers" },
      { "Week Start Monday" }
    };
  
  calendar_data.window = NULL;
  calendar_data.font = NULL;
  calendar_data.font_dialog = NULL;
  for (i = 0; i < 5; i++) {
    calendar_data.settings[i] = 0;
  }
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "GtkCalendar Example");
  gtk_container_set_border_width (GTK_CONTAINER (window), 5);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit),
                    NULL);
  g_signal_connect (G_OBJECT (window), "delete-event",
                    G_CALLBACK (gtk_false),
                    NULL);
  gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
  vbox = gtk_vbox_new (FALSE, DEF_PAD);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  /*
   * Главная часть окна, Календарь, переключатели и выбор шрифта.
   */
  hbox = gtk_hbox_new (FALSE, DEF_PAD);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, DEF_PAD);
  hbbox = gtk_hbutton_box_new ();
  gtk_box_pack_start (GTK_BOX (hbox), hbbox, FALSE, FALSE, DEF_PAD);
  gtk_button_box_set_layout (GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_SPREAD);
  gtk_box_set_spacing (GTK_BOX (hbbox), 5);
  /* Calendar widget */
  frame = gtk_frame_new ("Calendar");
  gtk_box_pack_start (GTK_BOX (hbbox), frame, FALSE, TRUE, DEF_PAD);
  calendar=gtk_calendar_new ();
  calendar_data.window = calendar;
  calendar_set_flags (&calendar_data);
  gtk_calendar_mark_day (GTK_CALENDAR (calendar), 19);  
  gtk_container_add( GTK_CONTAINER (frame), calendar);
  g_signal_connect (G_OBJECT (calendar), "month_changed", 
                    G_CALLBACK (calendar_month_changed),
                    (gpointer) &calendar_data);
  g_signal_connect (G_OBJECT (calendar), "day_selected", 
                    G_CALLBACK (calendar_day_selected),
                    (gpointer) &calendar_data);
  g_signal_connect (G_OBJECT (calendar), "day_selected_double_click", 
                    G_CALLBACK (calendar_day_selected_double_click),
                    (gpointer) &calendar_data);
  g_signal_connect (G_OBJECT (calendar), "prev_month", 
                    G_CALLBACK (calendar_prev_month),
                    (gpointer) &calendar_data);
  g_signal_connect (G_OBJECT (calendar), "next_month", 
                    G_CALLBACK (calendar_next_month),
                    (gpointer) &calendar_data);
  g_signal_connect (G_OBJECT (calendar), "prev_year", 
                    G_CALLBACK (calendar_prev_year),
                    (gpointer) &calendar_data);
  g_signal_connect (G_OBJECT (calendar), "next_year", 
                    G_CALLBACK (calendar_next_year),
                    (gpointer) &calendar_data);
  separator = gtk_vseparator_new ();
  gtk_box_pack_start (GTK_BOX (hbox), separator, FALSE, TRUE, 0);
  vbox2 = gtk_vbox_new (FALSE, DEF_PAD);
  gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, DEF_PAD);
  
  /* Создаём рамку справа для размещения в ней переключателей */ 
  frame = gtk_frame_new ("Flags");
  gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, DEF_PAD);
  vbox3 = gtk_vbox_new (TRUE, DEF_PAD_SMALL);
  gtk_container_add (GTK_CONTAINER (frame), vbox3);
  for (i = 0; i < 5; i++)
    {
      toggle = gtk_check_button_new_with_label (flags[i].label);
      g_signal_connect (G_OBJECT (toggle),
                        "toggled",
                        G_CALLBACK (calendar_toggle_flag),
                        (gpointer) &calendar_data);
      gtk_box_pack_start (GTK_BOX (vbox3), toggle, TRUE, TRUE, 0);
      calendar_data.flag_checkboxes[i] = toggle;
    }
  /* Создаём правую кнопку выбора шрифта (font-button) */ 
  button = gtk_button_new_with_label ("Font...");
  g_signal_connect (G_OBJECT (button),
                    "clicked",
                    G_CALLBACK (calendar_select_font),
                    (gpointer) &calendar_data);
  gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
  /*
   *  Создаём сигналы и события (Build the Signal-event part).
   */
  frame = gtk_frame_new ("Signal events");
  gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, DEF_PAD);
  vbox2 = gtk_vbox_new (TRUE, DEF_PAD_SMALL);
  gtk_container_add (GTK_CONTAINER (frame), vbox2);
  
  hbox = gtk_hbox_new (FALSE, 3);
  gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, TRUE, 0);
  label = gtk_label_new ("Signal:");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
  calendar_data.last_sig = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (hbox), calendar_data.last_sig, FALSE, TRUE, 0);
  hbox = gtk_hbox_new (FALSE, 3);
  gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, TRUE, 0);
  label = gtk_label_new ("Previous signal:");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
  calendar_data.prev_sig = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (hbox), calendar_data.prev_sig, FALSE, TRUE, 0);
  hbox = gtk_hbox_new (FALSE, 3);
  gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, TRUE, 0);
  label = gtk_label_new ("Second previous signal:");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
  calendar_data.prev2_sig = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (hbox), calendar_data.prev2_sig, FALSE, TRUE, 0);
  bbox = gtk_hbutton_box_new ();
  gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
  gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
  button = gtk_button_new_with_label ("Close");
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (gtk_main_quit), 
                    NULL);
  gtk_container_add (GTK_CONTAINER (bbox), button);
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_widget_grab_default (button);
  gtk_widget_show_all (window);
}
int main(int   argc,
         char *argv[] )
{
  gtk_init (&argc, &argv);
  create_calendar ();
  gtk_main ();
  return 0;
}

<<< Previous

Home

Next >>>

Combo Box

Up

Color Selection




GTK+ 2.0 Tutorial

<<< Previous

Miscellaneous Widgets

Next >>>


Выбор цвета

Виджет выбора цвета является достаточно сложным и позволяет выбирать цвет манипулируя треугольником в цветовой схеме RGB (Red(красный), Green(зелёный), Blue(синий)) и HSV (Hue(оттенок), Saturation(насыщенность), Value(значение)). Цвет можно выбирать используя простые слайдеры или вращая треугольную область насыщенности и оттенка вокруг цветовой шкалы. Опционально может быть установлена прозрачность.

Виджет выбор цвета создаёт только один сигнал, "color_changed", он производится когда происходит изменение цвета пользователем, или через функцию gtk_color_selection_set_color().

Виджет имеет две особенности: GtkColorSelection и GtkColorSelectionDialog.

GtkWidget *gtk_color_selection_new( void );

Вам не прийдется использовать этот конструктор непосредственно. Это создаёт шаблонный виджет ColorSelection который вы сделаете основным самостоятельно. Виджет  ColorSelection наследует от виджета VBox.

GtkWidget *gtk_color_selection_dialog_new( const gchar *title );

Самый основной конструктор color selection. Это создаёт диалог ColorSelectionDialog. Он состоит из рамки содержащей виджет ColorSelection, HSeparator и HBox с тремя кнопками, "Ok", "Cancel" и "Help". Вы можете получить доступ к этим кнопкам через виджеты "ok_button", "cancel_button" и "help_button" в структуре ColorSelectionDialog, (т,е, GTK_COLOR_SELECTION_DIALOG (colorseldialog)->ok_button)).

void gtk_color_selection_set_has_opacity_control( GtkColorSelection *colorsel,
                                                  gboolean           has_opacity );

Виджет выбор цвета поддерживает регулировку полупрозрачности (известную также как alpha channel), которая отключена по умолчанию. Вызов этой функции со значением аргумента has_opacity установленным в TRUE включает полупрозрачность. Соответственно значение FALSE отключает полупрозрачность.

void gtk_color_selection_set_current_color( GtkColorSelection *colorsel,
                                            GdkColor          *color );
void gtk_color_selection_set_current_alpha( GtkColorSelection *colorsel,
                                            guint16            alpha );

Вы можете установить текущий цвет явно, вызвав gtk_color_selection_set_current_color() с указателем на GdkColor. Установка полупрозрачности (alpha channel) делается с помощью gtk_color_selection_set_current_alpha(). Значение alpha должно быть между 0 (полностью прозрачно) и 65636 (полностью непрозрачно).

void gtk_color_selection_get_current_color( GtkColorSelection *colorsel,
                                            GdkColor *color );
void gtk_color_selection_get_current_alpha( GtkColorSelection *colorsel,
                                            guint16           *alpha );

Эти функции используются когда вам нужно получить текущее значение цвета при получении сигнала "color_changed".

Вот - простой пример, демонстрирующий использование ColorSelectionDialog.  Программа отображает окно, содержащее область рисунка.  Нажатие на кнопку открывает диалог выбора цвета и изменение цвета в диалоге выбора цвета, изменяет цвет фона самой программы.

#include <glib.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>
GtkWidget *colorseldlg = NULL;
GtkWidget *drawingarea = NULL;
GdkColor color;
/* Обработчик изменения цвета (Color changed handler) */
void color_changed_cb( GtkWidget         *widget,
                       GtkColorSelection *colorsel )
{
  GdkColor ncolor;
  gtk_color_selection_get_current_color (colorsel, &ncolor);
  gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &ncolor);       
}
/* Обработчик событий полотна (Drawingarea event handler) */
gint area_event( GtkWidget *widget,
                 GdkEvent  *event,
                 gpointer   client_data )
{
  gint handled = FALSE;
  gint response;
  GtkColorSelection *colorsel;
  /* Проверка события нажатой кнопки */
  if (event->type == GDK_BUTTON_PRESS)
    {
      handled = TRUE;
       /* Создаём диалог выбора цвета */
      if (colorseldlg == NULL)
        colorseldlg = gtk_color_selection_dialog_new ("Select background color");
      /* Получаем ColorSelection виджет */
      colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (colorseldlg)->colorsel);
      gtk_color_selection_set_previous_color (colorsel, &color);
      gtk_color_selection_set_current_color (colorsel, &color);
      gtk_color_selection_set_has_palette (colorsel, TRUE);
      /* Подключаем сигнал "color_changed", устанавливаем client-data
       * в виджет colorsel */
      g_signal_connect (G_OBJECT (colorsel), "color_changed",
                        G_CALLBACK (color_changed_cb), (gpointer) colorsel);
      /* Отображаем диалог */
      response = gtk_dialog_run (GTK_DIALOG (colorseldlg));
      if (response == GTK_RESPONSE_OK)
        gtk_color_selection_get_current_color (colorsel, &color);
      else 
        gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &color);
      gtk_widget_hide (colorseldlg);
    }
  return handled;
}
/* Выходим из обработчика */
gint destroy_window( GtkWidget *widget,
                     GdkEvent  *event,
                     gpointer   client_data )
{
  gtk_main_quit ();
  return TRUE;
}
/* Основная функция */
gint main( gint   argc,
           gchar *argv[] )
{
  GtkWidget *window;
  /* Инициализируем GTK, удаляем gtk-related commandline мусор */
  gtk_init (&argc, &argv);
  /* Создаём главное окно, устанавливаем заголовок и политику поведения */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Color selection test");
  gtk_window_set_policy (GTK_WINDOW (window), TRUE, TRUE, TRUE);
  /* Подключаем "delete" и "destroy" события для выхода */
  g_signal_connect (GTK_OBJECT (window), "delete_event",
                    GTK_SIGNAL_FUNC (destroy_window), (gpointer) window);
  
  /* Создаём полотно, устанавливаем размер и перехват событий */
  drawingarea = gtk_drawing_area_new ();
  color.red = 0;
  color.blue = 65535;
  color.green = 0;
  gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &color);       
  gtk_widget_set_size_request (GTK_WIDGET (drawingarea), 200, 200);
  gtk_widget_set_events (drawingarea, GDK_BUTTON_PRESS_MASK);
  g_signal_connect (GTK_OBJECT (drawingarea), "event", 
                    GTK_SIGNAL_FUNC (area_event), (gpointer) drawingarea);
  
  /* Добавляем полотно в окно, отображаем оба */
  gtk_container_add (GTK_CONTAINER (window), drawingarea);
  gtk_widget_show (drawingarea);
  gtk_widget_show (window);
  
  /* Входим в gtk main цикл (никогда не возвращаемся) */
  gtk_main ();
  /* Удовлетворите сварливые компиляторы :)*/
  return 0;
}

<<< Previous

Home

Next >>>

Calendar

Up

File Selections




GTK+ 2.0 Tutorial

<<< Previous

Разные виджеты (Miscellaneous Widgets)

Next >>>


Выбор файлов

Виджет выбора файла - быстрый и простой способ показать диалоговое окно File. Окно полностью укомплектовано всем необходимым включая кнопки Ok и Cancel что позволяет сэкономить время при программировании.

Диалог выбора файлов создаётся функцией:

GtkWidget *gtk_file_selection_new( const gchar *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

Наиболее используемые указатели ok_button, cancel_button и help_button.

Модифицированный пример из testgtk.c.

#include <gtk/gtk.h>
/* Получаем выбранное имя файла и выводим на консоль */
void file_ok_sel( GtkWidget        *w,
                  GtkFileSelection *fs )
{
    g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
}
int main( int   argc,
          char *argv[] )
{
    GtkWidget *filew;
    gtk_init (&argc, &argv);
    /* Создаём новый виджет выбора файлов */
    filew = gtk_file_selection_new ("File selection");
    g_signal_connect (G_OBJECT (filew), "destroy",
                      G_CALLBACK (gtk_main_quit), NULL);
    /* Соединяем ok_button с file_ok_sel функцией */
    g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
                      "clicked", G_CALLBACK (file_ok_sel), (gpointer) filew);
    /* Соединяем cancel_button с закрытием виджета */
    g_signal_connect_swapped (G_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
                              "clicked", G_CALLBACK (gtk_widget_destroy),
                              G_OBJECT (filew));
    /* Устанавливаем имя файла по умолчанию */
                                     "penguin.png");
    gtk_widget_show (filew);
    gtk_main ();
    return 0;
}

<<< Previous

Home

Next >>>

Color Selection

Up

Container Widgets




GTK+ 2.0 Tutorial

<<< Previous

Container Widgets

Next >>>


Виджет Выравнивания

Виджет выравнивания позволяет вам помещать виджет в пределах его окна в позиции и размере относительно размера виджета Выравнивания непосредственно. Например, это может быть очень полезным чтобы сосредоточить виджет в пределах окна.

Есть только две функции, связанные с виджетом Выравнивания:

GtkWidget* gtk_alignment_new( gfloat xalign,
                              gfloat yalign,
                              gfloat xscale,
                              gfloat yscale );
void gtk_alignment_set( GtkAlignment *alignment,
                        gfloat        xalign,
                        gfloat        yalign,
                        gfloat        xscale,
                        gfloat        yscale );

Первая создаёт виджет выравнивания со специфическими параметрами. Вторая функция позволяет изменять эти параметры.

Все четыре параметра выравнивания десятичные числа, которые могут расположиться от 0.0 до 1.0. Параметры xalign и yalign создают позицию виджета, помещенного в пределах виджета Выравнивания. Параметры xscale и yscale определяют свободное пространство выделенное для виджета.

Дочерний виджет помещается в виджет выравнивания используя:

    gtk_container_add (GTK_CONTAINER (alignment), child_widget);

Пример виджета выравнивания можно посмотреть в Progress Bar widget.


<<< Previous

Home

Next >>>

Container Widgets

Up

Fixed Container




GTK+ 2.0 Tutorial

<<< Previous

Контейнерные виджеты (Container Widgets)

Next >>>


Неподвижный контейнер

Неподвижный контейнер позволяет помещать виджеты, неподвижно в пределах окна, в позиции относительно верхнего левого угла. Позиция виджетов может быть изменена динамически.

Немногие функции связаны с неподвижным виджетом:

GtkWidget* gtk_fixed_new( void );
void gtk_fixed_put( GtkFixed  *fixed,
                    GtkWidget *widget,
                    gint       x,
                    gint       y );
void gtk_fixed_move( GtkFixed  *fixed,
                     GtkWidget *widget,
                     gint       x,
                     gint       y );

Функция gtk_fixed_new() - создаёт неподвижный контейнер.

gtk_fixed_put() - помещает дочерний виджет в неподвижный контейнер, в позицию относительно  x и y.

gtk_fixed_move() - позволяет перемещать виджет в новую позицию.

void gtk_fixed_set_has_window( GtkFixed  *fixed,
                               gboolean   has_window );
gboolean gtk_fixed_get_has_window( GtkFixed *fixed );

Обычно, Неподвижные виджеты не имеют собственного окна X.  Так как это отличается от поведения Неподвижных виджетов в более ранних выпусках GTK, функция gtk_fixed_set_has_window() позволяет создание Неподвижных виджетов с их собственным окном.

Следующий пример демонстрирует использование неподвижных контейнеров (Fixed Container).

#include <gtk/gtk.h>
/* Будем использовать некоторые глобальные
 * переменные, чтобы хранить позицию виджета 
 * в пределах неподвижного контейнера */
gint x = 50;
gint y = 50;
/* Эта функция обратного вызова перемещает кнопку в новую позицию в Неподвижном контейнере. */
void move_button( GtkWidget *widget,
                  GtkWidget *fixed )
{
  x = (x + 30) % 300;
  y = (y + 50) % 300;
  gtk_fixed_move (GTK_FIXED (fixed), widget, x, y); 
}
int main( int   argc,
          char *argv[] )
{
  /* GtkWidget хранит типы виджетов */
  GtkWidget *window;
  GtkWidget *fixed;
  GtkWidget *button;
  gint i;
  /* Инициализируем GTK */
  gtk_init (&argc, &argv);
    
  /* Создаём новое окно */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Fixed Container");
  /* Подключаем событие "destroy" к обработчику сигналов */ 
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit), NULL);
 
  /* Определяем ширину окантовки окна. */
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  /* Создаём неподвижный контейнер */
  fixed = gtk_fixed_new ();
  gtk_container_add (GTK_CONTAINER (window), fixed);
  gtk_widget_show (fixed);
  
  for (i = 1 ; i <= 3 ; i++) {
    /* Создаём кнопку с ярлыком "Нажми меня" */
    button = gtk_button_new_with_label ("Нажми меня");
  
    /* Когда кнопка получает сигнал "clicked", то вызывается функция
     * move_button() с помещенным в неё в качестве аргумента
     * неподвижным контейнером. */
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (move_button), (gpointer) fixed);
  
    /* Упаковываем кнопку в неподвижный контейнер. */
    gtk_fixed_put (GTK_FIXED (fixed), button, i*50, i*50);
  
    /* Последний шаг - отображаем новый виджет. */
    gtk_widget_show (button);
  }
  /* Отображаем всё окно */
  gtk_widget_show (window);
    
  /* Входим в цикл */
  gtk_main ();
    
  return 0;
}

<<< Previous

Home

Next >>>

The Alignment widget

Up

Layout Container




GTK+ 2.0 Tutorial

<<< Previous

Container Widgets

Next >>>


Контейнер Размещения

Контейнер Размещения подобен Неподвижному контейнеру за исключением того, что он имеет большое количество (где бесконечность - меньше чем 2^32) прокручиваемой области. Система X window имеет ограничения, окна не могут превышать ширину или длину в 32767 pixels. Контейнер Размещения обходит это ограничение создавая экзотическое окно использования материала так, чтобы вы могли плавно прокручивать область даже когда в ней находится много дочерних виджетов.

Контейнер размещения создаётся так:

GtkWidget *gtk_layout_new( GtkAdjustment *hadjustment,
                           GtkAdjustment *vadjustment );

Как вы можете заметить, Контейнер Размещения использует специальные объекты регулирования для прокручивания области размещения.

Вы можете добавлять или удалять виджеты из контейнера размещения при помощи функций:

void gtk_layout_put( GtkLayout *layout,
                     GtkWidget *widget,
                     gint       x,
                     gint       y );
void gtk_layout_move( GtkLayout *layout,
                      GtkWidget *widget,
                      gint       x,
                      gint       y );

Функция для установки размера контейнера:

void gtk_layout_set_size( GtkLayout *layout,
                          guint      width,
                          guint      height );

Последние функции предназначены для манипулирования, по горизонтали или вертикали, виджетами размещенными в контейнере:

GtkAdjustment* gtk_layout_get_hadjustment( GtkLayout *layout );
GtkAdjustment* gtk_layout_get_vadjustment( GtkLayout *layout );
void gtk_layout_set_hadjustment( GtkLayout     *layout,
                                 GtkAdjustment *adjustment );
void gtk_layout_set_vadjustment( GtkLayout     *layout,
                                 GtkAdjustment *adjustment);

<<< Previous

Home

Next >>>

Fixed Container

Up

Frames




GTK+ 2.0 Tutorial

<<< Previous

Getting Started

Next >>>


Компиляция Hello World

Для компиляции воспользуйтесь командой:

gcc -Wall -g helloworld.c -o helloworld `pkg-config --cflags gtk+-2.0` \
`pkg-config --libs gtk+-2.0`

Здесь используется программа pkg-config, которую можно получить на www.freedesktop.org. Эта программа читает .pc которым комплектуется GTK для определения ключей компилятора необходимых для сборки. pkg-config --cflags gtk+-2.0 создаёт поток вывода в виде списка содержимого каталога компилятора, а pkg-config --libs gtk+-2.0 вывод списка найденных библиотек необходимых для связи (link) при компиляции. В выше приведенном примере команды могли быть объединены в одну, например
`pkg-config --cflags --libs gtk+-2.0
`.

Обратите внимание что данный тип команды существенен для компиляции.

Библиотеки которые обычно нужны для связки (linked):


<<< Previous

Home

Next >>>

Getting Started

Up

Theory of Signals and Callbacks




GTK+ 2.0 Tutorial

<<< Previous

Container Widgets

Next >>>


Рамки

Рамки могут использоваться для ограничения одного или группы виджетов с установкой произвольно маркированного поля. Позиция и стиль поля маркировки может изменяться.

Рамка создаётся так:

GtkWidget *gtk_frame_new( const gchar *label );

Поле маркировки по умолчанию располагается вверху слева. Значение NULL в качестве аргумента label, отключает отображение маркировки. Текст маркировки может быть изменён с помощью следующей функции.

void gtk_frame_set_label( GtkFrame    *frame,
                          const gchar *label );

Позиция поля маркировки изменяется так:

void gtk_frame_set_label_align( GtkFrame *frame,
                                gfloat    xalign,
                                gfloat    yalign );

xalign и yalign могут иметь значения между 0.0 и 1.0. Аргумент xalign указывает позицию по горизонтали на вершине рамки. yalign в настоящее время не используется.  Значение по умолчанию xalign - 0.0, это помещает метку в левом конце рамки.

Следующая функция изменяет стиль поля, которое используется, чтобы выделить рамку.

void gtk_frame_set_shadow_type( GtkFrame      *frame,
                                GtkShadowType  type);

Аргумент type может принимать одно из следующих значений:

  GTK_SHADOW_NONE
  GTK_SHADOW_IN
  GTK_SHADOW_OUT
  GTK_SHADOW_ETCHED_IN (по умолчанию)
  GTK_SHADOW_ETCHED_OUT

Следующий пример кода демонстрирует использование рамки.

#include <gtk/gtk.h>
int main( int   argc,
          char *argv[] )
{
  /* GtkWidget содержит типы виджетов */
  GtkWidget *window;
  GtkWidget *frame;
  /* Инициализируем GTK */
  gtk_init (&argc, &argv);
  /* Создаём новое окно */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Frame Example");
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit), NULL);
  gtk_widget_set_size_request (window, 300, 300);
  /* Устанавливаем окантовку окна. */
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  /* Создаём рамку */
  frame = gtk_frame_new (NULL);
  gtk_container_add (GTK_CONTAINER (window), frame);
  /* Устанавливаем маркировку рамки */
  gtk_frame_set_label (GTK_FRAME (frame), "GTK Frame Widget");
  /* Выравниваем маркировку справа */
  gtk_frame_set_label_align (GTK_FRAME (frame), 1.0, 0.0);
  /* Устанавливаем стиль рамки */
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
  gtk_widget_show (frame);
  /* Отображаем окно */
  gtk_widget_show (window);
  /* Входим в цикл */
  gtk_main ();
  return 0;
}

<<< Previous

Home

Next >>>

Layout Container

Up

Aspect Frames




GTK+ 2.0 Tutorial

<<< Previous

Container Widgets

Next >>>


Пропорциональная рамка

Пропорциональная рамка очень похожа на виджет рамка, за исключением того, что стороны виджета имеют определенную пропорцию соотношения ширины и высоты рамки. Это может пригодится при просмотре изображений.  Размер предварительного просмотра должен измениться, когда пользователь изменяет размеры окна, но соотношение сторон должно всегда соответствовать оригинальному изображению.

Создание аспект рамки:

GtkWidget *gtk_aspect_frame_new( const gchar *label,
                                 gfloat       xalign,
                                 gfloat       yalign,
                                 gfloat       ratio,
                                 gboolean     obey_child);

xalign и yalign определяют выравнивание как в Alignment widgets. Если obey_child равен TRUE, то пропорции дочернего виджета будут соответствовать коэффициенту сжатия идеального размера. Иначе будет использовано отношение сторон.

Установки можно изменить функцией:

void gtk_aspect_frame_set( GtkAspectFrame *aspect_frame,
                           gfloat          xalign,
                           gfloat          yalign,
                           gfloat          ratio,
                           gboolean        obey_child);

В следующем примере приведен код аспект рамки, имеющей соотношение сторон 2х1, при изменении размера окна меняется размер рамки, но не меняется соотношение сторон.

#include <gtk/gtk.h>
int main( int argc,
          char *argv[] )
{
    GtkWidget *window;
    GtkWidget *aspect_frame;
    GtkWidget *drawing_area;
    gtk_init (&argc, &argv);
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (gtk_main_quit), NULL);
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    /* Создаём аспект рамку и помещаем в основное окно */
    aspect_frame = gtk_aspect_frame_new ("2x1", /* label */
                                         0.5, /* center x */
                                         0.5, /* center y */
                                         2, /* xsize/ysize = 2 */
                                         FALSE /* ignore child's aspect */);
    gtk_container_add (GTK_CONTAINER (window), aspect_frame);
    gtk_widget_show (aspect_frame);
    /* Добавляем дочерний виджет в аспект рамку */
    drawing_area = gtk_drawing_area_new ();
    /* При размере она в 200x200, рамка имеет размер 200x100
     * потому, что соотношение установлено в 2х1 */
    gtk_widget_set_size_request (drawing_area, 200, 200);
    gtk_container_add (GTK_CONTAINER (aspect_frame), drawing_area);
    gtk_widget_show (drawing_area);
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}

<<< Previous

Home

Next >>>

Frames

Up

Paned Window Widgets




GTK+ 2.0 Tutorial

<<< Previous

Container Widgets

Next >>>


Разделяемые окна

Разделяемые окна используются когда нужно создать две части, относительного размера одного окна, контролируемые пользователем. Между частями существует специальный маркер для изменения размера частей при его захвате и перетаскивании. Разделение может быть как горизонтальным (HPaned), так и вертикальным (VPaned).

Создание нового разделённого окна:

GtkWidget *gtk_hpaned_new (void);
GtkWidget *gtk_vpaned_new (void);

После создания двух разделённых частей может потребоваться размещение в них дочерних виджетов. Это делается так:

void gtk_paned_add1 (GtkPaned *paned, GtkWidget *child);
void gtk_paned_add2 (GtkPaned *paned, GtkWidget *child);

gtk_paned_add1() добавляет дочерний виджет в левую или верхнюю часть разделённого окна. gtk_paned_add2() добавляет дочерний виджет в правую или нижнюю часть разделённого окна.

Как пример, мы создадим часть пользовательского интерфейса воображаемой почтовой программы.  Окно разделено на две части вертикально, с главной частью, являющейся списком почтовых сообщений и части основания - текст почтового сообщения.

Большая часть программы достаточно проста. В ней есть несколько строк для информации в которые не может быть добавлен текст. Это могло быть выполнено вызовом функции gtk_widget_realize(), но как демонстрация дополнительной методики, мы подключаем обработчик с сигналом "realize" - добавить текст.  Кроме того, мы должны добавить опцию GTK_SHRINK к некоторым из элементов в таблице, содержащей текстовое окно и его полосы прокрутки, чтобы, когда часть основания уменьшается, сжались правильные части виджета, вместо того, чтобы размешаться в основании окна.

#include <stdio.h>
#include <gtk/gtk.h>
   
/* Создаём список "messages" */
GtkWidget *create_list( void )
{
    GtkWidget *scrolled_window;
    GtkWidget *tree_view;
    GtkListStore *model;
    GtkTreeIter iter;
    GtkCellRenderer *cell;
    GtkTreeViewColumn *column;
    int i;
   
    /* Создаём новое окно, с полосой прокрутки при необходимости */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                    GTK_POLICY_AUTOMATIC, 
                                    GTK_POLICY_AUTOMATIC);
   
    model = gtk_list_store_new (1, G_TYPE_STRING);
    tree_view = gtk_tree_view_new ();
    gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), 
                                           tree_view);
    gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (model));
    gtk_widget_show (tree_view);
   
    /* Добавляем некоторое сообщение в окно */
    for (i = 0; i < 10; i++) {
        gchar *msg = g_strdup_printf ("Message #%d", i);
        gtk_list_store_append (GTK_LIST_STORE (model), &iter);
        gtk_list_store_set (GTK_LIST_STORE (model), 
                            &iter,
                            0, msg,
                            -1);
        g_free (msg);
    }
   
    cell = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes ("Messages",
                                                       cell,
                                                       "text", 0,
                                                       NULL);
  
    gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
                                 GTK_TREE_VIEW_COLUMN (column));
    return scrolled_window;
}
   
/* Добавляем некоторый текст в виджет текста */
void insert_text (GtkTextBuffer *buffer)
{
   GtkTextIter iter;
 
   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
   gtk_text_buffer_insert (buffer, &iter,   
    "From: pathfinder@nasa.gov\n"
    "To: mom@nasa.gov\n"
    "Subject: Made it!\n"
    "\n"
    "We just got in this morning. The weather has been\n"
    "great - clear but cold, and there are lots of fun sights.\n"
    "Sojourner says hi. See you soon.\n"
    " -Path\n", -1);
}
   
/* Создаём прокручиваемую текстовую область, которая содержит список сообщений "message" */
GtkWidget *create_text( void )
{
   GtkWidget *scrolled_window;
   GtkWidget *view;
   GtkTextBuffer *buffer;
   view = gtk_text_view_new ();
   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                   GTK_POLICY_AUTOMATIC,
                                   GTK_POLICY_AUTOMATIC);
   gtk_container_add (GTK_CONTAINER (scrolled_window), view);
   insert_text (buffer);
   gtk_widget_show_all (scrolled_window);
   return scrolled_window;
}
   
int main( int   argc,
          char *argv[] )
{
    GtkWidget *window;
    GtkWidget *vpaned;
    GtkWidget *list;
    GtkWidget *text;
    gtk_init (&argc, &argv);
   
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Paned Windows");
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (gtk_main_quit), NULL);
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    gtk_widget_set_size_request (GTK_WIDGET (window), 450, 400);
    /* Создаём виджет vpaned и добавляем его в основное окно */
   
    vpaned = gtk_vpaned_new ();
    gtk_container_add (GTK_CONTAINER (window), vpaned);
    gtk_widget_show (vpaned);
   
    /* Теперь создаём содержимое двух половин окна */
   
    list = create_list ();
    gtk_paned_add1 (GTK_PANED (vpaned), list);
    gtk_widget_show (list);
   
    text = create_text ();
    gtk_paned_add2 (GTK_PANED (vpaned), text);
    gtk_widget_show (text);
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}

<<< Previous

Home

Next >>>

Aspect Frames

Up

Viewports




GTK+ 2.0 Tutorial

<<< Previous

Виджеты контейнеры (Container Widgets)

Next >>>


Окно просмотра

Маловероятно, что вам когда либо прийдется использовать окно просмотра непосредственно, скорее всего вам следует использовать виджет Scrolled Window, который включает в себя виджет «окно просмотра».

Окно просмотра позволяет поместить больший виджет внутри себя, чтобы вы могли просматривать его по частям.  Виджет использует  Adjustments, чтобы определить область, которая является в настоящее время в поле зрения.

Окно просмотра создаётся с помощью функции:

GtkWidget *gtk_viewport_new( GtkAdjustment *hadjustment,
                             GtkAdjustment *vadjustment );

Как вы можете видеть допускаются как вертикальные так и горизонтальные регулировки. 

Вы можете получить и установить регуляторы, после того как виджет был создан, используя следующие четыре функции:

GtkAdjustment *gtk_viewport_get_hadjustment (GtkViewport *viewport );
GtkAdjustment *gtk_viewport_get_vadjustment (GtkViewport *viewport );
void gtk_viewport_set_hadjustment( GtkViewport   *viewport,
                                   GtkAdjustment *adjustment );
void gtk_viewport_set_vadjustment( GtkViewport   *viewport,
                                   GtkAdjustment *adjustment );

Единственная функция области просмотра используется, чтобы изменить ее внешность:

void gtk_viewport_set_shadow_type( GtkViewport   *viewport,
                                   GtkShadowType  type );

Возможные значения параметра type :

  GTK_SHADOW_NONE,
  GTK_SHADOW_IN,
  GTK_SHADOW_OUT,
  GTK_SHADOW_ETCHED_IN,
  GTK_SHADOW_ETCHED_OUT

<<< Previous

Home

Next >>>

Paned Window Widgets

Up

Scrolled Windows




GTK+ 2.0 Tutorial

<<< Previous

Container Widgets

Next >>>


Прокручиваемое окно

Прокручиваемое окно (Scrolled windows) используется для создания большой области содержащей другие виджеты с возможность постепенного просмотра содержимого. Вы можете вставлять в прокручиваемое окно любой тип виджетов и содержимое будет доступно несмотря на размер, благодаря scrollbars.

Функция для создания прокручиваемого окна:

GtkWidget *gtk_scrolled_window_new( GtkAdjustment *hadjustment,
                                    GtkAdjustment *vadjustment );

Первый аргумент отвечает за горизонтальное регулирование, второй за вертикальное. Все значения могут быть равны NULL.

void gtk_scrolled_window_set_policy( GtkScrolledWindow *scrolled_window,
                                     GtkPolicyType      hscrollbar_policy,
                                     GtkPolicyType      vscrollbar_policy );

Эта функция устанавливает политику использования полосы прокручивания. Первый аргумент - окно прокручивания которое вы хотите настроить. Второй - политика горизонтального прокручивания и третий - политика вертикального прокручивания.

Политика может быть двух типов, GTK_POLICY_AUTOMATIC или GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC автоматически определяет необходимость появления полосы прокручивания, а GTK_POLICY_ALWAYS всегда показывает полосу прокручивания.

Вы можете размещать объекты в окне прокручивания с помощью функции:

void gtk_scrolled_window_add_with_viewport( GtkScrolledWindow *scrolled_window,
                                            GtkWidget         *child);

В этом примере в окно прокручивания помещено 100 кнопок переключателей. В исходном коде прокомментированны только плохо знакомые вам части.

#include <stdio.h>
#include <gtk/gtk.h>
void destroy( GtkWidget *widget,
              gpointer   data )
{
    gtk_main_quit ();
}
int main( int   argc,
          char *argv[] )
{
    static GtkWidget *window;
    GtkWidget *scrolled_window;
    GtkWidget *table;
    GtkWidget *button;
    char buffer[32];
    int i, j;
    
    gtk_init (&argc, &argv);
    
    /* Создаём новый диалог окна прокручивания для упаковки */
    window = gtk_dialog_new ();
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (destroy), NULL);
    gtk_window_set_title (GTK_WINDOW (window), "GtkScrolledWindow example");
    gtk_container_set_border_width (GTK_CONTAINER (window), 0);
    gtk_widget_set_size_request (window, 300, 300);
    
    /* создаём новое окно прокручивания. */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    
    gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 10);
    
    /* Политика появления полос прокручивания GTK_POLICY AUTOMATIC, или GTK_POLICY_ALWAYS.
     * GTK_POLICY_AUTOMATIC автоматически определяет необходимость полосы прокручивания
     * GTK_POLICY_ALWAYS всегда выводит полосу прокручивания даже если прокручивать нечего
     * Первая - горизонтальная полоса прокручивания,вторая - вертикальная */
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    /* Создаём окно диалога с упакованным в него vbox */                                                                
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window, 
                        TRUE, TRUE, 0);
    gtk_widget_show (scrolled_window);
    
    /* создаём таблицу 10 на 10 */
    table = gtk_table_new (10, 10, FALSE);
    
    /* устанавливаем интервал в 10 на x и 10 на y */
    gtk_table_set_row_spacings (GTK_TABLE (table), 10);
    gtk_table_set_col_spacings (GTK_TABLE (table), 10);
    
    /* упаковываем таблицу в окно прокручивания */
    gtk_scrolled_window_add_with_viewport (
                   GTK_SCROLLED_WINDOW (scrolled_window), table);
    gtk_widget_show (table);
    
    /* просто создаём сетку из кнопок для демонстрации окна прокручивания */
    for (i = 0; i < 10; i++)
       for (j = 0; j < 10; j++) {
          sprintf (buffer, "button (%d,%d)\n", i, j);
          button = gtk_toggle_button_new_with_label (buffer);
          gtk_table_attach_defaults (GTK_TABLE (table), button,
                                     i, i+1, j, j+1);
          gtk_widget_show (button);
       }
    
    /* Добавляем в диалог кнопку "close" */
    button = gtk_button_new_with_label ("close");
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (gtk_widget_destroy),
                              G_OBJECT (window));
    
    /* устанавливаем кнопку по умолчанию */
    
    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);
    
    /* Перехватываем сигнал нажатия клавиши "Enter" для активизации кнопки по умолчанию */
    gtk_widget_grab_default (button);
    gtk_widget_show (button);
    
    gtk_widget_show (window);
    
    gtk_main();
    
    return 0;
}

Попробуйте по изменять размеры окна, чтобы увидеть реакцию полос прокручивания. Вы можете использовать функцию gtk_widget_set_size_request() для установки размеров окна или других виджетов по умолчанию.


<<< Previous

Home

Next >>>

Viewports

Up

Button Boxes




GTK+ 2.0 Tutorial

<<< Previous

Виджеты контейнеры (Container Widgets)

Next >>>


Контейнеры кнопок

Контейнеры кнопок позволяют быстро размещать группы кнопок с горизонтальным или вертикальным положением. Для создания контейнеров кнопок используются функции:

GtkWidget *gtk_hbutton_box_new( void );
GtkWidget *gtk_vbutton_box_new( void );

Для добавления в контейнер кнопок применяется:

    gtk_container_add (GTK_CONTAINER (button_box), child_widget);

Пример демонстрирует разные настройки контейнеров кнопок

#include <gtk/gtk.h>
/* Создаём контейнер кнопок со специфическими параметрами */
GtkWidget *create_bbox( gint  horizontal,
                        char *title,
                        gint  spacing,
                        gint  child_w,
                        gint  child_h,
                        gint  layout )
{
  GtkWidget *frame;
  GtkWidget *bbox;
  GtkWidget *button;
  frame = gtk_frame_new (title);
  if (horizontal)
    bbox = gtk_hbutton_box_new ();
  else
    bbox = gtk_vbutton_box_new ();
  gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), bbox);
  /* Установка внешнего вида контейнера */
  gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), layout);
  gtk_box_set_spacing (GTK_BOX (bbox), spacing);
  /*gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), child_w, child_h);*/
  button = gtk_button_new_from_stock (GTK_STOCK_OK);
  gtk_container_add (GTK_CONTAINER (bbox), button);
  button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
  gtk_container_add (GTK_CONTAINER (bbox), button);
  button = gtk_button_new_from_stock (GTK_STOCK_HELP);
  gtk_container_add (GTK_CONTAINER (bbox), button);
  return frame;
}
int main( int   argc,
          char *argv[] )
{
  static GtkWidget* window = NULL;
  GtkWidget *main_vbox;
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *frame_horz;
  GtkWidget *frame_vert;
  /* Инициализируем GTK */
  gtk_init (&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Button Boxes");
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit),
                    NULL);
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  main_vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (window), main_vbox);
  frame_horz = gtk_frame_new ("Горизонтальный контейнер кнопок");
  gtk_box_pack_start (GTK_BOX (main_vbox), frame_horz, TRUE, TRUE, 10);
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
  gtk_container_add (GTK_CONTAINER (frame_horz), vbox);
  gtk_box_pack_start (GTK_BOX (vbox),
           create_bbox (TRUE, "Spread (spacing 40)", 40, 85, 20, GTK_BUTTONBOX_SPREAD),
                      TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (vbox),
           create_bbox (TRUE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
                      TRUE, TRUE, 5);
  gtk_box_pack_start (GTK_BOX (vbox),
           create_bbox (TRUE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
                      TRUE, TRUE, 5);
  gtk_box_pack_start (GTK_BOX (vbox),
           create_bbox (TRUE, "End (spacing 10)", 10, 85, 20, GTK_BUTTONBOX_END),
                      TRUE, TRUE, 5);
  frame_vert = gtk_frame_new ("Вертикальный контейнер кнопок");
  gtk_box_pack_start (GTK_BOX (main_vbox), frame_vert, TRUE, TRUE, 10);
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
  gtk_container_add (GTK_CONTAINER (frame_vert), hbox);
  gtk_box_pack_start (GTK_BOX (hbox),
           create_bbox (FALSE, "Spread (spacing 5)", 5, 85, 20, GTK_BUTTONBOX_SPREAD),
                      TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (hbox),
           create_bbox (FALSE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
                      TRUE, TRUE, 5);
  gtk_box_pack_start (GTK_BOX (hbox),
           create_bbox (FALSE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
                      TRUE, TRUE, 5);
  gtk_box_pack_start (GTK_BOX (hbox),
           create_bbox (FALSE, "End (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_END),
                      TRUE, TRUE, 5);
  gtk_widget_show_all (window);
  /* Входим в цикл ожидания событий */
  gtk_main ();
  return 0;
}

<<< Previous

Home

Next >>>

Scrolled Windows

Up

Toolbar




GTK+ 2.0 Tutorial

<<< Previous

Контейнерные виджеты (Container Widgets)

Next >>>


Панель инструментов

Панели инструментов обычно используются для группировки некоторого числа виджетов, чтобы упростить настройку их вида и расположения. Обычно панель состоит из кнопок с изображениями и текстовыми ярлыками, но можно добавить и любой другой виджет на панель. Объекты на панели могут выравниваться горизонтально или вертикально, а кнопки могут отображаться с пиктограммами, текстовыми ярлыками или с тем и другим сразу.

Создание панели инструментов:

GtkWidget *gtk_toolbar_new( void );

После создания панели в неё можно добавлять пункты (короткие текстовые строки) или элементы (любые виджеты). Чтобы описать пункт, нужен текстовый ярлык, пиктограмма, текст подсказки, текст расширенной подсказки и функция обратного вызова. Для прикрепления к панели пункта можно использовать функции:

GtkWidget *gtk_toolbar_append_item( GtkToolbar    *toolbar,
                                    const char    *text,
                                    const char    *tooltip_text,
                                    const char    *tooltip_private_text,
                                    GtkWidget     *icon,
                                    GtkSignalFunc  callback,
                                    gpointer       user_data );
GtkWidget *gtk_toolbar_prepend_item( GtkToolbar    *toolbar,
                                     const char    *text,
                                     const char    *tooltip_text,
                                     const char    *tooltip_private_text,
                                     GtkWidget     *icon,
                                     GtkSignalFunc  callback,
                                     gpointer       user_data );

Если Вы хотите использовать gtk_toolbar_insert_item(), единственный дополнительный параметр который должен быть определен - положение в которое пункт должен быть вставлен, выглядит это таким образом:

GtkWidget *gtk_toolbar_insert_item( GtkToolbar    *toolbar,
                                    const char    *text,
                                    const char    *tooltip_text,
                                    const char    *tooltip_private_text,
                                    GtkWidget     *icon,
                                    GtkSignalFunc  callback,
                                    gpointer       user_data,
                                    gint           position );

Чтобы упростить добавку места между пунктами панели инструментов, вы можете использовать следующие функции:

void gtk_toolbar_append_space( GtkToolbar *toolbar );
void gtk_toolbar_prepend_space( GtkToolbar *toolbar );
void gtk_toolbar_insert_space( GtkToolbar *toolbar,
                               gint        position );

Если требуется, ориентация панели инструментов и ее стиля может быть изменена "на лету" используя следующие функции:

void gtk_toolbar_set_orientation( GtkToolbar     *toolbar,
                                  GtkOrientation  orientation );
void gtk_toolbar_set_style( GtkToolbar      *toolbar,
                            GtkToolbarStyle  style );
void gtk_toolbar_set_tooltips( GtkToolbar *toolbar,
                               gint        enable );

Где аргумент orientation одно из двух значений GTK_ORIENTATION_HORIZONTAL или GTK_ORIENTATION_VERTICAL. Аргумент style используется для установки одного из возможных вариантов отображения пунктов на панели GTK_TOOLBAR_ICONS, GTK_TOOLBAR_TEXT или GTK_TOOLBAR_BOTH.

Чтобы показать некоторые дополнительные действия, которые могут быть сделаны с панелью инструментов, давайте рассмотрим следующую программу (мы прервем пример с некоторыми дополнительными объяснениями):

#include <gtk/gtk.h>
/* Эта функция подключается к кнопке Close или
 * закрытию окна с помощью WM */
gint delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
  gtk_main_quit ();
  return FALSE;
}

Вышеупомянутое начало кажется наверняка знакомым, если это не первая ваша программа GTK. Дополнительно мы включили изображение (XPM picture) для всех кнопок.

GtkWidget* close_button; /* Эта кнопка создаёт сигнал для
                          * закрытия приложения */
GtkWidget* tooltips_button; /* включить/выключить (enable/disable) подсказки */
GtkWidget* text_button,
         * icon_button,
         * both_button; /* кнопка переключения стиля панели */
GtkWidget* entry; /* ввод текста для упаковки в какой нибудь виджет при отображении на панели */

Фактически не все вышеупомянутые виджеты необходимы здесь, но для более ясного понимания мы разместили их все вместе.

/* при переключении кнопки мы проверяем какой стиль был активным
 * и делаем соответствующие установки
 * ВНИМАНИЕ: панель передаётся как данные для обратного вызова! */
void radio_event (GtkWidget *widget, gpointer data)
{
  if (GTK_TOGGLE_BUTTON (text_button)->active) 
    gtk_toolbar_set_style (GTK_TOOLBAR (data), GTK_TOOLBAR_TEXT);
  else if (GTK_TOGGLE_BUTTON (icon_button)->active)
    gtk_toolbar_set_style (GTK_TOOLBAR (data), GTK_TOOLBAR_ICONS);
  else if (GTK_TOGGLE_BUTTON (both_button)->active)
    gtk_toolbar_set_style (GTK_TOOLBAR (data), GTK_TOOLBAR_BOTH);
}
/* проверяем переключатель для включения/выключения подсказок */
void toggle_event (GtkWidget *widget, gpointer data)
{
  gtk_toolbar_set_tooltips (GTK_TOOLBAR (data),
                            GTK_TOGGLE_BUTTON (widget)->active );
}

В зависимости от того какая кнопка нажата, будет вызвана одна из двух функций. Вам знакомо это, если вы уже использовали переключатели и кнопки выбора (radio buttons).

int main (int argc, char *argv[])
{
  /* Наше главное окно (диалог) и контейнер */
  GtkWidget* dialog;
  GtkWidget* handlebox;
  /* Нам нужна панель и пиктограммы (одна для всех кнопок),
   * а также icon widget для размещения в нём пиктограмм
   * (но мы создадим отдельный виджет для размещения каждой кнопки) */
  GtkWidget * toolbar;
  GtkWidget * iconw;
  /* Необходимый для всех GTK программ вызов. */
  gtk_init (&argc, &argv);
  
  /* Создаём новое окно с заголовком и необходимого размера */
  dialog = gtk_dialog_new ();
  gtk_window_set_title (GTK_WINDOW (dialog), "GTKToolbar Tutorial");
  gtk_widget_set_size_request (GTK_WIDGET (dialog), 600, 300);
  GTK_WINDOW (dialog)->allow_shrink = TRUE;
  /* типичный выход при попытке закрытия */
  g_signal_connect (G_OBJECT (dialog), "delete_event",
                    G_CALLBACK (delete_event), NULL);
  /* мы должны реализовать окно, потому что используем пиктограммы в контексте */
  gtk_widget_realize (dialog);
  /* поместим панель в удобный контейнер для разделения с основным окном */
  handlebox = gtk_handle_box_new ();
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                      handlebox, FALSE, FALSE, 5);

Из всего вышеперечисленного в объяснении нуждается только «удобный контейнер» (handle box). «Удобный контейнер» используется для упаковки виджетов, но отличается от обычных контейнеров. Различие заключается в том, что «удобный контейнер» может быть отсоединен от основного окна для произвольного позиционирования виджетов размещенных в нем (в пределах родительского окна). Обычно он используется для создания съёмных панелей инструментов.

  /* панель будет размещена горизонтально с пиктограммами и текстом,
   * между пунктами будет промежуток в 5pxl 
   * и всё это будет упаковано в handlebox */
  toolbar = gtk_toolbar_new ();
  gtk_toolbar_set_orientation (GTK_TOOLBAR (toolbar), GTK_ORIENTATION_HORIZONTAL);
  gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH);
  gtk_container_set_border_width (GTK_CONTAINER (toolbar), 5);
  gtk_toolbar_set_space_size (GTK_TOOLBAR (toolbar), 5);
  gtk_container_add (GTK_CONTAINER (handlebox), toolbar);

То, что сделано выше, является прямой инициализацией виджета панели инструментов.

  /* наш первый пункт кнопка <close> */
  iconw = gtk_image_new_from_file ("gtk.xpm"); /* icon widget */
  close_button = 
    gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), /* наша панель */
                             "Close",               /* ярлык кнопки */
                             "Closes this app",     /* подсказка кнопки */
                             "Private",             /* подробная подсказка */
                             iconw,                 /* icon widget */
                             GTK_SIGNAL_FUNC (delete_event), /* сигнал */
                             NULL);
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar)); /* пробел после пункта */

В вышеупомянутом коде вы видите самый простой способ добавления кнопки на панель инструментов.  Как раз перед добавлением нового пункта, мы должны сконструировать виджет изображения (image widget), чтобы служить изображением для этого пункта; этот шаг должен будет быть повторен для каждого нового пункта. Также мы добавляем пробел после пункта для избежания касания с ним других пунктов. Как вы видите, функция gtk_toolbar_append_item() возвращает указатель на вновь созданную кнопку, вы можете работать с ним как обычно.

  /* создаём группу кнопок выбора */
  iconw = gtk_image_new_from_file ("gtk.xpm");
  icon_button = gtk_toolbar_append_element (
                    GTK_TOOLBAR (toolbar),
                    GTK_TOOLBAR_CHILD_RADIOBUTTON, /* тип элемента */
                    NULL,                          /* указатель на виджет */
                    "Icon",                        /* ярлык */
                    "Only icons in toolbar",       /* подсказка */
                    "Private",                     /* расширенная подсказка */
                    iconw,                         /* пиктограмма */
                    GTK_SIGNAL_FUNC (radio_event), /* сигнал */
                    toolbar);                      /* данные сигнала */
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));

Для создания группы переключателей мы использовали gtk_toolbar_append_element. Фактически используя эту функцию можно легко добавлять пункты или пробелы (type = GTK_TOOLBAR_CHILD_SPACE или +GTK_TOOLBAR_CHILD_BUTTON). В создании других радио-кнопок для этой группы требуется указатель на предыдущую кнопку в группе, чтобы легко построить список кнопок (см. секцию Radio Buttons).

  /* following radio buttons refer to previous ones */
  iconw = gtk_image_new_from_file ("gtk.xpm");
  text_button = 
    gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
                                GTK_TOOLBAR_CHILD_RADIOBUTTON,
                                icon_button,
                                "Text",
                                "Only texts in toolbar",
                                "Private",
                                iconw,
                                GTK_SIGNAL_FUNC (radio_event),
                                toolbar);
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
                                          
  iconw = gtk_image_new_from_file ("gtk.xpm");
  both_button = 
    gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
                                GTK_TOOLBAR_CHILD_RADIOBUTTON,
                                text_button,
                                "Both",
                                "Icons and text in toolbar",
                                "Private",
                                iconw,
                                GTK_SIGNAL_FUNC (radio_event),
                                toolbar);
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (both_button), TRUE);

В конце мы должны установить статус одной из кнопок вручную (иначе, они все остаются в активном статусе, препятствуя нам переключаться между ними).

  /* простая кнопка переключения */
  iconw = gtk_image_new_from_file ("gtk.xpm");
  tooltips_button = 
    gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
                                GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
                                NULL,
                                "Tooltips",
                                "Toolbar with or without tips",
                                "Private",
                                iconw,
                                GTK_SIGNAL_FUNC (toggle_event),
                                toolbar);
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tooltips_button), TRUE);

Переключатель создаётся очевидным способом (если вы уже знаете как создать кнопку выбора).

  /* упаковываем виджет в панель и прикрепляем соответствующую подсказку */
  entry = gtk_entry_new ();
  gtk_toolbar_append_widget (GTK_TOOLBAR (toolbar), 
                             entry, 
                             "This is just an entry", 
                             "Private");
  /* отображаем созданное */
  gtk_widget_show (entry);

Как вы видите, добавить любой виджет к панели инструментов достаточно просто. Однако вы должны помнить, что этот виджет нужно показать вручную (вопреки другим пунктам, которые будут показаны вместе с панелью инструментов).

  /* выводим на экран всё сразу. */
  gtk_widget_show (toolbar);
  gtk_widget_show (handlebox);
  gtk_widget_show (dialog);
  /* переходим в цикл ожидания событий */
  gtk_main ();
  
  return 0;
}

Наша пиктограмма (XPM icon):

/* XPM */
static char * gtk_xpm[] = {
"32 39 5 1",
".      c none",
"+      c black",
"@      c #3070E0",
"#      c #F05050",
"$      c #35E035",
"................+...............",
"..............+++++.............",
"............+++++@@++...........",
"..........+++++@@@@@@++.........",
"........++++@@@@@@@@@@++........",
"......++++@@++++++++@@@++.......",
".....+++@@@+++++++++++@@@++.....",
"...+++@@@@+++@@@@@@++++@@@@+....",
"..+++@@@@+++@@@@@@@@+++@@@@@++..",
".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
".+##++@@@@+++@@@+++++@@@@@@@@$@.",
".+###++@@@@+++@@@+++@@@@@++$$$@.",
".+####+++@@@+++++++@@@@@+@$$$$@.",
".+#####+++@@@@+++@@@@++@$$$$$$+.",
".+######++++@@@@@@@++@$$$$$$$$+.",
".+#######+##+@@@@+++$$$$$$@@$$+.",
".+###+++##+##+@@++@$$$$$$++$$$+.",
".+###++++##+##+@@$$$$$$$@+@$$@+.",
".+###++++++#+++@$$@+@$$@++$$$@+.",
".+####+++++++#++$$@+@$$++$$$$+..",
".++####++++++#++$$@+@$++@$$$$+..",
".+#####+++++##++$$++@+++$$$$$+..",
".++####+++##+#++$$+++++@$$$$$+..",
".++####+++####++$$++++++@$$$@+..",
".+#####++#####++$$+++@++++@$@+..",
".+#####++#####++$$++@$$@+++$@@..",
".++####++#####++$$++$$$$$+@$@++.",
".++####++#####++$$++$$$$$$$$+++.",
".+++####+#####++$$++$$$$$$$@+++.",
"..+++#########+@$$+@$$$$$$+++...",
"...+++########+@$$$$$$$$@+++....",
".....+++######+@$$$$$$$+++......",
"......+++#####+@$$$$$@++........",
".......+++####+@$$$$+++.........",
".........++###+$$$@++...........",
"..........++##+$@+++............",
"...........+++++++..............",
".............++++..............."};

<<< Previous

Home

Next >>>

Button Boxes

Up

Notebooks




GTK+ 2.0 Tutorial

<<< Previous

Container Widgets

Next >>>


Вкладки (Notebooks)

Виджет вкладки (NoteBook) - это набор страниц которые размещаются друг над другом и несут в себе различную информацию, только одна страница из набора может быть видима при просмотре. Этот виджет в последнее время стал очень популярным в GUI programming, с его помощью очень удобно показывать блоки однотипной информации разделяя их соответствующим образом.

Функция создания:

GtkWidget *gtk_notebook_new( void );

Существует достаточно много функций для работы с этим виджетом. Рассмотрим каждую в отдельности ниже.

Сначала рассмотрим индикаторы страниц или "tabs". Индикаторы могут размещаться вверху, внизу, слева или справа.

void gtk_notebook_set_tab_pos( GtkNotebook     *notebook,
                               GtkPositionType  pos );

Аргумент GtkPositionType имеет интуитивно понятные значения:

  GTK_POS_LEFT
  GTK_POS_RIGHT
  GTK_POS_TOP
  GTK_POS_BOTTOM

По умолчанию используется GTK_POS_TOP.

Следующим рассмотрим добавление страниц к набору. Есть три способа сделать это. Первые два рассмотрим вместе, потому что они очень похожи.

void gtk_notebook_append_page( GtkNotebook *notebook,
                               GtkWidget   *child,
                               GtkWidget   *tab_label );
void gtk_notebook_prepend_page( GtkNotebook *notebook,
                                GtkWidget   *child,
                                GtkWidget   *tab_label );

Эти функции добавляют страницы к набору в конец (append), или в начало (prepend). child - это виджет который размещен в пределах добавляемой страницы, а tab_label - это ярлык добавленной страницы. Дочерний виджет должен быть создан отдельно и может располагаться в другом виджете контейнере, например в таблице.

Третья функция добавления страницы имеет теже параметры что и предыдущие две, а также позволяет определять расположение добавляемой страницы.

void gtk_notebook_insert_page( GtkNotebook *notebook,
                               GtkWidget   *child,
                               GtkWidget   *tab_label,
                               gint         position );

Параметр position определяет позицию  добавляемой страницы относительно первой из набора, которая имеет позицию ноль (zero).

Зная как можно добавлять страницы, рассмотрим как их можно удалять:

void gtk_notebook_remove_page( GtkNotebook *notebook,
                               gint         page_num );

Эта функция удаляет страницу определённую аргументом page_num из набора notebook.

Функция определяющая текущую страницу в наборе:

gint gtk_notebook_get_current_page( GtkNotebook *notebook );

Следующие две функции перемещают страницы вперед или назад. Просто укажите функциям набор страниц с которым хотите работать. Учтите: когда набор страниц находится на последней странице и вызвана функция gtk_notebook_next_page(), он переходит на первую. Аналогично, если набор на первой странице и вызвана функция gtk_notebook_prev_page(), он переходит на последнюю.

void gtk_notebook_next_page( GtkNoteBook *notebook );
void gtk_notebook_prev_page( GtkNoteBook *notebook );

Следующая функция определяет активную ("active") страницу. Если вы захотите например открыть какую нибудь страницу, то вам нужна будет эта функция. По умолчанию всегда открывается первая страница.

void gtk_notebook_set_current_page( GtkNotebook *notebook,
                                    gint         page_num );

Следующие две функции добавляют или удаляют из набора вкладки и границы набора соответственно.

void gtk_notebook_set_show_tabs( GtkNotebook *notebook,
                                 gboolean     show_tabs );
void gtk_notebook_set_show_border( GtkNotebook *notebook,
                                   gboolean     show_border );

Следующая функция позволяет пролистывать вкладки с помощью специальных стрелок, когда имеется слишком большое кол-во страниц и вкладки не могут сжаться до соответствующего уровня.

void gtk_notebook_set_scrollable( GtkNotebook *notebook,
                                  gboolean     scrollable );

show_tabs, show_border и scrollable могут иметь значение TRUE или FALSE.

Рассмотрим усовершенствованный пример из estgtk.c, который поставляется вместе с кодом GTK. Эта небольшая программа состоит из набора страниц и шести кнопок. Набор содержит 11 страниц вставленных разными способами, наложением, вставкой, и по выбору. Кнопки позволяют добавлять/удалять вкладки и границы, удалять страницы, изменять положение страниц в разные стороны и выходить из программы.

#include <stdio.h>
#include <gtk/gtk.h>
/* Функция перемещает позиции вкладок */
void rotate_book( GtkButton   *button,
                  GtkNotebook *notebook )
{
    gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos + 1) % 4);
}
/* Добавить/удалить вкладку и границу */
void tabsborder_book( GtkButton   *button,
                      GtkNotebook *notebook )
{
    gint tval = FALSE;
    gint bval = FALSE;
    if (notebook->show_tabs == 0)
    if (notebook->show_border == 0)
            bval = TRUE;
    gtk_notebook_set_show_tabs (notebook, tval);
    gtk_notebook_set_show_border (notebook, bval);
}
/* Удалить страницу из набора */
void remove_book( GtkButton   *button,
                  GtkNotebook *notebook )
{
    gint page;
    page = gtk_notebook_get_current_page (notebook);
    gtk_notebook_remove_page (notebook, page);
     This forces the widget to redraw itself. */
    gtk_widget_queue_draw (GTK_WIDGET (notebook));
}
gint delete( GtkWidget *widget,
             GtkWidget *event,
             gpointer   data )
{
    gtk_main_quit ();
    return FALSE;
}
int main( int argc,
          char *argv[] )
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *table;
    GtkWidget *notebook;
    GtkWidget *frame;
    GtkWidget *label;
    GtkWidget *checkbutton;
    int i;
    char bufferf[32];
    char bufferl[32];
    gtk_init (&argc, &argv);
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_signal_connect (G_OBJECT (window), "delete_event",
                      G_CALLBACK (delete), NULL);
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    table = gtk_table_new (3, 6, FALSE);
    gtk_container_add (GTK_CONTAINER (window), table);
    /* Создаём новый набор, указываем позицию вкладок */
    notebook = gtk_notebook_new ();
    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
    gtk_table_attach_defaults (GTK_TABLE (table), notebook, 0, 6, 0, 1);
    gtk_widget_show (notebook);
    /* прикладываем страницы к набору */
    for (i = 0; i < 5; i++) {
        sprintf(bufferf, "Append Frame %d", i + 1);
        sprintf(bufferl, "Page %d", i + 1);
        
        frame = gtk_frame_new (bufferf);
        gtk_container_set_border_width (GTK_CONTAINER (frame), 10);
        gtk_widget_set_size_request (frame, 100, 75);
        gtk_widget_show (frame);
        
        label = gtk_label_new (bufferf);
        gtk_container_add (GTK_CONTAINER (frame), label);
        gtk_widget_show (label);
        
        label = gtk_label_new (bufferl);
        gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
    }
    /* добавляем страницу специальной меткой */
    checkbutton = gtk_check_button_new_with_label ("Check me please!");
    gtk_widget_set_size_request (checkbutton, 100, 75);
    gtk_widget_show (checkbutton);
    label = gtk_label_new ("Add page");
    gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);
    /* вставляем страницы в набор */
    for (i = 0; i < 5; i++) {
        sprintf (bufferf, "Prepend Frame %d", i + 1);
        sprintf (bufferl, "PPage %d", i + 1);
        
        frame = gtk_frame_new (bufferf);
        gtk_container_set_border_width (GTK_CONTAINER (frame), 10);
        gtk_widget_set_size_request (frame, 100, 75);
        gtk_widget_show (frame);
        
        label = gtk_label_new (bufferf);
        gtk_container_add (GTK_CONTAINER (frame), label);
        gtk_widget_show (label);
        
        label = gtk_label_new (bufferl);
        gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), frame, label);
    }
    /* с какой страницы открывать набор (page 4) */
    gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 3);
    /* Создаём кнопки */
    button = gtk_button_new_with_label ("close");
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (delete), NULL);
    gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 1, 1, 2);
    gtk_widget_show (button);
    button = gtk_button_new_with_label ("next page");
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (gtk_notebook_next_page),
                              G_OBJECT (notebook));
    gtk_table_attach_defaults (GTK_TABLE (table), button, 1, 2, 1, 2);
    gtk_widget_show (button);
    button = gtk_button_new_with_label ("prev page");
    g_signal_connect_swapped (G_OBJECT (button), "clicked",
                              G_CALLBACK (gtk_notebook_prev_page),
                              G_OBJECT (notebook));
    gtk_table_attach_defaults (GTK_TABLE (table), button, 2, 3, 1, 2);
    gtk_widget_show (button);
    button = gtk_button_new_with_label ("tab position");
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (rotate_book),
                      (gpointer) notebook);
    gtk_table_attach_defaults (GTK_TABLE (table), button, 3, 4, 1, 2);
    gtk_widget_show (button);
    button = gtk_button_new_with_label ("tabs/border on/off");
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (tabsborder_book),
                      (gpointer) notebook);
    gtk_table_attach_defaults (GTK_TABLE (table), button, 4, 5, 1, 2);
    gtk_widget_show (button);
    button = gtk_button_new_with_label ("remove page");
    g_signal_connect (G_OBJECT (button), "clicked",
                      G_CALLBACK (remove_book),
                      (gpointer) notebook);
    gtk_table_attach_defaults (GTK_TABLE (table), button, 5, 6, 1, 2);
    gtk_widget_show (button);
    gtk_widget_show (table);
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}

<<< Previous

Home

Next >>>

Toolbar

Up

Menu Widget




GTK+ 2.0 Tutorial

<<< Previous

Menu Widget

Next >>>


Пример создания меню

#include <stdio.h>
#include <gtk/gtk.h>
static gint button_press (GtkWidget *, GdkEvent *);
static void menuitem_response (gchar *);
int main( int   argc,
          char *argv[] )
{
    GtkWidget *window;
    GtkWidget *menu;
    GtkWidget *menu_bar;
    GtkWidget *root_menu;
    GtkWidget *menu_items;
    GtkWidget *vbox;
    GtkWidget *button;
    char buf[128];
    int i;
    gtk_init (&argc, &argv);
    /* создаём новое окно */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_size_request (GTK_WIDGET (window), 200, 100);
    gtk_window_set_title (GTK_WINDOW (window), "GTK Menu Test");
    g_signal_connect (G_OBJECT (window), "delete_event",
                      G_CALLBACK (gtk_main_quit), NULL);
    /* инициализируем menu-widget, и помните -- никогда
     * не используйте gtk_show_widget() для menu widget!! 
     * Это меню содержит пункты меню
     * и появляется когда вы нажимаете "Root Menu" в приложении */
    menu = gtk_menu_new ();
    /* Затем мы делаем небольшой цикл, который делает три входа меню для "test-menu".
     * Обратите внимание на вызов gtk_menu_shell_append. Он добавляет список
     * пунктов меню в наше меню. Обычно создаётся перехват сигнала нажатия "clicked"
     * на пункт меню и создается соответствующий обратный вызов,
     * но здесь это опущено для экономии места. */
    for (i = 0; i < 3; i++)
        {
            /* Копируем имя в буфер. */
            sprintf (buf, "Test-undermenu - %d", i);
            /* Создаём новый пункт меню (menu-item) с именем... */
            menu_items = gtk_menu_item_new_with_label (buf);
            /* ...и добавляем его в меню. */
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items);
            /* Делаем кое-что интересное когда пункт меню выбран */
            g_signal_connect_swapped (G_OBJECT (menu_items), "activate",
                                      G_CALLBACK (menuitem_response), 
                                      (gpointer) g_strdup (buf));
            /* Отображаем виджет */
            gtk_widget_show (menu_items);
        }
    /* Главное меню отображенное на панели с помощью ярлыка
     * Прикреплять обработчик сигнала не нужно,
     * поскольку остальная часть меню появляется только при нажатии */
    root_menu = gtk_menu_item_new_with_label ("Root Menu");
    gtk_widget_show (root_menu);
    /* Теперь мы делаем так, чтобы наше вновь созданное меню стало главным меню "root menu" */
    gtk_menu_item_set_submenu (GTK_MENU_ITEM (root_menu), menu);
    /* vbox для помещения меню и кнопок: */
    vbox = gtk_vbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show (vbox);
    /* Создаём панель меню (menu-bar) содержащую меню и находящуюся в основном окне */
    menu_bar = gtk_menu_bar_new ();
    gtk_box_pack_start (GTK_BOX (vbox), menu_bar, FALSE, FALSE, 2);
    gtk_widget_show (menu_bar);
    /* Создаём кнопки для появления меню */
    button = gtk_button_new_with_label ("press me");
    g_signal_connect_swapped (G_OBJECT (button), "event",
                              G_CALLBACK (button_press), 
                              G_OBJECT (menu));
    gtk_box_pack_end (GTK_BOX (vbox), button, TRUE, TRUE, 2);
    gtk_widget_show (button);
    /* И наконец мы прилагаем пункты меню (menu-item) к главному пункту ("root" menu-item) на панели меню (menu-bar) */
    gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), root_menu);
    /* отображаем всё содержимое окна. */
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}
/* Ответ на нажатие кнопки (button-press) помещение меню как виджета.
 *
 * Заметьте что аргумент "widget" отправляемое меню, а не нажатая кнопка.
 */
static gint button_press( GtkWidget *widget,
                          GdkEvent *event )
{
    if (event->type == GDK_BUTTON_PRESS) {
        GdkEventButton *bevent = (GdkEventButton *) event; 
        gtk_menu_popup (GTK_MENU (widget), NULL, NULL, NULL, NULL,
                        bevent->button, bevent->time);
        /* Событие обработано; нажатие останавливается здесь. */
        return TRUE;
    }
    /* Событие не обработано передаётся значение FALSE */
    return FALSE;
}
/* Печатаем строку выбранного пункта меню */
static void menuitem_response( gchar *string )
{
    printf ("%s\n", string);
}

Также можно сделать пункты меню привязанные к определённым клавишам.


<<< Previous

Home

Next >>>

Menu Widget

Up

Using ItemFactory




GTK+ 2.0 Tutorial

<<< Previous

Menu Widget

Next >>>


Использование ItemFactory

После рассмотрения сложного пути создания меню, рассмотрим как это же можно сделать при помощи вызова функции gtk_item_factory.

ItemFactory создаёт меню из массива записей  ItemFactory.  Это означает, что вы можете определить ваше меню в его самой простой форме и затем создать виджеты (меню/строки) меню с минимумом запросов функции.

ItemFactory записи

В ядре ItemFactory находится ItemFactoryEntry.  Эта структура определяет один пункт меню и когда массив этих записей определен, целое меню сформировано. Структура записей ItemFactory выглядит так:

struct _GtkItemFactoryEntry
{
  gchar *path;
  gchar *accelerator;
  GtkItemFactoryCallback callback;
  guint callback_action;
  gchar *item_type;
};

Каждая строка определяет часть пункта меню.

*path - определяет путь и название пункта меню, например: "/File/Open" было бы именем пункта меню который подходит для записи ItemFactory с путём "/File". Однако пункт "/File/Open" был бы показан в меню File как "Open", потому что первые "/" используются для определения пути и не могут присутствовать в имени пункта. Символ, которому предшествует символ подчеркивания, указывает акселератор (сокращенная клавиша) в открытом меню.

*accelerator  является строкой, которая указывает ключевую комбинацию клавиш и используется как ярлык к пункту меню. Строка может состоять из единичного символа или комбинации модификаторов и символов.

Модификаторами клавиш могут быть:

"<ALT>                               - alt
"<CTL>" или "<CTRL>" или "<CONTROL>" - control
"<MOD1>" до "<MOD5>"                 - modn
"<SHFT>" или "<SHIFT>"               - shift

Пример:

"<ConTroL>a"
"<SHFT><ALT><CONTROL>X"

callback - функция вызываемая при создании пунктом меню сигнала "activate". Форма обратного вызова описана в разделе Callback Description.

Значение callback_action помещается в функцию обратного вызова. Прототип функции смотрите в разделе Callback Description.

item_type - является строкой, которая определяет тип виджета упакованного в контейнер пунктов меню. Возможные значения:

NULL или "" или "<Item>" - создаёт простой пункт
"<Title>"                - создаёт заголовочный пункт
"<CheckItem>"            - создаёт контрольный пункт
"<ToggleItem>"           - создаёт переключающий пункт
"<RadioItem>"            - создаёт основной (root) выборочный пункт
"Path"                   - создаёт вторичный выборочный пункт
"<Tearoff>"              - создаёт разрыв
"<Separator>"            - создаёт разделитель
"<Branch>"               - создаёт пункт содержащий суб меню (optional)
"<LastBranch>"           - создаёт выровненный по правому краю переход

Отметьте, что <LastBranch> полезен только для одного подменю строки меню.

Описание обратного вызова (Callback Description)

Обратный вызов для  ItemFactory entry может иметь две формы. Если callback_action равен 0 (zero), он имеет следующую форму:

void callback(void)

другая форма:

void callback(gpointer    callback_data,
              guint       callback_action,
              GtkWidget  *widget)

callback_data - является указателем на произвольную часть данных и установлен вызовом gtk_item_factory_create_items().

callback_action - некоторое значение callback_action в ItemFactory записях.

*widget - указатель на виджет пункта меню (подробности в Manual Menu Creation).

ItemFactory entry examples

Создаём простой пункт меню:

GtkItemFactoryEntry entry = {"/_File/_Open...", "<CTRL>O", print_hello,
                                0, "<Item>"};

Здесь создаётся простой пункт меню "/File/Open" (отображается как "Open"), внутри строки меню "/File". Он имеет акселератор (shortcut) control+'O' при нажатии которого вызывается функция print_hello(). print_hello() имеет форму void print_hello(void) так как callback_action имеет значение zero. Если 'O' в "Open" подчеркнута, то при открытом меню и нажатой клавише 'O' пункт будет активизирован. Заметьте, что "File/_Open" может быть использован как путь вместо "/_File/_Open".

Создание входа с более сложным обратным вызовом (callback):

GtkItemFactoryEntry entry = {"/_View/Display _FPS", NULL, print_state,
                                7,"<CheckItem>"};

Это определяет новый пункт меню отображаемый как "Display FPS" в вышестоящем пункте меню "View". При нажатии вызывается функция print_state(). Так как callback_action не равно zero print_state() имеет форму:

void print_state(gpointer    callback_data,
                 guint       callback_action,
                 GtkWidget  *widget)

с callback_action равным 7.

Создаём установку кнопки выбора:

GtkItemFactoryEntry entry1 = {"/_View/_Low Resolution", NULL, change_resolution,
                                1, "<RadioButton>"};
GtkItemFactoryEntry entry2 = {"/_View/_High Resolution", NULL, change_resolution,
                                2, "/View/Low Resolution"};

entry1 определяет одиночную кнопку выбора которая переключает вызов функции change_resolution() с параметром callback_action равным 1. change_resolution() имеет форму:

void change_resolution(gpointer    callback_data,
                       guint       callback_action,
                       GtkWidget  *widget)

entry2 определяет кнопку выбора которая принадлежит группе в которой находится  entry1. Она вызывает туже самую функцию при переключении, но параметр callback_action равен 2. Обратите внимание на отсутствие акселераторов. Если бы потребовалась ещё одна кнопка выбора для переключения, то её можно было бы создать таким же образом в этой же группе с item_type равным "/View/Low Resolution".

ItemFactoryEntry массивы

 Массив записей для определения меню. Ниже приведен пример объявления массива:

static GtkItemFactoryEntry entries[] = {
  { "/_File",         NULL,      NULL,         0, "<Branch>" },
  { "/File/tear1",    NULL,      NULL,         0, "<Tearoff>" },
  { "/File/_New",     "<CTRL>N", new_file,     1, "<Item>" },
  { "/File/_Open...", "<CTRL>O", open_file,    1, "<Item>" },
  { "/File/sep1",     NULL,      NULL,         0, "<Seperator>" },
  { "/File/_Quit",    "<CTRL>Q", quit_program, 0, "<Item>"} };

Создание ItemFactory

Массив GtkItemFactoryEntry пунктов определяет меню. Функция выглядит так:

GtkItemFactory* gtk_item_factory_new( GtkType        container_type,
                                      const gchar   *path,
                                      GtkAccelGroup *accel_group );

container_type может иметь следующие значения:

GTK_TYPE_MENU
GTK_TYPE_MENU_BAR
GTK_TYPE_OPTION_MENU

container_type определяет тип нужного вам меню, например - всплывающее меню, панель меню или меню опций (как поле со списком но  с выпавшим меню).

path определяет путь к основному меню. В основном уникальное имя основного меню должно быть заключено в треугольные скобки "<>".  Это важно для обозначения акселераторов.  Оно должно быть уникальным и для каждого меню и для каждой программы. Например программа с именем 'foo', может иметь главное меню "<FooMain>", а всплывающее меню "<FooImagePopUp>", или нечто похожее. Главное чтобы они были уникальны.

accel_group указатель на gtk_accel_group. Таблицы акселераторов устанавливаются в то время как генерируется меню. Новая группа акселераторов создаётся функцией gtk_accel_group_new().

Но это - только первый шаг.  Чтобы преобразовывать массив информации GtkItemFactoryEntry в виджеты, используется следующая функция :

void gtk_item_factory_create_items( GtkItemFactory      *ifactory,
                                    guint                n_entries,
                                    GtkItemFactoryEntry *entries,
                                    gpointer             callback_data );

*ifactory - указатель на вышеупомянутое созданное производство пунктов.

n_entries -  является числом входов в массив GtkItemFactoryEntry..

*entries - указатель на массив GtkItemFactoryEntry.

callback_data - является тем, что передают во все функции обратного вызова для всех входов с callback_action != 0.

Группа акселераторов создана, теперь нужно прикрепить меню к окну:

void gtk_window_add_accel_group( GtkWindow     *window,
                                 GtkAccelGroup *accel_group);

Использование меню и его пунктов

Следующая функция извлекает виджеты из ItemFactory:

GtkWidget* gtk_item_factory_get_widget( GtkItemFactory *ifactory,
                                        const gchar    *path );

Например, если ItemFactory имеет две записи "/File", и "/file/new", используя дорожку "/File" извлечётся виджет menu из ItemFactory. Используя путь "/File/New" извлечётся виджет menu item. Это позволяет установить начальное состояние пунктов меню. Например для установки по умолчанию кнопки выбора в путь "/Shape/Oval", код будет выглядеть так:

gtk_check_menu_item_set_active(
        GTK_CHECK_MENU_ITEM (gtk_item_factory_get_item (item_factory, "/Shape/Oval")),
        TRUE);

Для извлечения основного меню используется функция gtk_item_factory_get_item() с путем "<main>" (в независимости от использованного пути в gtk_item_factory_new()).  В случае ItemFactory, создаваемого с типом GTK_TYPE_MENU_BAR, возвращается виджет панели меню. Если тип GTK_TYPE_MENU возвращается виджет меню. Если тип GTK_TYPE_OPTION_MENU возвращается виджет меню опций.

Помните: вход определенный путем "/_File" фактически путь "/File".

Теперь вы имеете строку меню или меню и можете манипулировать ими как обсуждалось в разделе Manual Menu Creation.


<<< Previous

Home

Next >>>

Manual Menu Example

Up

Item Factory Example




GTK+ 2.0 Tutorial

<<< Previous

Menu Widget

Next >>>


Пример производства пунктов меню

Пример использования производства пунктов (item factory) GTK.

/* Начало примера меню itemfactory.c */
#include &lt;gtk/gtk.h&gt;
#include &lt;strings.h&gt;
/* Обязательный основной обратный вызов */
static void print_hello( GtkWidget *w,
                         gpointer   data )
{
  g_message ("Hello, World!\n");
}
/* Для контрольной кнопки */
static void print_toggle(gpointer   callback_data,
                         guint      callback_action,
                         GtkWidget *menu_item)
{
   g_message ("Состояние контрольной кнопки - %d\n",
              GTK_CHECK_MENU_ITEM(menu_item)-&gt;active);
}
/* Для кнопки выбора */
static void print_selected(gpointer   callback_data,
                           guint      callback_action,
                           GtkWidget *menu_item)
{
   if(GTK_CHECK_MENU_ITEM(menu_item)-&gt;active)
     g_message("Кнопка выбора %d выбрана\n", callback_action);
}
/* Наше меню, массив GtkItemFactoryEntry структур которые определяют каждый пункт меню */
static GtkItemFactoryEntry menu_items[] = {
  { "/_File",         NULL,         NULL,           0, "&lt;Branch&gt;" },
  { "/File/_New",     "&lt;control&gt;N", print_hello,    0, "<Item>" },
  { "/File/_Open",    "&lt;control&gt;O", print_hello,    0, "<Item>" },
  { "/File/_Save",    "&lt;control&gt;S", print_hello,    0, "<Item>" },
  { "/File/Save _As", NULL,         NULL,           0, "<Item>" },
  { "/File/sep1",     NULL,         NULL,           0, "&lt;Separator&gt;" },
  { "/File/Quit",     "&lt;control&gt;Q", gtk_main_quit,  0, "<Item>" },
  { "/_Options",      NULL,         NULL,           0, "&lt;Branch&gt;" },
  { "/Options/tear",  NULL,         NULL,           0, "&lt;Tearoff&gt;" },
  { "/Options/Check", NULL,         print_toggle,   1, "&lt;CheckItem&gt;" },
  { "/Options/sep",   NULL,         NULL,           0, "&lt;Separator&gt;" },
  { "/Options/Rad1",  NULL,         print_selected, 1, "&lt;RadioItem&gt;" },
  { "/Options/Rad2",  NULL,         print_selected, 2, "/Options/Rad1" },
  { "/Options/Rad3",  NULL,         print_selected, 3, "/Options/Rad1" },
  { "/_Help",         NULL,         NULL,           0, "&lt;LastBranch&gt;" },
  { "/_Help/About",   NULL,         NULL,           0, "<Item>" },
};
static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
/* Возвращает виджет панель меню, сделанный из вышеупомянутого меню */
GtkWidget *get_menubar_menu( GtkWidget  *window)
{
  GtkItemFactory *item_factory;
  GtkAccelGroup *accel_group;
  /* Создаём группу акселераторов (shortcut keys) */
  accel_group = gtk_accel_group_new ();
  /* Создаём ItemFactory (строку меню) */
  item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "&lt;main&gt;",
                                       accel_group);
  /* Эта функция генерирует пункты меню. Передает item factory,
     номер пункта в массиве, массив непосредственно, а также любые
     данные обратных вызовов для пунктов меню. */
  gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
  /* Прикрепляем новую группу акселераторов к окну. */
  gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
  /* Возвращаем фактически созданную производством пунктов (item factory) панель меню. */
  return gtk_item_factory_get_widget (item_factory, "&lt;main&gt;");
}
/* Всплывающее меню при нажатии на всплывающую кнопку */
static gint popup_cb(GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
{
   GdkEventButton *bevent = (GdkEventButton *)event;
  
   /* только нажатие кнопки */
   if(event-&gt;type != GDK_BUTTON_PRESS)
     return FALSE;
  
   /* Отображаем меню */
   gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
                  NULL, NULL, bevent-&gt;button, bevent-&gt;time);
  
   return TRUE;
}
/* Тоже что и get_menubar_menu(), но возвращает кнопку с сигналом для всплывающего меню */
GtkWidget *get_popup_menu(void)
{
   GtkItemFactory *item_factory;
   GtkWidget *button, *menu;
  
   /* Тоже что и выше, но без акселераторов */
   item_factory = gtk_item_factory_new (GTK_TYPE_MENU, "&lt;main&gt;",
                                        NULL);
   gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
   menu = gtk_item_factory_get_widget(item_factory, "&lt;main&gt;");
  
   /* Кнопка для активизации всплывающего меню */
   button = gtk_button_new_with_label("Popup");
   /* Создаём всплывающее меню при щелчке */
   g_signal_connect(G_OBJECT(button),
                    "event",
                    G_CALLBACK(popup_cb),
                    (gpointer) menu);
   return button;
}
/* То же самое, но возвращается меню опций */
GtkWidget *get_option_menu(void)
{
   GtkItemFactory *item_factory;
   GtkWidget *option_menu;
  
   /* Ещё раз, но без акселераторов */
   item_factory = gtk_item_factory_new (GTK_TYPE_OPTION_MENU, "&lt;main&gt;",
                                        NULL);
   gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
   option_menu = gtk_item_factory_get_widget(item_factory, "&lt;main&gt;");
   return option_menu;
}
/* Начальная функция */
int main( int argc,
          char *argv[] )
{
  GtkWidget *window;
  GtkWidget *main_vbox;
  GtkWidget *menubar, *option_menu, *popup_button;
 
  /* Инициализация GTK */
  gtk_init (&argc, &argv);
 
  /* Создаём окно */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit),
                    NULL);
  gtk_window_set_title (GTK_WINDOW(window), "Item Factory");
  gtk_widget_set_size_request (GTK_WIDGET(window), 300, 200);
 
  /* Создаём vbox для размещения меню */
  main_vbox = gtk_vbox_new (FALSE, 1);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 1);
  gtk_container_add (GTK_CONTAINER (window), main_vbox);
 
  /* получаем три типа меню */
  /* Помните: все три меню созданы отдельно друг от друга */
  menubar = get_menubar_menu (window);
  popup_button = get_popup_menu();
  option_menu = get_option_menu();
  
  /* Упаковываем всё вместе */
  gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0);
  gtk_box_pack_end (GTK_BOX (main_vbox), popup_button, FALSE, TRUE, 0);
  gtk_box_pack_end (GTK_BOX (main_vbox), option_menu, FALSE, TRUE, 0);
  /* Отображаем виджет */
  gtk_widget_show_all (window);
  
  /* КОНЕЦ! */
  gtk_main ();
 
  return(0);
}
/* Конец примера */

<<< Previous

Home

Next >>>

Using ItemFactory

Up

Undocumented Widgets




Option Menu


Menu Items

Check Menu Item

Radio Menu Item

Separator Menu Item

Tearoff Menu Item


Curves


Drawing Area


Font Selection Dialog


Message Dialog


Gamma Curve


Image


Plugs and Sockets


Tree View


Text View


GTK+ 2.0 Tutorial

<<< Previous

Getting Started

Next >>>


Теория Сигналов и Обратных вызовов

Note

В версии 2.0, система сигналов перемещена из GTK в GLib, поэтому функции и типы в этой секции имеют приставку "g_" а не "gtk_". Сдесь не говорится о расширениях системы сигналов GLib 2.0 по сравнению с системой сигналов GTK 1.2.

Перед подробным рассмотрением helloworld, мы обсудим сигналы (signals) и обратные вызовы (callbacks). GTK - набор инструментов управляемых событиями, это значит что процесс находясь в функции gtk_main() ждет пока не произойдет событие позволяющее передать управление соответствующей функции.

Такой контроль выполняется с использованием идеи сигналов (idea of "signals"). (Учтите что эта система сигналов не тоже самое что система сигналов Unix, хотя используется идентичная терминология). Когда происходит событие, такое как нажатие кнопки мыши, виджет создаёт ("emitted") сигнал соответствующий нажатию. Так GTK выполняет большинство своих действий. Есть сигналы которые наследуют все виджеты, например сигнал закрытия ("destroy"), а есть специфические сигналы, например сигнал кнопки переключения ("toggled").

Чтобы заставить кнопку работать, нужно настроить обработчик сигналов, который при возникновении события запустит функцию.
Это делается так:

gulong g_signal_connect( gpointer      *object,
                         const gchar   *name,
                         GCallback     func,
                         gpointer      func_data );

первый аргумент - виджет который создаёт сигнал, второй - имя сигнала, третий - функция которая должна быть вызвана этим сигналом и четвертый - данные которые передаются этой функции.

Функция определенная в третьем аргументе должна иметь общую форму:

void callback_func( GtkWidget *widget,
                    gpointer   callback_data );

где первым аргументом будет указатель на виджет, который создал сигнал, а второй - указатель на данные как последний аргумент функции g_signal_connect() как показано выше.

Выше описанная форма функции обратного вызова имеет общий вид, но некоторые виджеты имеют специфические параметры и сигналы.

Другой вызов использованный в примере helloworld:

gulong g_signal_connect_swapped( gpointer     *object,
                                 const gchar  *name,
                                 GCallback    func,
                                 gpointer     *slot_object );

g_signal_connect_swapped() тоже самое, что g_signal_connect() только в качестве единственного аргумента используется указатель на GTK объект. Для использования этой функции вызова сигнала, обратный вызов должен иметь форму

void callback_func( GtkObject *object );

где объектом обычно является виджет. Обычно обратный вызов не устанавливается для g_signal_connect_swapped(). Они обычно используются в вызовах GTK функций которые принимают как единственный аргумент сигнал виджета или объект, что и показано в примере helloworld.

Наличие двух функций вызова сигналов объясняется необходимостью обратным вызовам иметь разное количество аргументов. Многие функции в библиотеке GTK принимают только указатель GtkWidget как единственный аргумент, таким образом используя g_signal_connect_swapped() в ваших функциях, вам могут понадобится расширенные данные обратных вызовов.


<<< Previous

Home

Next >>>

Compiling Hello World

Up

Events




GTK+ 2.0 Tutorial

<<< Previous

Timeouts, IO and Idle Functions

Next >>>


Контроль IO

Изящная особенность GDK (библиотека, которая лежит в основе GTK), является способность сделать так, чтобы проверить данные относительно дескриптора файла (как возвращено open(2) или socket(2)).  Это особенно полезно для сетевых приложений. Функция:

gint gdk_input_add( gint              source,
                    GdkInputCondition condition,
                    GdkInputFunction  function,
                    gpointer          data );

Первый аргумент - дескриптор файла который вы хотите посмотреть, а второй - то, что вы хотите чтобы искал GDK. Возможные значения:

Третим аргументом как вы понимаете является ваша функция которую вы хотите вызывать, а четвертый - данные которые посылаются в  эту функцию.

Возвращаемое значение это тег который может быть использован для остановки контроля дескриптора файла используя функцию:

void gdk_input_remove( gint tag );

Функция отзыва должна быть объявлена как:

void input_callback( gpointer          data,
                     gint              source, 
                     GdkInputCondition condition );

Где source и condition тоже что и выше.


<<< Previous

Home

Next >>>

Timeouts, IO and Idle Functions

Up

Idle Functions




GTK+ 2.0 Tutorial

<<< Previous

Timeouts, IO and Idle Functions

Next >>>


Пустая функция

А что, если нужна функция которая используется, когда ничего не происходит?

gint gtk_idle_add( GtkFunction function,
                   gpointer    data );

Это заставляет GTK вызывать указанную  функцию когда ничего не происходит.

void gtk_idle_remove( gint tag );

Функция, на которую указывает первый параметр gtk_idle_add будет вызываться всякий раз, когда появляется возможность. Как и в других случаях, возвращённый FALSE остановит вызов функции.


<<< Previous

Home

Next >>>

Monitoring IO

Up

Advanced Event and Signal Handling




GTK+ 2.0 Tutorial

<<< Previous

Advanced Event and Signal Handling

Next >>>


Эмиссия Сигнала и Распространение

Signal Emission - процесс, посредством которого GTK выполняет все обработчики для определенного объекта и сигнала.

Для начала отметьте, что возвращаемое значение от эмиссии сигнала (Signal Emission) - возвращаемое значение последнего выполняемого обработчика. Так как события сигналов все имеют тип GTK_RUN_LAST, он же будет установлен (самим GTK) по умолчанию обработчиком, если вы не вызовите gtk_signal_connect_after().

Событием обработчика (говорят "button_press_event") может быть:

Некоторые последствия вышеупомянутого:


<<< Previous

Home

Next >>>

Advanced Event and Signal Handling

Up

Managing Selections




GTK+ 2.0 Tutorial

<<< Previous

Managing Selections

Next >>>


Поиск выделения (Retrieving the selection)

Поиск выделения является асинхронным процессом, который запускается вызовом функции:

gboolean gtk_selection_convert( GtkWidget *widget, 
                                GdkAtom    selection, 
                                GdkAtom    target,
                                guint32    time );

Это преобразует (converts) выделение в форму определённую target. Если вообще возможно, то аргумент time должен быть временем события которое вызвало выделение (selection). Это помогает событиям удостовериться в том, что пользователь их попросил. Однако, если это не доступно (например, если преобразование было вызвано "clicked" сигналом), то вы можете использовать постоянный GDK_CURRENT_TIME.

Когда хозяин выделения отвечает на запрос, вашему приложению посылается сигнал "selection_received".  Обработчик для этого сигнала получает указатель на структуру GtkSelectionData, которая определена как:

struct _GtkSelectionData
{
  GdkAtom selection;
  GdkAtom target;
  GdkAtom type;
  gint    format;
  guchar *data;
  gint    length;
};

selection и target значения которые вы дали в вашем вызове gtk_selection_convert(). type  является атомом, который идентифицирует тип данных, возвращенных владельцем выбора. Некоторые возможные значения : "STRING" - строка из символов latin-1, "ATOM" - ряд атомов, "INTEGER" - целое число, и т.д.. Большинство targets могут вернуть только один тип. format даёт длину единиц (например букв) в битах. Обычно вам не нужно заботится об этом получая данные. data - указатель на возвращенные данные, а length - возвращает длину данных в байтах. Если длина отрицательная, то произошла ошибка и выделение не может быть восстановлено. Это могло случится, если у выделения небыло приложения владельца, или запрашиваемая вами цель не поддерживается приложением. Буфер всегда гарантированно больше length на один байт; лишний байт всегда 0 (zero), таким образом нет необходимости копировать строку, только чтобы закончить её (nul-terminate them).

В следующем примере мы восстанавливаем специальную цель "TARGETS", которая является списком всех целей в которые может быть преобразовано выделение.

#include <stdlib.h>
#include <gtk/gtk.h>
void selection_received( GtkWidget        *widget, 
                         GtkSelectionData *selection_data, 
                         gpointer          data );
/* Вызываем обработчик когда пользователь нажал на кнопку "Get Targets" */
void get_targets( GtkWidget *widget,
                  gpointer data )
{
  static GdkAtom targets_atom = GDK_NONE;
  GtkWidget *window = (GtkWidget *)data;        
  /* Получаем атом соответствующий строке "TARGETS" */
  if (targets_atom == GDK_NONE)
    targets_atom = gdk_atom_intern ("TARGETS", FALSE);
  /* Запрос цели "TARGETS" для первичного выделения */
  gtk_selection_convert (window, GDK_SELECTION_PRIMARY, targets_atom,
                         GDK_CURRENT_TIME);
}
/* Обработчик сигнала вызванный владельцем выделения возвращает данные */
void selection_received( GtkWidget        *widget,
                         GtkSelectionData *selection_data, 
                         gpointer          data )
{
  GdkAtom *atoms;
  GList *item_list;
  int i;
  /* **** ВАЖНО **** Проверьте успешность поиска  */
  if (selection_data->length < 0)
    {
      g_print ("Selection retrieval failed\n");
      return;
    }
  /* Удостоверьтесь, что мы получили данные в ожидаемой форме */
  if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
    {
      g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
      return;
    }
  
  /* Распечатайте атомы, которые мы получили */
  atoms = (GdkAtom *)selection_data->data;
  item_list = NULL;
  for (i = 0; i < selection_data->length / sizeof(GdkAtom); i++)
    {
      char *name;
      name = gdk_atom_name (atoms[i]);
      if (name != NULL)
        g_print ("%s\n",name);
      else
        g_print ("(bad atom)\n");
    }
  return;
}
int main( int   argc,
          char *argv[] )
{
  GtkWidget *window;
  GtkWidget *button;
  
  gtk_init (&argc, &argv);
  /* Создаём окно верхнего уровня */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Event Box");
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (exit), NULL);
  /* Создаём кнопку для получения целей */
  button = gtk_button_new_with_label ("Get Targets");
  gtk_container_add (GTK_CONTAINER (window), button);
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (get_targets), (gpointer) window);
  g_signal_connect (G_OBJECT (window), "selection_received",
                    G_CALLBACK (selection_received), NULL);
  gtk_widget_show (button);
  gtk_widget_show (window);
  
  gtk_main ();
  
  return 0;
}

<<< Previous

Home

Next >>>

Managing Selections

Up

Supplying the selection




GTK+ 2.0 Tutorial

<<< Previous

Управление выделениями (Managing Selections)

Next >>>


Замещение выделения

Замещать выделение немного сложнее. Вы должны зарегистрировать обработчики которые будут вызваны по требованию выделения. Для каждой пары selection/target делается вызов:

void gtk_selection_add_target (GtkWidget           *widget, 
                               GdkAtom              selection,
                               GdkAtom              target,
                               guint                info);

виджет selection и target идентифицируют запросы, которыми этот обработчик будет управлять. Когда получен сигнал о выделении, производится сигнал "selection_get". info может использоваться как нумератор, чтобы идентифицировать определенную цель в пределах функции обратного вызова.

Функция обратного вызова имеет сигнатуру:

void  "selection_get" (GtkWidget          *widget,
                       GtkSelectionData   *selection_data,
                       guint               info,
                       guint               time);

GtkSelectionData - то же самое как выше, но на сей раз, мы ответственны за то, чем заполнить поля type, format, data и length. ( Фактически важна здесь область формата - сервер X использует это, чтобы выяснить, должны ли данные меняться байтом или нет. Обычно это будет 8 - то есть символ - или 32 - то есть целое число.) Это выполняется вызовом функции:

void gtk_selection_data_set( GtkSelectionData *selection_data,
                             GdkAtom           type,
                             gint              format,
                             guchar           *data,
                             gint              length );

Эта функция заботится о надлежащем создании копии данных, так что вам ненужно заботиться об этом. (Вы не должны в ручную заполнять  структуру GtkSelectionData.)

При запросе пользователя, требуется монопольное использование выделения, вызовом:

gboolean gtk_selection_owner_set( GtkWidget *widget,
                                  GdkAtom    selection,
                                  guint32    time );

Если другое приложение будет требовать монопольного использования выделения, то вы получите "selection_clear_event".

Как пример замещения выделения, следующая программа добавляет функциональные возможности выделения к выключателю. Когда кнопка выключателя вдавлена, программа требует первичного выделения. Поддерживаются только цели "STRING" (кроме определенных целей как "TARGETS" подаваемые непосредственно GTK). Когда цель этого требует, возвращается строка представления времени.

#include <stdlib.h>
#include <gtk/gtk.h>
#include <time.h>
#include <string.h>
GtkWidget *selection_button;
GtkWidget *selection_widget;
/* вызов, когда пользователь переключает выбор */
void selection_toggled( GtkWidget *widget,
                        gint      *have_selection )
{
  if (GTK_TOGGLE_BUTTON (widget)->active)
    {
      *have_selection = gtk_selection_owner_set (selection_widget,
                                                 GDK_SELECTION_PRIMARY,
                                                 GDK_CURRENT_TIME);
      /* если требование выделения не состоялось, возвращаем кнопку в состояние выключено */
      if (!*have_selection)
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
    }
  else
    {
      if (*have_selection)
        {
          /* Перед очисткой выделения устанавливая владельца в значение NULL,
             проверяем являемся ли мы фактическим владельцем */
          if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
            gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
                                     GDK_CURRENT_TIME);
          *have_selection = FALSE;
        }
    }
}
/* Вызываем когда другое приложение запросило выделение */
gint selection_clear( GtkWidget         *widget,
                      GdkEventSelection *event,
                      gint              *have_selection )
{
  *have_selection = FALSE;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (selection_button), FALSE);
  return TRUE;
}
/* Подставляет текущее время как выделение. */
void selection_handle( GtkWidget        *widget, 
                       GtkSelectionData *selection_data,
                       guint             info,
                       guint             time_stamp,
                       gpointer          data )
{
  gchar *timestr;
  time_t current_time;
  current_time = time (NULL);
  timestr = asctime (localtime (&current_time)); 
  /* Когда мы возвращаем единственную строку, это не должен быть нулевой предел.
     Это делается используя */
  gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
                          8, timestr, strlen (timestr));
}
int main( int   argc,
          char *argv[] )
{
  GtkWidget *window;
  static int have_selection = FALSE;
  
  gtk_init (&argc, &argv);
  /* Создаём окно верхнего уровня */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Event Box");
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (exit), NULL);
  /* Создаём кнопку переключатель действующую как выделение */
  selection_widget = gtk_invisible_new ();
  selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
  gtk_container_add (GTK_CONTAINER (window), selection_button);
  gtk_widget_show (selection_button);
  g_signal_connect (G_OBJECT (selection_button), "toggled",
                    G_CALLBACK (selection_toggled), (gpointer) &have_selection);
  g_signal_connect (G_OBJECT (selection_widget), "selection_clear_event",
                    G_CALLBACK (selection_clear), (gpointer) &have_selection);
  gtk_selection_add_target (selection_widget,
                            GDK_SELECTION_PRIMARY,
                            GDK_SELECTION_TYPE_STRING,
                            1);
  g_signal_connect (G_OBJECT (selection_widget), "selection_get",
                    G_CALLBACK (selection_handle), (gpointer) &have_selection);
  gtk_widget_show (selection_button);
  gtk_widget_show (window);
  
  gtk_main ();
  
  return 0;
}

<<< Previous

Home

Next >>>

Retrieving the selection

Up

Drag-and-drop (DND)




GTK+ 2.0 Tutorial

<<< Previous

Drag-and-drop (DND)

Next >>>


Свойства

Перетаскиваемые данные имеют следующие свойства:

Действия перетаскивания весьма очевидны, они определяют может ли виджет переместиться (после перетаскивания виджет удаляется в исходном местоположении) или он должен быть скопирован (виджет после перетаскивания находится и в новом и в старом месте прибывания), например GDK_ACTION_COPY и/или GDK_ACTION_MOVE. Есть дополнительное действие перетаскивания GDK_ACTION_LINK, которое вы изучите при достижении более высокого уровня понимания системы drag-and-drop.

Клиент определяет произвольный тип drag-and-drop более гибким, потому что ваше приложение будет определять и проверять это более точно. Вы должны установить ваши виджеты назначения, чтобы получить точные типы drag-and-drop, определённые именем и/или номером. Более надежно использовать имя, так-как  другое пиложение может использовать тот же номер для совершенно другого значения.

Посланный и полученные типы формата данных (selection target) входят в игру только в вашем запросе и полученных функциях обработчика данных. Термин selection target немного вводит в заблуждение. Этот термин адаптирован для GTK+ выделения (вырезать/копировать и вставить).  То, что фактически подразумевает selection target - тип формата данных (то есть. GdkAtom, целое число, или строка) посылаемый или полученный. Ваш запрос функции обработки данных должен определить тип (selection target) отправляемых данных и ваш обработчик данных должен обработать тип (selection target) получаемых данных.


<<< Previous

Home

Next >>>

Drag-and-drop (DND)

Up

Functions




GTK+ 2.0 Tutorial

<<< Previous

Drag-and-drop (DND)

Next >>>


Функции

Настройка исходного виджета

Функция gtk_drag_source_set() определяет ряд целевых типов операции перетаскивания на виджете.

void gtk_drag_source_set( GtkWidget            *widget,
                          GdkModifierType       start_button_mask,
                          const GtkTargetEntry *targets,
                          gint                  n_targets,
                          GdkDragAction         actions );

Параметры показывают следующее:

Параметр targets - массив следующей структуры:

struct GtkTargetEntry {
   gchar *target;
   guint  flags;
   guint  info;
 };

Поля определяют строку представляющую тип перетаскивания, дополнительные флаги и приложение заданного целочисленного идентификатора.

Если виджет больше не обязан действовать как источник для операций перетаскивания, функция gtk_drag_source_unset() может использоваться, чтобы удалить ряд типов адресата перетаскивания.

void gtk_drag_source_unset( GtkWidget *widget );

Сигналы исходных виджетов:

Исходному виджету посылают следующие сигналы в течение операции перетаскивания.

Таблица 1. Сигналы исходных виджетов

drag_begin

void (*drag_begin)(GtkWidget *widget, GdkDragContext *dc, gpointer data)

drag_motion

gboolean (*drag_motion)(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t, gpointer data)

drag_data_get

void (*drag_data_get)(GtkWidget *widget, GdkDragContext *dc, GtkSelectionData *selection_data, guint info, guint t, gpointer data)

drag_data_delete

void (*drag_data_delete)(GtkWidget *widget, GdkDragContext *dc, gpointer data)

drag_drop

gboolean (*drag_drop)(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t, gpointer data)

drag_end

void (*drag_end)(GtkWidget *widget, GdkDragContext *dc, gpointer data)

Установки виджета назначения:

gtk_drag_dest_set() - определяет виджет который может принять перетаскиваемый объект и определяет типы приёма.

gtk_drag_dest_unset() - определяет, что виджет больше не может осуществлять приём.

void gtk_drag_dest_set( GtkWidget            *widget,
                        GtkDestDefaults       flags,
                        const GtkTargetEntry *targets,
                        gint                  n_targets,
                        GdkDragAction         actions );
void gtk_drag_dest_unset( GtkWidget *widget );

Сигналы виджета назначения:

Виджету назначения посылают следующие сигналы в течение операции перетаскивания.

Таблица 2. Сигналы виджета назначения

drag_data_received

void (*drag_data_received)(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info, guint t, gpointer data)


<<< Previous

Home

Next >>>

Properties

Up

GLib




GTK+ 2.0 Tutorial

<<< Previous

Getting Started

Next >>>


События

В дополнение к механизму сигналов, описанному выше, есть ряд событий, которые отражают механизм событий X. Обратные вызовы также могут быть связаны с событиями. Вот эти события:

Чтобы ассоциировать функцию обратного вызова с одним из этих событий нужно использовать функцию g_signal_connect(), как описано выше, в качестве параметра нужно использовать название одного из событий. Формат функции обратного вызова для событий имеет отличия от аналогичных для сигналов:

gint callback_func( GtkWidget *widget,
                    GdkEvent  *event,
                    gpointer   callback_data );

GdkEvent это C объединение(union) структур, тип которых зависит от выбранного события. Для того чтобы понять какое событие произошло каждая из возможных альтернатив имеет свой тип, который отражает произошедшее событие. Многие компоненты структуры событий зависят от типа события. Возможные варианты типов событий:

  GDK_NOTHING
  GDK_DELETE
  GDK_DESTROY
  GDK_EXPOSE
  GDK_MOTION_NOTIFY
  GDK_BUTTON_PRESS
  GDK_2BUTTON_PRESS
  GDK_3BUTTON_PRESS
  GDK_BUTTON_RELEASE
  GDK_KEY_PRESS
  GDK_KEY_RELEASE
  GDK_ENTER_NOTIFY
  GDK_LEAVE_NOTIFY
  GDK_FOCUS_CHANGE
  GDK_CONFIGURE
  GDK_MAP
  GDK_UNMAP
  GDK_PROPERTY_NOTIFY
  GDK_SELECTION_CLEAR
  GDK_SELECTION_REQUEST
  GDK_SELECTION_NOTIFY
  GDK_PROXIMITY_IN
  GDK_PROXIMITY_OUT
  GDK_DRAG_ENTER
  GDK_DRAG_LEAVE
  GDK_DRAG_MOTION
  GDK_DRAG_STATUS
  GDK_DROP_START
  GDK_DROP_FINISHED
  GDK_CLIENT_EVENT
  GDK_VISIBILITY_NOTIFY
  GDK_NO_EXPOSE
  GDK_SCROLL
  GDK_WINDOW_STATE
  GDK_SETTING

Так, для ассоциации функции обратного вызова с событием можно использовать следующее:

g_signal_connect (G_OBJECT (button), "button_press_event",
                      G_CALLBACK (button_press_callback), NULL);

Это предполагает что кнопка(button) это кнопка виджета (Button widget). Когда курсор находится на кнопке и кнопка мыши нажата, то вызывается функция button_press_callback(). Эта функция может быть объявлена так:

static gint button_press_callback( GtkWidget      *widget, 
                                                GdkEventButton *event,
                                                gpointer        data );

Обратите внимание, что мы можем объявить второй аргумент как тип GdkEventButton, поскольку мы знаем, какое событие произойдет для той функции, которая будет вызвана.

Значение возвращенное функцией указывает механизму обработки событий GTK о дальнейших действиях. Возвращенное TRUE указывает на прекращение дальнейшего выполнения события. Возвращенное FALSE продолжает нормальное выполнение события. Для более подробной информации смотрите секцию Advanced Event and Signal Handling.

Детально типы данных GdkEvent, рассматриваются в GDK Event Types.

GDK selection и drag-and-drop APIs также создают множество событий которые отражены сигналами GTK. Смотрите Signals on the source widget и Signals on the destination widget для детального изучения функций обратного вызова сигналов:


<<< Previous

Home

Next >>>

Theory of Signals and Callbacks

Up

Stepping Through Hello World




GTK+ 2.0 Tutorial

<<< Previous

GLib

Next >>>


Списки двойной связи

Следующие функции используются для создания, управления и уничтожения стандартных списков двойной связи. Каждый элемент в списке содержит часть данных, вместе с указателями, которые связываются с предыдущими и следующими элементами в списке. Это позволяет легко передвигаться по списку в любом направлении.  Элемент данных имеет тип "gpointer", что означает, что данные могут быть указателем на ваши реальные данные или (через приведение) числовое значение (но не думайте, что int и gpointer имеют одинаковый размер!). Эти подпрограммы внутренне распределяют элементы списка в блоках, которые более эффективны чем распределение элементов индивидуально.

Нет функции чтобы определенно создать список.  Вместо этого просто создайте переменную типа GList* и установите его значение в NULL; NULL, как предполагается, является пустым списком.

Для добавления элементов в список используйте g_list_append(), g_list_prepend(), g_list_insert(), или g_list_insert_sorted().  Во всех случаях они принимают указатель на начало списка, и возвращают (возможно измененный) указатель на начало списка.  Таким образом, для всех операций, которые добавляют или удаляют элементы, убедитесь, что сохранили возвращенное значение!

GList *g_list_append( GList    *list,
                      gpointer  data );

Это добавляет новый элемент (со значением data) в конец списка.

GList *g_list_prepend( GList    *list,
                       gpointer  data );

Это добавляет новый элемент (со значением data) в начало списка.

GList *g_list_insert( GList    *list,
                      gpointer  data,
                      gint      position );

Это вставляет новый элемент (со значением data) в список в данной позиции. Если позиция - 0, то результат аналогичен g_list_prepend(); если позиция - меньше чем 0, то результат аналогичен g_list_append().

GList *g_list_remove( GList    *list,
                      gpointer  data );

Это удаляет из списка элемент со значением data; если элемент не существует, список не изменяется.

void g_list_free( GList *list );

Это освобождает всю память используемую GList.  Если элементы списка обращаются к динамически-распределенной памяти, то они должны быть сначала освобождены.

Есть много других функций GLib которые поддерживают списки двойной связи; для более подробной информации смотрите документацию glib.  Вот несколько из сигнатур некоторых полезных функций:

  
GList *g_list_remove_link( GList *list,
                           GList *link );
GList *g_list_reverse( GList *list );
GList *g_list_nth( GList *list,
                   gint   n );
                           
GList *g_list_find( GList    *list,
                    gpointer  data );
GList *g_list_last( GList *list );
GList *g_list_first( GList *list );
gint g_list_length( GList *list );
void g_list_foreach( GList    *list,
                     GFunc     func,
                     gpointer  user_data );

<<< Previous

Home

Next >>>

GLib

Up

Singly Linked Lists




GTK+ 2.0 Tutorial

<<< Previous

GLib

Next >>>


Односвязные списки

Многие из вышеупомянутых функций идентичны для односвязных списков. Вот список некоторых операций:

GSList *g_slist_append( GSList   *list,
                        gpointer  data );
                
GSList *g_slist_prepend( GSList   *list,
                         gpointer  data );
                             
GSList *g_slist_insert( GSList   *list,
                        gpointer  data,
                        gint      position );
                             
GSList *g_slist_remove( GSList   *list,
                        gpointer  data );
                             
GSList *g_slist_remove_link( GSList *list,
                             GSList *link );
                             
GSList *g_slist_reverse( GSList *list );
GSList *g_slist_nth( GSList *list,
                     gint    n );
                             
GSList *g_slist_find( GSList   *list,
                      gpointer  data );
                             
GSList *g_slist_last( GSList *list );
gint g_slist_length( GSList *list );
void g_slist_foreach( GSList   *list,
                      GFunc     func,
                      gpointer  user_data );
        

<<< Previous

Home

Next >>>

Doubly Linked Lists

Up

Memory Management




GTK+ 2.0 Tutorial

<<< Previous

GLib

Next >>>


Управление памятью

gpointer g_malloc( gulong size );

Эта функция - замена malloc(). Вы не должны проверять возвращаемое событие, это выполняет за вас функция. Если распределение памяти закончилось неудачей, по любой причине, приложение будет закрыто.

gpointer g_malloc0( gulong size );

То же самое как выше, но обнуляет память перед возвращением указателя.

gpointer g_realloc( gpointer mem,
                    gulong   size );

Перемещает "size" байты памяти, начинающиеся  в "mem".  Очевидно, что память должна была быть предварительно распределена.

void g_free( gpointer mem );

Облегчённый вариант предыдущего примера освобождения памяти. Если mem равно NULL, то просто возвращается NULL.

void g_mem_profile( void );

Формирует дамп конфигурации используемой памяти, но требует, чтобы вы добавили #define MEM_PROFILE к заголовку glib/gmem.c и произвели re-make и make install.

void g_mem_check( gpointer mem );

Проверки правильности местоположения памяти. Требует, чтобы вы добавили #define MEM_CHECK к заголовку gmem.c и выполнили re-make и make install.


<<< Previous

Home

Next >>>

Singly Linked Lists

Up

Timers




GTK+ 2.0 Tutorial

<<< Previous

GLib

Next >>>


Таймеры

Функции таймера могут использоваться в операциях времени (например, чтобы определять, сколько времени прошло). Вопервых вы создаёте новый таймер используя функцию g_timer_new().  После чего вы можете использовать g_timer_start() - чтобы начать рассчитывать операцию, g_timer_stop() - чтобы прекратить рассчитывать операцию и g_timer_elapsed() - чтобы определить прошедшее время.

GTimer *g_timer_new( void );
void g_timer_destroy( GTimer *timer );
void g_timer_start( GTimer  *timer );
void g_timer_stop( GTimer  *timer );
void g_timer_reset( GTimer  *timer );
gdouble g_timer_elapsed( GTimer *timer,
                         gulong *microseconds );

<<< Previous

Home

Next >>>

Memory Management

Up

String Handling




GTK+ 2.0 Tutorial

<<< Previous

GLib

Next >>>


Обработка строки

GLib определяет новый тип, названный GString, который является подобным стандартному C string, но который увеличивается автоматически. Это строковые данные с нулевым символом в конце, что дает вам - защиту от буферного переполнения, программируя ошибки в пределах вашей программы.  Это - очень важная особенность, и следовательно я рекомендую, чтобы вы использовали GStrings.  Сам GString имеет простое общее определение:

struct GString 
{
  gchar *str; /* Points to the string's current \0-terminated value. */
  gint len; /* Current length */
};

Есть множество операций, которые вы можете сделать с GString.

GString *g_string_new( gchar *init );

Эта конструкция GString, копирует строковое значение из init в GString и возвращает указатель на него. NULL можно дать как параметр для первоначально пустого GString.

void g_string_free( GString *string,
                    gint     free_segment );

Освобождаем память для данного GString. Если free_segment TRUE, то также освобождаются его символьные данные.

             
GString *g_string_assign( GString     *lval,
                          const gchar *rval );

Это копирует символы из rval в lval, удаляя предыдущее содержание lval.  Отметьте, что lval будет удлинен по мере необходимости, чтобы вместить содержание строки, в отличие от стандартной функции strcpy().

Остальная часть этих функций должна быть относительно очевидной ("_c" версии принимают символ вместо строки):

             
GString *g_string_truncate( GString *string,
                            gint     len );
                             
GString *g_string_append( GString *string,
                          gchar   *val );
                            
GString *g_string_append_c( GString *string,
                            gchar    c );
        
GString *g_string_prepend( GString *string,
                           gchar   *val );
                             
GString *g_string_prepend_c( GString *string,
                             gchar    c );
        
void g_string_sprintf( GString *string,
                       gchar   *fmt,
                       ...);
        
void g_string_sprintfa ( GString *string,
                         gchar   *fmt,
                         ... );

<<< Previous

Home

Next >>>

Timers

Up

Utility and Error Functions




GTK+ 2.0 Tutorial

<<< Previous

GLib

Next >>>


Утилиты и функции обработки ошибок

gchar *g_strdup( const gchar *str );

Функция замены strdup. Копирует оригинальное содержимое строк в недавно выделенную память и возвращает указатель на неё.

gchar *g_strerror( gint errnum );

Я рекомендую использовать это для всех сообщений об ошибках. Это намного лучше и более портативно чем perror() или другие функции. Вывод имеет обычно форму:

program name:function that failed:file or further description:strerror

Вот - пример одного такого вызова, используемого в нашей программе hello_world:

g_print("hello_world:open:%s:%s\n", filename, g_strerror(errno));

void g_error( gchar *format, ... );

Печатает сообщение об ошибках. Формат такой же как у printf, но добавляет "** ERROR **: " к вашему сообщению и выходит из программы. Используйте только для неустранимых ошибок.

void g_warning( gchar *format, ... );

Тоже что и выше но перед сообщением выводит "** WARNING **: " и не выходит из программы.

void g_message( gchar *format, ... );

Печатает "message: " перед помещенной строкой.

void g_print( gchar *format, ... );

Замена для printf().

И наша старая функция:

gchar *g_strsignal( gint signum );

Распечатывает название сигнала системы Unix, получая номер сигнала. Полезен в основном функциям обрабатывающим сигналы.

Всё вышеописанное взято из glib.h и это лишь небольшая часть. Если вы захотите документировать какую нибудь функцию сообщите мне об этом!


<<< Previous

Home

Next >>>

String Handling

Up

GTK's rc Files




GTK+ 2.0 Tutorial

<<< Previous

GTK's rc Files

Next >>>


Формат файла настройки GTK

Формат файла GTK иллюстрирован в примере ниже. Это файл testgtkrc из пакета поставки GTK, но с некоторыми дополнениями и комментариями. Вы можете включить эти комментарии в своё приложение, чтобы пользователь смог более точно настраивать его самостоятельно.

Есть несколько директив для изменения признаков виджета.

В дополнение к этому существует несколько состояний виджетов, и вы можете устанавливать разные цвета, pixmaps и шрифты в этих состояниях. Вот эти состояния:

Для установки цветов виджета используйте ключевые слова "fg" и "bg" в следующем формате:

fg[<STATE>] = { Red, Green, Blue }

Где STATE одно из вышеперечисленных состояний (PRELIGHT, ACTIVE, и т.д.), а Red, Green и Blue значения в диапазоне 0 - 1.0, например { 1.0, 1.0, 1.0 } даёт белый цвет. Значения должны быть десятичными или 0, таким образом значение "1" не будет работать, его нужно выставлять как "1.0". Неопределённые значения устанавливаются равными 0.

bg_pixmap подобен выше указанному, но вместо цветов указывается имя файла.

pixmap_path список путей разделённых ":". По этим путям будет производится поиск pixmap которые вы определите.

Директива font очень лёгкая:

font = "<font name>"

Единственная сложность заключается в выяснении строки шрифта. Использование утилиты xfontsel может в этом помочь.

"Widget_class" устанавливает стиль класса виджетов. Эти классы перечислены в кратком обзоре виджета в иерархии класса.

Директива "widget" устанавливает стиль для определенного названия виджетов, полностью игнорируя любой стиль определенный для класса виджетов. Эти виджеты зарегистрированы в приложении с помощью вызова gtk_widget_set_name(). Это позволяет вам определять атрибуты основного виджета, вместо установки атрибутов виджета для всего класса. Пожалуйста документируйте эти специальные виджеты, чтобы пользователи могли настраивать их.

Когда ключевое слово parent используется как признак, виджет берет атрибуты своего родителя в приложении.

Определяя стиль вы можете назначить предварительно определённые атрибуты.

style "main_button" = "button"
{
  font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
  bg[PRELIGHT] = { 0.75, 0, 0 }
}

В этом примере берётся стиль "button" и создаётся новый "main_button" стиль, просто изменяя шрифт и цвет.

Конечно многие атрибуты неприменимы ко всем виджетам, это просто вопрос здравого смысла, что именно может быть применено.


<<< Previous

Home

Next >>>

GTK's rc Files

Up

Example rc file




GTK+ 2.0 Tutorial

<<< Previous

GTK's rc Files

Next >>>


Пример файла настройки (rc file)

# pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
#
pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
#
# style <name> [= <name>]
# {
#   <option>
# }
#
# widget <widget_set> style <style_name>
# widget_class <widget_class_set> style <style_name>
# Список всех возможных состояний. Помните, что некоторые неприменимы к
# определённым виджетам.
#
# NORMAL - Нормальное состояние виджета, когда курсор не находится на нём и не происходит никаких
# действий мышки связанных с этим виджетом.
#
# PRELIGHT - Когда курсор мыши находится на виджете, применяется цвет определённый для данного состояния
#
# ACTIVE - Когда на виджете происходит щелчок мыши он активизируется и задействует соответствующие атрибуты.
#
# INSENSITIVE - Когда виджет находится в неактивном состоянии и не может быть активирован.
#
# SELECTED - Когда объект выделен или выбран.
#
# Учитывая эти состояния мы можем установить признаки виджетов для каждого из них
# используя следующие директивы.
#
#  fg - Устанавливает цвет переднего плана виджета.
#  bg - Устанавливает второстепенный цвет виджета.
#  bg_pixmap - Устанавливает фон виджета пиксельной карты.
#  font - Устанавливает шрифт используемый с данным виджетом.
# Это устанавливает стиль называемый "button".  Имя не имеет реального значения, как
# это установлено актуальными виджетами в начале файла.
style "window"
{
  # Устанавливаем наполнение окна определенным pixmap.
  # bg_pixmap[<STATE>] = "<pixmap filename>"
  bg_pixmap[NORMAL] = "warning.xpm"
}
style "scale"
{
  #Устанавливаем цвет переднего плана (цвет шрифта) красным в состоянии "NORMAL"
  
  fg[NORMAL] = { 1.0, 0, 0 }
  
  #Устанавливаем фоновый pixmap этого виджета, согласно родительского.
  bg_pixmap[NORMAL] = "<parent>"
}
style "button"
{
  # Показываем все возможные состояния кнопки. SELECTED единственное состояние которое не применяется.
  
  fg[PRELIGHT] = { 0, 1.0, 1.0 }
  bg[PRELIGHT] = { 0, 0, 1.0 }
  bg[ACTIVE] = { 1.0, 0, 0 }
  fg[ACTIVE] = { 0, 1.0, 0 }
  bg[NORMAL] = { 1.0, 1.0, 0 }
  fg[NORMAL] = { .99, 0, .99 }
  bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
  fg[INSENSITIVE] = { 1.0, 0, 1.0 }
}
# В этом примере, мы наследуем признаки стиля "кнопки" и затем отменяем
# шрифт и цвет фона, чтобы создать новый стиль "main_button"
style "main_button" = "button"
{
  font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
  bg[PRELIGHT] = { 0.75, 0, 0 }
}
style "toggle_button" = "button"
{
  fg[NORMAL] = { 1.0, 0, 0 }
  fg[ACTIVE] = { 1.0, 0, 0 }
  
  # Устанавливаем фон pixmap toggle_button как у родительского виджета
  bg_pixmap[NORMAL] = "<parent>"
}
style "text"
{
  bg_pixmap[NORMAL] = "marble.xpm"
  fg[NORMAL] = { 1.0, 1.0, 1.0 }
}
style "ruler"
{
  font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
}
# pixmap_path "~/.pixmaps"
# Эти установленные типы виджетов используют стиль определённый выше.
# Типы виджета перечислены в иерархии класса, но перечислены и в этом документе для
# справочной информации пользователей.
widget_class "GtkWindow" style "window"
widget_class "GtkDialog" style "window"
widget_class "GtkFileSelection" style "window"
widget_class "*Gtk*Scale" style "scale"
widget_class "*GtkCheckButton*" style "toggle_button"
widget_class "*GtkRadioButton*" style "toggle_button"
widget_class "*GtkButton*" style "button"
widget_class "*Ruler" style "ruler"
widget_class "*GtkText" style "text"
# Устанавливаем все дочерние кнопки основного окна "main window" в стиль main_button.
# Они должны быть зарегистрированы, чтобы быть использованными
widget "main window.*GtkButton*" style "main_button"

<<< Previous

Home

Next >>>

GTK's rc File Format

Up

Writing Your Own Widgets




GTK+ 2.0 Tutorial

<<< Previous

Создание собственных виджетов (Writing Your Own Widgets)

Next >>>


Анатомия виджета

Перед тем как создавать собственный виджет, нужно иметь представление как GTK работает с объектами. Здесь приведен только краткий обзор, для более полной информации обратитесь к документации.

GTK виджеты выполнены в объектно ориентированном стиле. Однако написаны полностью на стандартном C. Это очень улучшает мобильность и стабильность по использованию текущего поколения C++ компиляторов; однако означает, что автор виджета должен обратить внимание на некоторые из деталей выполнения. Информация, обычная для всех случаев одного класса виджетов (например, для всех виджетов Кнопки) сохраняется в структуре класса (class structure).  Есть только одна копия, в которой сохранена информация о сигналах класса (которые действуют как действительные функции в C).  Чтобы поддерживать наследование, первая область в структуре класса должна быть копией структуры класса родителя.  Декларация структуры класса GtkButtton:

struct _GtkButtonClass
{
  GtkContainerClass parent_class;
  void (* pressed)  (GtkButton *button);
  void (* released) (GtkButton *button);
  void (* clicked)  (GtkButton *button);
  void (* enter)    (GtkButton *button);
  void (* leave)    (GtkButton *button);
};

Когда кнопка трактуется как контейнер (например когда изменяется размер), его структура класса может быть приведена к GtkContainerClass, а важные области использованы обработчиками сигналов.

Есть также структура для каждого виджета, который создан на примере основного.  Эта структура имеет области, чтобы хранить информацию, которая является отличной для каждого виджета.  Мы назовем эту структуру object structure.  Для класса Кнопки, это выглядит так:

struct _GtkButton
{
  GtkContainer container;
  GtkWidget *child;
  guint in_button : 1;
  guint button_down : 1;
};

Отметьте, что как и в структуре класса первая область в структуре объекта должна быть копией родителя так, чтобы структура могла быть приведена к структуре объекта родительского класса как необходимо.


<<< Previous

Home

Next >>>

Writing Your Own Widgets

Up

Creating a Composite widget




GTK+ 2.0 Tutorial

<<< Previous

Writing Your Own Widgets

Next >>>


Создание сложного виджета

Введение

Один тип виджета который может вас заинтересовать, создаёт виджет на базе других виджетов GTK. Этот тип виджета не делает ничего, что не могло бы быть сделано, не создавая новые виджеты, но обеспечивает удобный способ упаковать элементы пользовательского интерфейса для многократного использования. Виджеты FileSelection и ColorSelection являются примерами такого типа виджетов.

Пример виджета который мы рассмотрим в этом разделе - Tictactoe widget, массив 3x3 кнопок переключателей который создаёт сигнал когда  задействованы три переключателя в ряд по горизонтали или вертикали.

Выбор родительского класса

Родительский класс для сложного виджета - типичный контейнерный класс, который содержит все элементы сложного виджета.  Например родительский класс виджета FileSelection - Диалоговый класс (Dialog class).  Так как наши кнопки будут упорядочены в таблице, могло бы показаться естественным сделать наш родительский класс классом Таблицы (Table class). Но к сожалению, этот вариант не работает. Создание виджета разделено на две функции - функция WIDGETNAME_new() пользовательских вызовов, и функция WIDGETNAME_init() которая выполняет основную работу инициализации виджета, который не зависит от аргументов переданных в функцию the _new(). Дочерние виджеты только вызывают функцию _init их родительского виджета. Но это разделение не работает для таблиц, когда необходимо перед созданием знать число колонок и строк.  Если мы не хотим дублировать большинство функциональных возможностей gtk_table_new() в нашем виджете Tictactoe, лучше всего избежать использование таблицы. Поэтому мы создаём таблицу из Vbox и прикрепляем её внутри VBox.

Заголовочный файл

Каждый класс виджетов имеет заголовочные файлы в которых объявляются объекты и структуры классов для виджета, наряду с общими функциями. Есть некоторые особенности о которых стоит упомянуть. Чтобы предотвратить двойные определения, мы заключаем весь заголовочный файл в:

#ifndef __TICTACTOE_H__
#define __TICTACTOE_H__
.
.
.
#endif /* __TICTACTOE_H__ */

А для поддержки C++ программ помещаем заголовочный файл в оболочку:

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
.
.
.
#ifdef __cplusplus
}
#endif /* __cplusplus */

Наряду с функциями и структурами, мы объявляем три стандартных макроопределения в нашем заголовочном файле, TICTACTOE (obj), TICTACTOE_CLASS (klass), и IS_TICTACTOE (obj), которые помещают указатель в указатель на объект или структуру класса и проверяют если объект - виджет Tictactoe соответственно.

Вот полный заголовочный файл:

/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#ifndef __TICTACTOE_H__
#define __TICTACTOE_H__
#include <gdk/gdk.h>
#include <gtk/gtkvbox.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define TICTACTOE(obj)          GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
#define TICTACTOE_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
#define IS_TICTACTOE(obj)       GTK_CHECK_TYPE (obj, tictactoe_get_type ())
typedef struct _Tictactoe       Tictactoe;
typedef struct _TictactoeClass  TictactoeClass;
struct _Tictactoe
{
  GtkVBox vbox;
  
  GtkWidget *buttons[3][3];
};
struct _TictactoeClass
{
  GtkVBoxClass parent_class;
  void (* tictactoe) (Tictactoe *ttt);
};
GtkType        tictactoe_get_type        (void);
GtkWidget*     tictactoe_new             (void);
void           tictactoe_clear           (Tictactoe *ttt);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __TICTACTOE_H__ */
      

Функция _get_type()

Продолжаем создание нашего виджета. Основная функция для каждого виджета - WIDGETNAME_get_type(). Когда вызывается эта функция,  сначала она сообщает GTK о классе виджета, а потом идентифицирует класс виджета уникальным ID. В последующие вызовы она просто  возвращает ID.

GtkType
tictactoe_get_type ()
{
  static guint ttt_type = 0;
  if (!ttt_type)
    {
      GtkTypeInfo ttt_info =
      {
        "Tictactoe",
        sizeof (Tictactoe),
        sizeof (TictactoeClass),
        (GtkClassInitFunc) tictactoe_class_init,
        (GtkObjectInitFunc) tictactoe_init,
        (GtkArgSetFunc) NULL,
        (GtkArgGetFunc) NULL
      };
      ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info);
    }
  return ttt_type;
}

Структура GtkTypeInfo имеет следующее определение:

struct _GtkTypeInfo
{
  gchar *type_name;
  guint object_size;
  guint class_size;
  GtkClassInitFunc class_init_func;
  GtkObjectInitFunc object_init_func;
  GtkArgSetFunc arg_set_func;
  GtkArgGetFunc arg_get_func;
};

Значение полей этой структуры достаточно очевидно. Мы будем игнорировать arg_set_func и arg_get_func поля: они имеют важную, но пока мало используемую роль, позволяющую устанавливать варианты виджета интерпретируемых языков. Как только GTK получает корректно заполненную копию этой структуры, можно создавать объекты виджета специфического типа.

Функция _class_init()

Функция WIDGETNAME_class_init() инициализирует поля структуры класса виджета и настраивает любые сигналы для класса. Для нашего виджета Tictactoe это выглядит так:

enum {
  TICTACTOE_SIGNAL,
  LAST_SIGNAL
};
static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
static void
tictactoe_class_init (TictactoeClass *class)
{
  GtkObjectClass *object_class;
  object_class = (GtkObjectClass*) class;
  
  tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
                                         GTK_RUN_FIRST,
                                         object_class->type,
                                         GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
                                         gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
  class->tictactoe = NULL;
}

Наш виджет имеет только один сигнал, сигнал tictactoe, который производится когда ряд, колонка, или диагональ полностью заполнены.  Не каждый сложный виджет нуждается в сигналах. Если вы новичок в программировании, вы можете перейти к следующему разделу, поскольку далее будет более сложный материал.

Функция:

gint gtk_signal_new( const gchar         *name,
                     GtkSignalRunType     run_type,
                     GtkType              object_type,
                     gint                 function_offset,
                     GtkSignalMarshaller  marshaller,
                     GtkType              return_val,
                     guint                nparams,
                     ...);

Создаёт новый сигнал. Значения параметров:

Определяя типы, используется перечисление GtkType:

typedef enum
{
  GTK_TYPE_INVALID,
  GTK_TYPE_NONE,
  GTK_TYPE_CHAR,
  GTK_TYPE_BOOL,
  GTK_TYPE_INT,
  GTK_TYPE_UINT,
  GTK_TYPE_LONG,
  GTK_TYPE_ULONG,
  GTK_TYPE_FLOAT,
  GTK_TYPE_DOUBLE,
  GTK_TYPE_STRING,
  GTK_TYPE_ENUM,
  GTK_TYPE_FLAGS,
  GTK_TYPE_BOXED,
  GTK_TYPE_FOREIGN,
  GTK_TYPE_CALLBACK,
  GTK_TYPE_ARGS,
  GTK_TYPE_POINTER,
  /* было бы хорошо, если следующие два могли бы быть удалены в конечном счете */
  GTK_TYPE_SIGNAL,
  GTK_TYPE_C_CALLBACK,
  GTK_TYPE_OBJECT
} GtkFundamentalType;

gtk_signal_new()  возвращает уникальный целочисленный идентификатор для сигнала, который мы храним в массиве tictactoe_signals, который мы индексируем используя перечисления.

После создания нашего сигнала, нужно чтобы GTK ассоциировал наш сигнал с Tictactoe class. Это делается вызовом функции gtk_object_class_add_signals(). Устанавливая указатель, который указывает на обработчик значения по умолчанию для сигнала "tictactoe" в NULL, указываем, что нет никакого действия по умолчанию.

Функция _init()

Каждый класс виджета также нуждается в функции, чтобы инициализировать структуру объекта.  Обычно, эта функция имеет справедливо ограниченную роль установки полей структуры к значениям по умолчанию. Для сложных виджетов, однако, эта функция также создает составные виджеты.

static void
tictactoe_init (Tictactoe *ttt)
{
  GtkWidget *table;
  gint i,j;
  
  table = gtk_table_new (3, 3, TRUE);
  gtk_container_add (GTK_CONTAINER(ttt), table);
  gtk_widget_show (table);
  for (i=0;i<3; i++)
    for (j=0;j<3; j++)
      {
        ttt->buttons[i][j] = gtk_toggle_button_new ();
        gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j], 
                                   i, i+1, j, j+1);
        gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
                            GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
        gtk_widget_set_size_request (ttt->buttons[i][j], 20, 20);
        gtk_widget_show (ttt->buttons[i][j]);
      }
}

И остальные...

Есть еще одна функция, которую каждый виджет  ( за исключением основных типов виджета как Bin, который не может быть проиллюстрирован) должен иметь это функция, которую пользователь вызывает, чтобы создать объект определенного типа. Это традиционный вызов WIDGETNAME_new(). В некоторых виджетах, хотя не для виджета Tictactoe, эта функция берет параметры, и делает некоторые установки, основанные на параметрах. Другие две функции являются специальными для виджета Tictactoe.

tictactoe_clear()  является общей функцией, которая сбрасывает все кнопки в виджете в позицию выключено (up). Обратите внимание на  использование gtk_signal_handler_block_by_data(), чтобы оградить наш обработчик сигнала для кнопок переключателей от излишних вызовов.

tictactoe_toggle()  является обработчиком сигнала, который вызывается, когда пользователь нажимает на кнопку. Выясняется наличие необходимой комбинации нажатых кнопок, если комбинация существует, производится сигнал "tictactoe".

GtkWidget*
tictactoe_new ()
{
  return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
}
void           
tictactoe_clear (Tictactoe *ttt)
{
  int i,j;
  for (i=0;i<3;i++)
    for (j=0;j<3;j++)
      {
        gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
                                     FALSE);
        gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
      }
}
static void
tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
{
  int i,k;
  static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
                             { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
                             { 0, 1, 2 }, { 0, 1, 2 } };
  static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
                             { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
                             { 0, 1, 2 }, { 2, 1, 0 } };
  int success, found;
  for (k=0; k<8; k++)
    {
      success = TRUE;
      found = FALSE;
      for (i=0;i<3;i++)
        {
          success = success && 
            GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
          found = found ||
            ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
        }
      
      if (success && found)
        {
          gtk_signal_emit (GTK_OBJECT (ttt), 
                           tictactoe_signals[TICTACTOE_SIGNAL]);
          break;
        }
    }
}

И наконец, пример программы, использующей наш виджет Tictactoe:

#include <gtk/gtk.h>
#include "tictactoe.h"
/* Вызывается когда строка, столбец, или диагональ заполнены */
void
win (GtkWidget *widget, gpointer data)
{
  g_print ("Yay!\n");
  tictactoe_clear (TICTACTOE (widget));
}
int 
main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *ttt;
  
  gtk_init (&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  
  gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
  
  gtk_signal_connect (GTK_OBJECT (window), "destroy",
                      GTK_SIGNAL_FUNC (gtk_exit), NULL);
  
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  /* Создаём новый Tictactoe виджет */
  ttt = tictactoe_new ();
  gtk_container_add (GTK_CONTAINER (window), ttt);
  gtk_widget_show (ttt);
  /* И присоединяем "tictactoe" сигнал */
  gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
                      GTK_SIGNAL_FUNC (win), NULL);
  gtk_widget_show (window);
  
  gtk_main ();
  
  return 0;
}

<<< Previous

Home

Next >>>

The Anatomy Of A Widget

Up

Creating a widget from scratch




GTK+ 2.0 Tutorial

<<< Previous

Writing Your Own Widgets

Next >>>


Создание виджета с нуля

Введение

В этом разделе большое внимание будет уделено самостоятельному отображению виджетов и взаимодействию с событиями. Для примера мы создадим аналог циферблата, чтобы пользователь при помощи перемещения стрелки мог устанавливать необходимое значение.

Отображение виджета на экране

Отображение виджета на экране проходит в несколько шагов. После создания виджета функцией WIDGETNAME_new(), необходимо задействовать ещё несколько функций:

Как вы наверное заметили последние две функции очень похожи - каждая отвечает за отрисовку виджета на экране. Большинство типов виджетов не беспокоят различия между двумя этими функциями. По умолчанию функция draw() в виджет классе просто генерирует событие для перерисовки площади.  Однако, некоторые типы виджетов могут сохранить работу, различая две функции. Например, если виджет имеет многоуровневые окна X, то экспозиционное событие идентифицируют окно и может перерисовать только затронутое окно, которое не доступно для вызова draw().

Контейнерные виджеты, даже если они не беспокоятся о различии сами, не могут просто использовать по умолчанию draw() функцию, потому что их дочерние виджеты могут зависеть от этих различий.  Однако, было бы расточительно дублировать код отрисовки между двумя функциями. Поэтому виджеты вызывают функцию WIDGETNAME_paint() которая выполняет основную работу по прорисовке виджета, которая в свою очередь вызывает функции draw() и expose().

В нашем примере, циферблат является не контейнерным виджетом и имеет единственное окно, поэтому мы можем по умолчанию использовать функцию draw() и только обеспечивать выполнение функции expose().

Истоки виджета циферблата

Большинство виджетов GTK берут своё начало с уже существующих виджетов. Хотя раздел называется "Создание виджета с нуля", на самом деле виджет циферблата создаётся на основе существующего виджета регулировок (Range widget). Это сделано потому, что наш виджет циферблата будет иметь интерфейс виджета масштабирования, который является специализированным потомком виджета регулировок (Range widget). Хотя код представленный ниже имеет законченную форму, не нужно думать, что это было написано ab initio способом. Кроме того не мешало бы просмотреть методы работы виджетов масштабирования.

Основы

Для начала заголовочный файл:

/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#ifndef __GTK_DIAL_H__
#define __GTK_DIAL_H__
#include <gdk/gdk.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtkwidget.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define GTK_DIAL(obj)          GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
#define GTK_DIAL_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
#define GTK_IS_DIAL(obj)       GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
typedef struct _GtkDial        GtkDial;
typedef struct _GtkDialClass   GtkDialClass;
struct _GtkDial
{
  GtkWidget widget;
  /* политика обновления (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
  guint policy : 2;
  /* Кнопка в настоящий момент нажата или 0 если нет */
  guint8 button;
  /* Размер компонентов циферблата */
  gint radius;
  gint pointer_width;
  /* ID из таймера обновления, или 0 если нет */
  guint32 timer;
  /* Текущий угол */
  gfloat angle;
  /* Сохраняем старые значения чтобы знать об изменениях */
  gfloat old_value;
  gfloat old_lower;
  gfloat old_upper;
  /* Объект настройки, который хранит данные для этого циферблата */
  GtkAdjustment *adjustment;
};
struct _GtkDialClass
{
  GtkWidgetClass parent_class;
};
GtkWidget*     gtk_dial_new                    (GtkAdjustment *adjustment);
GtkType        gtk_dial_get_type               (void);
GtkAdjustment* gtk_dial_get_adjustment         (GtkDial      *dial);
void           gtk_dial_set_update_policy      (GtkDial      *dial,
                                                GtkUpdateType  policy);
void           gtk_dial_set_adjustment         (GtkDial      *dial,
                                                GtkAdjustment *adjustment);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __GTK_DIAL_H__ */

Затем, после включения заголовочного файла, мы получаем некоторые функции для обеспечения информации о виджете и его инициализации:

#include <math.h>
#include <stdio.h>
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
#include "gtkdial.h"
#define SCROLL_DELAY_LENGTH  300
#define DIAL_DEFAULT_SIZE 100
/* Forward declarations */
[ omitted to save space ]
/* Локальные данные */
static GtkWidgetClass *parent_class = NULL;
GtkType
gtk_dial_get_type ()
{
  static GtkType dial_type = 0;
  if (!dial_type)
    {
      static const GtkTypeInfo dial_info =
      {
        "GtkDial",
        sizeof (GtkDial),
        sizeof (GtkDialClass),
        (GtkClassInitFunc) gtk_dial_class_init,
        (GtkObjectInitFunc) gtk_dial_init,
        /* reserved_1 */ NULL,
        /* reserved_1 */ NULL,
        (GtkClassInitFunc) NULL
      };
      dial_type = gtk_type_unique (GTK_TYPE_WIDGET, &dial_info);
    }
  return dial_type;
}
static void
gtk_dial_class_init (GtkDialClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
  parent_class = gtk_type_class (gtk_widget_get_type ());
  object_class->destroy = gtk_dial_destroy;
  widget_class->realize = gtk_dial_realize;
  widget_class->expose_event = gtk_dial_expose;
  widget_class->size_request = gtk_dial_size_request;
  widget_class->size_allocate = gtk_dial_size_allocate;
  widget_class->button_press_event = gtk_dial_button_press;
  widget_class->button_release_event = gtk_dial_button_release;
  widget_class->motion_notify_event = gtk_dial_motion_notify;
}
static void
gtk_dial_init (GtkDial *dial)
{
  dial->button = 0;
  dial->policy = GTK_UPDATE_CONTINUOUS;
  dial->timer = 0;
  dial->radius = 0;
  dial->pointer_width = 0;
  dial->angle = 0.0;
  dial->old_value = 0.0;
  dial->old_lower = 0.0;
  dial->old_upper = 0.0;
  dial->adjustment = NULL;
}
GtkWidget*
gtk_dial_new (GtkAdjustment *adjustment)
{
  GtkDial *dial;
  dial = gtk_type_new (gtk_dial_get_type ());
  if (!adjustment)
    adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
  gtk_dial_set_adjustment (dial, adjustment);
  return GTK_WIDGET (dial);
}
static void
gtk_dial_destroy (GtkObject *object)
{
  GtkDial *dial;
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_DIAL (object));
  dial = GTK_DIAL (object);
  if (dial->adjustment)
    gtk_object_unref (GTK_OBJECT (dial->adjustment));
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

Заметьте, что функция init() делает меньше чем в виджете Tictactoe, так как это не сложный виджет, а функция new()делает больше, так как имеет аргумент. Кроме того, заметьте что указатель на объект регулирования мы увеличиваем подсчетом ссылок, (и соответственно уменьшаем когда больше не используем).

Кроме того, есть несколько функций, чтобы управлять опциями виджета:

GtkAdjustment*
gtk_dial_get_adjustment (GtkDial *dial)
{
  g_return_val_if_fail (dial != NULL, NULL);
  g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
  return dial->adjustment;
}
void
gtk_dial_set_update_policy (GtkDial      *dial,
                             GtkUpdateType  policy)
{
  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));
  dial->policy = policy;
}
void
gtk_dial_set_adjustment (GtkDial      *dial,
                          GtkAdjustment *adjustment)
{
  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));
  if (dial->adjustment)
    {
      gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
      gtk_object_unref (GTK_OBJECT (dial->adjustment));
    }
  dial->adjustment = adjustment;
  gtk_object_ref (GTK_OBJECT (dial->adjustment));
  gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
                      (GtkSignalFunc) gtk_dial_adjustment_changed,
                      (gpointer) dial);
  gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
                      (GtkSignalFunc) gtk_dial_adjustment_value_changed,
                      (gpointer) dial);
  dial->old_value = adjustment->value;
  dial->old_lower = adjustment->lower;
  dial->old_upper = adjustment->upper;
  gtk_dial_update (dial);
}

gtk_dial_realize()

Теперь мы дошли до нового типа функций. Первая функция выполняет работу по созданию X window (X окна). Обратите внимание, что маску передают в функции gdk_window_new(), которая определяет, какие поля структуры GdkWindowAttr фактически имеют данные в них (оставшимся полям будут присвоены значения по умолчанию). Также ценность, отмечающая - способ, которым маска события виджета создана. Мы вызываем gtk_widget_get_events(), чтобы отыскать маску события которую пользователь определил для этого виджета (с помощью gtk_widget_set_events()), и добавляем событие которое нас интересует непосредственно.

После создания окна, мы устанавливаем его стиль и фон, и помещаем указатель на виджет в пользовательской области данных GdkWindow. Это позволяет GTK посылать события окна правильному виджету.

static void
gtk_dial_realize (GtkWidget *widget)
{
  GtkDial *dial;
  GdkWindowAttr attributes;
  gint attributes_mask;
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_DIAL (widget));
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  dial = GTK_DIAL (widget);
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.event_mask = gtk_widget_get_events (widget) | 
    GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
    GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
    GDK_POINTER_MOTION_HINT_MASK;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
  widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
  widget->style = gtk_style_attach (widget->style, widget->window);
  gdk_window_set_user_data (widget->window, widget);
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
}

Определение размера

Когда окно содержащее виджет отображается первый раз и каждый раз когда изменяется его расположение, GTK запрашивает дочерние виджеты о желаемом размере.  Этот запрос обрабатывается функцией gtk_dial_size_request (). Так как наш виджет не контейнерный и не имеет никаких реальных ограничений на его размер, мы только возвращаем разумное значение по умолчанию.

static void 
gtk_dial_size_request (GtkWidget      *widget,
                       GtkRequisition *requisition)
{
  requisition->width = DIAL_DEFAULT_SIZE;
  requisition->height = DIAL_DEFAULT_SIZE;
}

После того, как все виджеты запросили идеальный размер, размещение окна вычислено, и каждый дочерний виджет зарегистрирован относительно его натуральной величины. Как правило это будет необходимый размер, но если пользователь изменит размеры окна, то размер  виджета может оказаться меньше требуемого. Уведомление о размере обрабатывается функцией gtk_dial_size_allocate(). Обратите внимание, что так же как вычисление размеров некоторых составляющих частей для будущего использования, эта подпрограмма также выполняет работу сворачивания и перемещения X окна виджета в новую позицию или размер.

static void
gtk_dial_size_allocate (GtkWidget     *widget,
                        GtkAllocation *allocation)
{
  GtkDial *dial;
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_DIAL (widget));
  g_return_if_fail (allocation != NULL);
  widget->allocation = *allocation;
  if (GTK_WIDGET_REALIZED (widget))
    {
      dial = GTK_DIAL (widget);
      gdk_window_move_resize (widget->window,
                              allocation->x, allocation->y,
                              allocation->width, allocation->height);
      dial->radius = MAX(allocation->width,allocation->height) * 0.45;
      dial->pointer_width = dial->radius / 5;
    }
}

gtk_dial_expose()

Как упомянуто выше, прорисовка данного виджета выполнена обработчиком для событий экспозиции (expose events). Здесь изменений не много, только функция  gtk_draw_polygon для отрисовки указателя с трехмерным оттенением согласно цветам сохраненным в стиле виджета.

static gint
gtk_dial_expose (GtkWidget      *widget,
                 GdkEventExpose *event)
{
  GtkDial *dial;
  GdkPoint points[3];
  gdouble s,c;
  gdouble theta;
  gint xc, yc;
  gint tick_length;
  gint i;
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  if (event->count > 0)
    return FALSE;
  
  dial = GTK_DIAL (widget);
  gdk_window_clear_area (widget->window,
                         0, 0,
                         widget->allocation.width,
                         widget->allocation.height);
  xc = widget->allocation.width/2;
  yc = widget->allocation.height/2;
  /* отрисовка подсказок */
  for (i=0; i<25; i++)
    {
      theta = (i*M_PI/18. - M_PI/6.);
      s = sin(theta);
      c = cos(theta);
      tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
      
      gdk_draw_line (widget->window,
                     widget->style->fg_gc[widget->state],
                     xc + c*(dial->radius - tick_length),
                     yc - s*(dial->radius - tick_length),
                     xc + c*dial->radius,
                     yc - s*dial->radius);
    }
  /* отрисовка указателя */
  s = sin(dial->angle);
  c = cos(dial->angle);
  points[0].x = xc + s*dial->pointer_width/2;
  points[0].y = yc + c*dial->pointer_width/2;
  points[1].x = xc + c*dial->radius;
  points[1].y = yc - s*dial->radius;
  points[2].x = xc - s*dial->pointer_width/2;
  points[2].y = yc - c*dial->pointer_width/2;
  gtk_draw_polygon (widget->style,
                    widget->window,
                    GTK_STATE_NORMAL,
                    GTK_SHADOW_OUT,
                    points, 3,
                    TRUE);
  
  return FALSE;
}

Обработка событий

Остальная часть кода виджета обрабатывает различные типы событий, и не слишком отличается от того, что могло бы быть найдено во многих приложениях GTK.  Два типа событий могут произойти - или пользователь может нажать на виджет мышкой и переместиться, чтобы переместить стрелку, или значение объекта настройки (Adjustment object) может измениться из-за некоторого внешнего обстоятельства.

Если пользователь нажал на виджет, мы выясняем был ли щелчок возле стрелки, ели это так, то заносим кнопку которую нажал пользователь в поле "кнопка" структуры виджета и захватываем все события связанные с мышкой с помощью вызова gtk_grab_add().  Последующее движение мыши заставляет повторно вычислить значение управления (функцией gtk_dial_update_mouse). В зависимости от политики, которая была установлена, событие "value_changed" генерируется немедленно (GTK_UPDATE_CONTINUOUS), после задержки таймера, добавленного gtk_timeout_add() (GTK_UPDATE_DELAYED), или только когда кнопка отпущена (GTK_UPDATE_DISCONTINUOUS).

static gint
gtk_dial_button_press (GtkWidget      *widget,
                       GdkEventButton *event)
{
  GtkDial *dial;
  gint dx, dy;
  double s, c;
  double d_parallel;
  double d_perpendicular;
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  dial = GTK_DIAL (widget);
  /* Определяем нажата ли кнопка в области стрелки - мы выполнем это 
     определяя параллельное и перпендикулярное положение точки нажатия
     от линии проходящей через стрелку */
  
  dx = event->x - widget->allocation.width / 2;
  dy = widget->allocation.height / 2 - event->y;
  
  s = sin(dial->angle);
  c = cos(dial->angle);
  
  d_parallel = s*dy + c*dx;
  d_perpendicular = fabs(s*dx - c*dy);
  
  if (!dial->button &&
      (d_perpendicular < dial->pointer_width/2) &&
      (d_parallel > - dial->pointer_width))
    {
      gtk_grab_add (widget);
      dial->button = event->button;
      gtk_dial_update_mouse (dial, event->x, event->y);
    }
  return FALSE;
}
static gint
gtk_dial_button_release (GtkWidget      *widget,
                          GdkEventButton *event)
{
  GtkDial *dial;
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  dial = GTK_DIAL (widget);
  if (dial->button == event->button)
    {
      gtk_grab_remove (widget);
      dial->button = 0;
      if (dial->policy == GTK_UPDATE_DELAYED)
        gtk_timeout_remove (dial->timer);
      
      if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
          (dial->old_value != dial->adjustment->value))
        gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
    }
  return FALSE;
}
static gint
gtk_dial_motion_notify (GtkWidget      *widget,
                         GdkEventMotion *event)
{
  GtkDial *dial;
  GdkModifierType mods;
  gint x, y, mask;
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  dial = GTK_DIAL (widget);
  if (dial->button != 0)
    {
      x = event->x;
      y = event->y;
      if (event->is_hint || (event->window != widget->window))
        gdk_window_get_pointer (widget->window, &x, &y, &mods);
      switch (dial->button)
        {
        case 1:
          mask = GDK_BUTTON1_MASK;
          break;
        case 2:
          mask = GDK_BUTTON2_MASK;
          break;
        case 3:
          mask = GDK_BUTTON3_MASK;
          break;
        default:
          mask = 0;
          break;
        }
      if (mods & mask)
        gtk_dial_update_mouse (dial, x,y);
    }
  return FALSE;
}
static gint
gtk_dial_timer (GtkDial *dial)
{
  g_return_val_if_fail (dial != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
  if (dial->policy == GTK_UPDATE_DELAYED)
    gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
  return FALSE;
}
static void
gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
{
  gint xc, yc;
  gfloat old_value;
  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));
  xc = GTK_WIDGET(dial)->allocation.width / 2;
  yc = GTK_WIDGET(dial)->allocation.height / 2;
  old_value = dial->adjustment->value;
  dial->angle = atan2(yc-y, x-xc);
  if (dial->angle < -M_PI/2.)
    dial->angle += 2*M_PI;
  if (dial->angle < -M_PI/6)
    dial->angle = -M_PI/6;
  if (dial->angle > 7.*M_PI/6.)
    dial->angle = 7.*M_PI/6.;
  dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
    (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
  if (dial->adjustment->value != old_value)
    {
      if (dial->policy == GTK_UPDATE_CONTINUOUS)
        {
          gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
        }
      else
        {
          gtk_widget_draw (GTK_WIDGET(dial), NULL);
          if (dial->policy == GTK_UPDATE_DELAYED)
            {
              if (dial->timer)
                gtk_timeout_remove (dial->timer);
              dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
                                             (GtkFunction) gtk_dial_timer,
                                             (gpointer) dial);
            }
        }
    }
}

Изменения настройки (Adjustment) нашего виджета внешними средствами происходит с помощью сигналов "changed" и "value_changed".  Обработчики для этих функций вызывает gtk_dial_update(), чтобы утвердить параметры, вычислить новый угол указателя и перерисовать виджет (вызвав gtk_widget_draw()).

static void
gtk_dial_update (GtkDial *dial)
{
  gfloat new_value;
  
  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));
  new_value = dial->adjustment->value;
  
  if (new_value < dial->adjustment->lower)
    new_value = dial->adjustment->lower;
  if (new_value > dial->adjustment->upper)
    new_value = dial->adjustment->upper;
  if (new_value != dial->adjustment->value)
    {
      dial->adjustment->value = new_value;
      gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
    }
  dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
    (dial->adjustment->upper - dial->adjustment->lower);
  gtk_widget_draw (GTK_WIDGET(dial), NULL);
}
static void
gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
                              gpointer       data)
{
  GtkDial *dial;
  g_return_if_fail (adjustment != NULL);
  g_return_if_fail (data != NULL);
  dial = GTK_DIAL (data);
  if ((dial->old_value != adjustment->value) ||
      (dial->old_lower != adjustment->lower) ||
      (dial->old_upper != adjustment->upper))
    {
      gtk_dial_update (dial);
      dial->old_value = adjustment->value;
      dial->old_lower = adjustment->lower;
      dial->old_upper = adjustment->upper;
    }
}
static void
gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
                                    gpointer       data)
{
  GtkDial *dial;
  g_return_if_fail (adjustment != NULL);
  g_return_if_fail (data != NULL);
  dial = GTK_DIAL (data);
  if (dial->old_value != adjustment->value)
    {
      gtk_dial_update (dial);
      dial->old_value = adjustment->value;
    }
}

Возможные Расширения

Виджет циферблата  (Dial widget) который мы описали имеет 670 строк кода. Однако, есть еще довольно много расширений, которые можно добавить этому виджету:


<<< Previous

Home

Next >>>

Creating a Composite widget

Up

Learning More




GTK+ 2.0 Tutorial

<<< Previous

Writing Your Own Widgets

Next >>>


Дальнейшее изучение

Только небольшая часть некоторых деталей вовлеченных в создание виджета была затронута выше. Если вы хотите создать свой виджет, то лучшим способом для изучения данного процесса, являются непосредственно исходные тексты GTK. Задайте себе несколько вопросов о том, какой виджет вы хотите создать: Это будет контейнерный виджет? Он будет иметь собственное окно? Это модификация существующего виджета? Если да, то найдите похожий виджет и начинайте вносить изменения. Удачи!


<<< Previous

Home

Next >>>

Creating a widget from scratch

Up

Scribble, A Simple Example Drawing Program




GTK+ 2.0 Tutorial

<<< Previous

Scribble, A Simple Example Drawing Program

Next >>>


Обработка событий

Сигналы GTK для действий высшего уровня (high-level) мы уже обсудили, такие как выбор пункта меню. Но иногда полезно использовать сигналы низкого уровня (lower-level), такие как передвижение курсора мышки, или нажатие клавиши. Обработчики для этих сигналов имеют дополнительный параметр, который является указателем на структуру, содержащую информацию о событии. Например, обработчики события движения передают указатель на структуру GdkEventMotion, которая частично выглядит так:

struct _GdkEventMotion
{
  GdkEventType type;
  GdkWindow *window;
  guint32 time;
  gdouble x;
  gdouble y;
  ...
  guint state;
  ...
};

type - устанавливает тип события, в этом случае GDK_MOTION_NOTIFY, window - окно в котором произошло событие. x и y - координаты произошедшего события. state - определяет состояние модификатора, когда произошло событие ( то есть определяет, какие модифицирующие клавиши и кнопки мыши были нажаты). Это - поразрядное ИЛИ, как например:

GDK_SHIFT_MASK  
GDK_LOCK_MASK   
GDK_CONTROL_MASK
GDK_MOD1_MASK   
GDK_MOD2_MASK   
GDK_MOD3_MASK   
GDK_MOD4_MASK   
GDK_MOD5_MASK   
GDK_BUTTON1_MASK
GDK_BUTTON2_MASK
GDK_BUTTON3_MASK
GDK_BUTTON4_MASK
GDK_BUTTON5_MASK

Для других сигналов, чтобы определить, какое событие произошло, мы вызываем gtk_signal_connect(). Но мы также должны сообщить GTK о каких событиях нас следует уведомлять. Это делается функцией:

void gtk_widget_set_events (GtkWidget *widget,
                            gint      events);

Вторая часть определяет события, которыми мы интересуемся. Это - поразрядное ИЛИ констант, которые определяют различные типы событий. Типы события:

GDK_EXPOSURE_MASK
GDK_POINTER_MOTION_MASK
GDK_POINTER_MOTION_HINT_MASK
GDK_BUTTON_MOTION_MASK     
GDK_BUTTON1_MOTION_MASK    
GDK_BUTTON2_MOTION_MASK    
GDK_BUTTON3_MOTION_MASK    
GDK_BUTTON_PRESS_MASK      
GDK_BUTTON_RELEASE_MASK    
GDK_KEY_PRESS_MASK         
GDK_KEY_RELEASE_MASK       
GDK_ENTER_NOTIFY_MASK      
GDK_LEAVE_NOTIFY_MASK      
GDK_FOCUS_CHANGE_MASK      
GDK_STRUCTURE_MASK         
GDK_PROPERTY_CHANGE_MASK   
GDK_PROXIMITY_IN_MASK      
GDK_PROXIMITY_OUT_MASK     

Есть несколько нюансов которые нужно соблюсти вызывая gtk_widget_set_events(). Первое - нужно вызвать эту функцию перед тем как будет создано окно X для виджета GTK. Практически вы должны вызвать функцию сразу после создания виджета. Во вторых - виджет должен иметь ассоциированное X окно(X window).  Для эффективности, многие типы виджетов не имеют собственного окна, а отображаются в окне их родителя. Вот эти виджеты:

GtkAlignment
GtkArrow
GtkBin
GtkBox
GtkImage
GtkItem
GtkLabel
GtkPixmap
GtkScrolledWindow
GtkSeparator
GtkTable
GtkAspectFrame
GtkFrame
GtkVBox
GtkHBox
GtkVSeparator
GtkHSeparator

Чтобы перехватывать события для этих виджетов, вы должны использовать виджет EventBox. См. раздел EventBox.

Для нашей программы рисования, мы должны знать, когда кнопка мыши нажата и мышь перемещена, таким образом мы определяем GDK_POINTER_MOTION_MASK и GDK_BUTTON_PRESS_MASK. Мы также должны знать, когда перерисовать наше окно, таким образом мы определяем GDK_EXPOSURE_MASK. Хотя нам необходимо уведомление об изменении размера нашего окна через конфигурацию события, мы не должны определять передачу флага GDK_STRUCTURE_MASK, потому что это автоматически определено для всех окон.

Однако, как оказывается есть проблема с определением GDK_POINTER_MOTION_MASK. Это заставит сервер добавлять новое событие передвижения в очередь событий каждый раз, когда пользователь перемещает мышь. Вообразите, что нам требуется 0.1 секунды, чтобы обработать событие движения, но в очередь X сервера поступает новое событие передвижения каждые 0.05 секунды. Если пользователь рисовал в течении 5 секунд, нам потребуется ещё 5 секунд для перехвата после освобождения кнопки мышки! Но нам нужно получить только одно событие движения для каждого случая, который мы обрабатываем. Это можно сделать определив GDK_POINTER_MOTION_HINT_MASK.

Мы определяем GDK_POINTER_MOTION_HINT_MASK, когда сервер в первый раз сообщает нам о передвижении указателя после входа в наше окно, или после нажатия или освобождения кнопки. Последующие события движения будут подавлены, пока мы явно не спросим положение указателя, используя функцию:

GdkWindow*    gdk_window_get_pointer     (GdkWindow       *window,
                                          gint            *x,
                                          gint            *y,
                                          GdkModifierType *mask);

( Есть другая функция gtk_widget_get_pointer(), которая имеет более простой интерфейс, но оказывается, не очень полезной, так как только восстанавливает положение мышки в состояние не нажатых кнопок.)

Код установки событий для нашего окна выглядит так:

  gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
                      (GtkSignalFunc) expose_event, NULL);
  gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
                      (GtkSignalFunc) configure_event, NULL);
  gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
                      (GtkSignalFunc) motion_notify_event, NULL);
  gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
                      (GtkSignalFunc) button_press_event, NULL);
  gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
                         | GDK_LEAVE_NOTIFY_MASK
                         | GDK_BUTTON_PRESS_MASK
                         | GDK_POINTER_MOTION_MASK
                         | GDK_POINTER_MOTION_HINT_MASK);

Мы сохраним "expose_event" и "configure_event" обработчики позже. Обработчики "motion_notify_event" и "button_press_event" довольно просты:

static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
{
  if (event->button == 1 && pixmap != NULL)
      draw_brush (widget, event->x, event->y);
  return TRUE;
}
static gint
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
  int x, y;
  GdkModifierType state;
  if (event->is_hint)
    gdk_window_get_pointer (event->window, &x, &y, &state);
  else
    {
      x = event->x;
      y = event->y;
      state = event->state;
    }
    
  if (state & GDK_BUTTON1_MASK && pixmap != NULL)
    draw_brush (widget, x, y);
  
  return TRUE;
}

<<< Previous

Home

Next >>>

Scribble, A Simple Example Drawing Program

Up

The DrawingArea Widget, And Drawing




GTK+ 2.0 Tutorial

<<< Previous

Scribble, A Simple Example Drawing Program

Next >>>


Виджет DrawingArea и рисование

Теперь мы приступаем к процессу рисования на экране. Виджет, который мы используем для этого, называется DrawingArea (область рисования). Виджет области рисования - по существу простое окно X. Это чистый холст в котором мы можем рисовать то, что мы хотим. Область рисования создаётся вызовом:

GtkWidget* gtk_drawing_area_new        (void);

Размер виджета по умолчанию может быть определён вызовом:

void       gtk_drawing_area_size       (GtkDrawingArea      *darea,
                                        gint                 width,
                                        gint                 height);

Этот размер может быть изменён вызовом функции gtk_widget_set_size_request(), а размер определённый с помощью этой функции может быть изменен пользователем с помощью простого увеличения или уменьшения окна содержащего область рисования.

Должно быть отмечено, что, когда мы создаем виджет DrawingArea, мы полностью ответственны за отрисовку содержимого. Если наше окно скрыто, то при открытии мы получаем событие экспозиции и должны перерисовать то, что было предварительно скрыто.

Необходимо помнить все, что было нарисовано на экране, чтобы можно было должным образом перерисовать. Кроме того, может быть визуально неприятно, если части окна очищаются и перерисовываются шаг за шагом. Решение этой проблемы заключается в использовании закадрового backing pixmap. Вместо того, чтобы рисовать непосредственно на экране, мы рисуем на изображении сохраненном в памяти сервера, но не отображенном на экране, затем когда изменения изображения или новые части изображения отображены, мы копируем необходимые части на экран.

Создание закадрового pixmap, выполняется функцией:

GdkPixmap* gdk_pixmap_new               (GdkWindow  *window,
                                         gint        width,
                                         gint        height,
                                         gint        depth);

Параметр window определяет окно GDK, от которого этот pixmap берет некоторые из его свойств. width и height определяет размер pixmap. depth определяет глубину цвета (color depth) - число битов в пикселах, для нового окна. Если глубина будет определена как -1, то будет соответствовать глубине window.

Мы создаем pixmap в нашем "configure_event" обработчике. Это событие производится всякий раз, когда окно изменяет размер, включая первоначальное создание.

/* Резервирование pixmap для области рисования */
static GdkPixmap *pixmap = NULL;
/* Создание нового backing pixmap соответствующего размера */
static gint
configure_event (GtkWidget *widget, GdkEventConfigure *event)
{
  if (pixmap)
    gdk_pixmap_unref(pixmap);
  pixmap = gdk_pixmap_new(widget->window,
                          widget->allocation.width,
                          widget->allocation.height,
                          -1);
  gdk_draw_rectangle (pixmap,
                      widget->style->white_gc,
                      TRUE,
                      0, 0,
                      widget->allocation.width,
                      widget->allocation.height);
  return TRUE;
}

Функция gdk_draw_rectangle() очищает pixmap инициализируя белым. Подробней об этом чуть ниже.

Наш обработчик события экспонирования просто копирует необходимую часть pixmap на дисплей (мы определяем область, которую должны перерисовать при использовании события->площадь области для события экспонирования):

/* Перерисовываем экран используя backing pixmap */
static gint
expose_event (GtkWidget *widget, GdkEventExpose *event)
{
  gdk_draw_pixmap(widget->window,
                  widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
                  pixmap,
                  event->area.x, event->area.y,
                  event->area.x, event->area.y,
                  event->area.width, event->area.height);
  return FALSE;
}

Теперь мы знаем как применить обновление экрана используя нашу карту пикселей (pixmap), но как фактически рисовать необходимый нам материал на нашей карте (pixmap)? Есть большое количество вызовов в библиотеках GTK's GDK для рисования drawables. drawable - это просто что то, что может быть нарисовано. Это может быть окно, карта пикселей, или точечный рисунок (черно-белое изображение). Мы уже видели два таких вызова выше gdk_draw_rectangle() и gdk_draw_pixmap(). Вот полный список:

gdk_draw_line ()
gdk_draw_rectangle ()
gdk_draw_arc ()
gdk_draw_polygon ()
gdk_draw_string ()
gdk_draw_text ()
gdk_draw_pixmap ()
gdk_draw_bitmap ()
gdk_draw_image ()
gdk_draw_points ()
gdk_draw_segments ()

См. справочную документацию или файл заголовка <gdk/gdk.h> для получения детальной информации относительно этих функций. Все эти функции имеют те же самые два аргумента. Первый параметр - drawable, второй параметр - graphics context (GC).

Графический контекст инкапсулирует информацию о цвете переднего и заднего фона, а также о толщине линии. GDK имеет полный набор функций для того, чтобы создавать и изменять графические контексты , но сохраняя простоту, мы будем использовать только предопределенные графические контексты. Каждый виджет имеет связанный стиль. (Который может быть изменен в gtkrc файле, см. раздел GTK's rc file.) Он, между прочим, хранит множество графического контекста. Некоторые примеры доступа к этим графическим контекстам:

widget->style->white_gc
widget->style->black_gc
widget->style->fg_gc[GTK_STATE_NORMAL]
widget->style->bg_gc[GTK_WIDGET_STATE(widget)]

Поля fg_gc, bg_gc, dark_gc, и light_gc индексированы параметром типа GtkStateType, который может принимать значения:

GTK_STATE_NORMAL,
GTK_STATE_ACTIVE,
GTK_STATE_PRELIGHT,
GTK_STATE_SELECTED,
GTK_STATE_INSENSITIVE

Например, для GTK_STATE_SELECTED цвет символа имеет значение по умолчанию - белый, а цвет фона - темно-синий.

Наша функция draw_brush(), для фактической отрисовки на дисплей:

/* Рисуем прямоугольник на экране */
static void
draw_brush (GtkWidget *widget, gdouble x, gdouble y)
{
  GdkRectangle update_rect;
  update_rect.x = x - 5;
  update_rect.y = y - 5;
  update_rect.width = 10;
  update_rect.height = 10;
  gdk_draw_rectangle (pixmap,
                      widget->style->black_gc,
                      TRUE,
                      update_rect.x, update_rect.y,
                      update_rect.width, update_rect.height);
  gtk_widget_draw (widget, &update_rect);
}

После того, как мы нарисовали прямоугольник перенесенный на pixmap, вызываем функцию:

void  gtk_widget_draw(GtkWidget           *widget,
                              GdkRectangle        *area);

которая сообщает X, что область полученная параметром area должна быть обновлена. X в конечном счете генерирует событие экспонирования (возможно объединение областей, которые передаются в нескольких вызовах gtk_widget_draw()) которое вызовет наш обработчик событий экспонирования, чтобы скопировать необходимые участки на экран.

Вот мы и рассмотрели всю программу для рисования, за исключением простых деталей, таких как создание основного окна.


<<< Previous

Home

Next >>>

Event Handling

Up

Adding XInput support




GTK+ 2.0 Tutorial

<<< Previous

Scribble, A Simple Example Drawing Program

Next >>>


Добавление поддержки XInput

Устройства ввода (например, графические планшеты), позволяющие рисовать удобнее и проще, чем мышью, последнее время стали намного дешевле . Самый простой метод использования подобных устройств - замена мыши, но следует заметить, что также существуют другие преимущества :

Для детальной информации о дополнительных возможностях XInput, смотрите XInput HOWTO.

Анализируя, например, структуру GdkEventMotion мы видим, что она имеет поля для хранения информации об устройствах с дополнительными возможностями.

struct _GdkEventMotion
{
  GdkEventType type;
  GdkWindow *window;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble pressure;
  gdouble xtilt;
  gdouble ytilt;
  guint state;
  gint16 is_hint;
  GdkInputSource source;
  guint32 deviceid;
};

pressure определяет давление, задаваемое числом между 0 и 1. xtilt и ytilt могут иметь значения между -1 и 1, соответствующие углу наклона в каждом из направлений. source и deviceid указывают на устройство, связанное с событием. source предоставляет простую информацию об устройстве. Она может иметь такие значения:

GDK_SOURCE_MOUSE
GDK_SOURCE_PEN
GDK_SOURCE_ERASER
GDK_SOURCE_CURSOR

deviceid - уникальный ID устройства. Оно может быть использовано для выяснения дальнейшей информации об устройстве с помощью вызова  gdk_input_list_devices() (см. ниже). Специальное значение GDK_CORE_POINTER чаще всего указывает простую мышь.

Расширение возможностей устройства

Для того, чтобы дать GTK понять о нашем желании использовать дополнительную информацию устройства ввода, нужно всего-то добавить одну строку в программу:

gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);

Используя значение GDK_EXTENSION_EVENTS_CURSOR мы указываем, что мы заинтересованы в дополнительных событиях устройства, но только если мы сами не должны рисовать свой собственный курсор. См. Дальнейшие исследования для выяснения более подробной информации о рисовании курсора. Также можно использовать GDK_EXTENSION_EVENTS_ALL при желании рисовать свои курсоры или GDK_EXTENSION_EVENTS_NONE для возврата к настройкам по умолчанию.

Но это ещё не всё. По умолчанию дополнительные возможности устройств не включены: мы должны пользователям предоставить некий механизм для конфигурирования устройств. Для автоматизации этого процесса GTK предоставляет виджет InputDialog. Предоставленный код демонстрирует работу с InputDialog:

void
input_dialog_destroy (GtkWidget *w, gpointer data)
{
  *((GtkWidget **)data) = NULL;
}
void
create_input_dialog ()
{
  static GtkWidget *inputd = NULL;
  if (!inputd)
    {
      inputd = gtk_input_dialog_new();
      gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
                          (GtkSignalFunc)input_dialog_destroy, &inputd);
      gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
                                 "clicked",
                                 (GtkSignalFunc)gtk_widget_hide,
                                 GTK_OBJECT(inputd));
      gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
      gtk_widget_show (inputd);
    }
  else
    {
      if (!GTK_WIDGET_MAPPED(inputd))
        gtk_widget_show(inputd);
      else
        gdk_window_raise(inputd->window);
    }
}

(Следует заметить, что после уничтожения диалога, мы не храним указатель на него. Это гарантирует отсутствие ошибки сегментации.)

InputDialog имеет две кнопки: "Закрыть" и "Сохранить", которые ничего в принципе не делают: "Закрыть" прячет диалог, "Сохранить" - спрятана.

Использование информации устройства с расширенными возможностями

Устройство включено - можно начинать пользоваться дополнительными возможностями. В принципе, использование этой информации - вполне безопасное действие, т.к. полученные данные будут вполне вменяемы, даже если устройство не было включено.

Следует использовать gdk_input_window_get_pointer(), а не gdk_window_get_pointer, т.к. последний вызов не возвращает дополнительную информацию устройства.

void gdk_input_window_get_pointer( GdkWindow       *window,
                                   guint32         deviceid,
                                   gdouble         *x,
                                   gdouble         *y,
                                   gdouble         *pressure,
                                   gdouble         *xtilt,
                                   gdouble         *ytilt,
                                   GdkModifierType *mask)

При вызове функции следует указать ID устройства и окно. Обычно ID берётся из поля deviceid структуры события. Опять же, даже при наличии обычного устройства ввода (мышь) полученные данные будут корректными. Просто event->deviceid будет иметь значение GDK_CORE_POINTER.

Обработчики событий нажатия кнопки или движения особо не меняются - лишь добавляется обработка дополнительной информации.

static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
{
  print_button_press (event->deviceid);
  if (event->button == 1 && pixmap != NULL)
    draw_brush (widget, event->source, event->x, event->y, event->pressure);
  return TRUE;
}
static gint
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
  gdouble x, y;
  gdouble pressure;
  GdkModifierType state;
  if (event->is_hint)
    gdk_input_window_get_pointer (event->window, event->deviceid,
                                  &x, &y, &pressure, NULL, NULL, &state);
  else
    {
      x = event->x;
      y = event->y;
      pressure = event->pressure;
      state = event->state;
    }
  if (state & GDK_BUTTON1_MASK && pixmap != NULL)
    draw_brush (widget, event->source, x, y, pressure);
  return TRUE;

Так же следует как-нибудь использовать полученную информацию. Скажем, функция draw_brush() рисует разными цветами в зависимости от event->source и меняет размер кисти в зависимости от давления.

/* Draw a rectangle on the screen, size depending on pressure,
   and color on the type of device */
static void
draw_brush (GtkWidget *widget, GdkInputSource source,
            gdouble x, gdouble y, gdouble pressure)
{
  GdkGC *gc;
  GdkRectangle update_rect;
  switch (source)
    {
    case GDK_SOURCE_MOUSE:
      gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
      break;
    case GDK_SOURCE_PEN:
      gc = widget->style->black_gc;
      break;
    case GDK_SOURCE_ERASER:
      gc = widget->style->white_gc;
      break;
    default:
      gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
    }
  update_rect.x = x - 10 * pressure;
  update_rect.y = y - 10 * pressure;
  update_rect.width = 20 * pressure;
  update_rect.height = 20 * pressure;
  gdk_draw_rectangle (pixmap, gc, TRUE,
                      update_rect.x, update_rect.y,
                      update_rect.width, update_rect.height);
  gtk_widget_draw (widget, &update_rect);
}

Выяснение дополнительной информации об устройстве

В качестве примера приведём код, который показывает имя устройства при нажатии на кнопку. Для выяснения имени используется функция

GList *gdk_input_list_devices (void);

, которая возвращает список (GList из библиотеки GLib) структур GdkDeviceInfo. GdkDeviceInfo определена как:

struct _GdkDeviceInfo
{
  guint32 deviceid;
  gchar *name;
  GdkInputSource source;
  GdkInputMode mode;
  gint has_cursor;
  gint num_axes;
  GdkAxisUse *axes;
  gint num_keys;
  GdkDeviceKey *keys;
};

Скорее всего большенство полей этой структуры будет Вами проигнорировано до тех пор, пока Вам не нужно сохранение конфигурации XInput. В данный момент нас интересует поле name, которое представляет из себя имя, присвоенное устройству X'ми. В свою очередь, если has_cursor равен false, нам следует рисовать курсор самим, но т.к. мы указали GDK_EXTENSION_EVENTS_CURSOR нам не следует об этом беспокоится.

Функция print_button_press() просто итерирует по возвращённому списку до тех пор пока не найдёт совпадение.

static void
print_button_press (guint32 deviceid)
{
  GList *tmp_list;
  /* gdk_input_list_devices returns an internal list, so we shouldn't
     free it afterwards */
  tmp_list = gdk_input_list_devices();
  while (tmp_list)
    {
      GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
      if (info->deviceid == deviceid)
        {
          printf("Button press on device '%s'\n", info->name);
          return;
        }
      tmp_list = tmp_list->next;
    }

Описанное выше - последний шаг включения поддержки "XInput" в нашей программе.

Дальнейшие исследования

Не смотря на то, что наша программа достаточно не плохо поддерживает XInput, не хватает того, что мы бы желали видеть в полноценной программе. Во-первых, пользователь скорее всего не захочет конфигурировать устройство ввода при каждом запуске программы - мы должны позволить сохранить конфигурацию. Это достигается итерированием по результату gdk_input_list_devices() и записью результата в файл.

Для восстановления состояния при загрузке программы GDK предоставляет следующие функции:

gdk_input_set_extension_events()
gdk_input_set_source()
gdk_input_set_mode()
gdk_input_set_axes()
gdk_input_set_ke

(Список, возвращённый gdk_input_list_devices() не должен изменяться на прямую.) Пример подобной программы - gsumi (доступна:http://www.msc.cornell.edu/~otaylor/gsumi/) Конечно, было бы прекрасно иметь стандартный метод выполнения подобной процедуры, но, наверное, это задача библиотек более высокого уровня, скажем GNOME.

Другой не малый недостаток - отсутствие курсоров. Платформы, отличные от XFree86, на данный момент не позволяют одновременное использование устройства ввода как простую мышь и специальное устройство, используемое напрямую из приложения. Подробнее: XInput-HOWTO. Это означает, что если автор приложения желает сделать своё приложение более универсальным, нужно курсоры рисовать самому.

Приложение, само желающее рисовать курсоры, должно сделать 2 вещи: определить требует ли устройство ввода прорисовки курсора и состояние устройства ввода (ведь приложение должно вести себя натурально: курсор пропадает, если стилус не дотрагивается до планшета, и появляется при контакте с планшетом). Первое достигается поиском устройства в списке по имени. Второе - используя событие "proximity_out". Пример прорисовки собственных курсоров может быть найден в программе "testinput" из поставки GTK.


<<< Previous

Home

Next >>>

The DrawingArea Widget, And Drawing

Up

Tips For Writing GTK Applications




GtkWidget

void GtkWidget::show	(GtkWidget *,
                    	 gpointer);
void GtkWidget::hide	(GtkWidget *,
                    	 gpointer);
void GtkWidget::map	(GtkWidget *,
                   	 gpointer);
void GtkWidget::unmap	(GtkWidget *,
                     	 gpointer);
void GtkWidget::realize	(GtkWidget *,
                       	 gpointer);
void GtkWidget::unrealize	(GtkWidget *,
                         	 gpointer);
void GtkWidget::draw	(GtkWidget *,
                    	 ggpointer,
                    	 gpointer);
void GtkWidget::draw-focus	(GtkWidget *,
                          	 gpointer);
void GtkWidget::draw-default	(GtkWidget *,
                            	 gpointer);
void GtkWidget::size-request	(GtkWidget *,
                            	 ggpointer,
                            	 gpointer);
void GtkWidget::size-allocate	(GtkWidget *,
                             	 ggpointer,
                             	 gpointer);
void GtkWidget::state-changed	(GtkWidget *,
                             	 GtkStateType,
                             	 gpointer);
void GtkWidget::parent-set	(GtkWidget *,
                          	 GtkObject *,
                          	 gpointer);
void GtkWidget::style-set	(GtkWidget *,
                         	 GtkStyle *,
                         	 gpointer);
void GtkWidget::add-accelerator	(GtkWidget *,
                               	 gguint,
                               	 GtkAccelGroup *,
                               	 gguint,
                               	 GdkModifierType,
                               	 GtkAccelFlags,
                               	 gpointer);
void GtkWidget::remove-accelerator	(GtkWidget *,
                                  	 GtkAccelGroup *,
                                  	 gguint,
                                  	 GdkModifierType,
                                  	 gpointer);
gboolean GtkWidget::event	(GtkWidget *,
                         	 GdkEvent *,
                         	 gpointer);
gboolean GtkWidget::button-press-event	(GtkWidget *,
                                      	 GdkEvent *,
                                      	 gpointer);
gboolean GtkWidget::button-release-event	(GtkWidget *,
                                        	 GdkEvent *,
                                        	 gpointer);
gboolean GtkWidget::motion-notify-event	(GtkWidget *,
                                       	 GdkEvent *,
                                       	 gpointer);
gboolean GtkWidget::delete-event	(GtkWidget *,
                                	 GdkEvent *,
                                	 gpointer);
gboolean GtkWidget::destroy-event	(GtkWidget *,
                                 	 GdkEvent *,
                                 	 gpointer);
gboolean GtkWidget::expose-event	(GtkWidget *,
                                	 GdkEvent *,
                                	 gpointer);
gboolean GtkWidget::key-press-event	(GtkWidget *,
                                   	 GdkEvent *,
                                   	 gpointer);
gboolean GtkWidget::key-release-event	(GtkWidget *,
                                     	 GdkEvent *,
                                     	 gpointer);
gboolean GtkWidget::enter-notify-event	(GtkWidget *,
                                      	 GdkEvent *,
                                      	 gpointer);
gboolean GtkWidget::leave-notify-event	(GtkWidget *,
                                      	 GdkEvent *,
                                      	 gpointer);
gboolean GtkWidget::configure-event	(GtkWidget *,
                                   	 GdkEvent *,
                                   	 gpointer);
gboolean GtkWidget::focus-in-event	(GtkWidget *,
                                  	 GdkEvent *,
                                  	 gpointer);
gboolean GtkWidget::focus-out-event	(GtkWidget *,
                                   	 GdkEvent *,
                                   	 gpointer);
gboolean GtkWidget::map-event	(GtkWidget *,
                             	 GdkEvent *,
                             	 gpointer);
gboolean GtkWidget::unmap-event	(GtkWidget *,
                               	 GdkEvent *,
                               	 gpointer);
gboolean GtkWidget::property-notify-event	(GtkWidget *,
                                         	 GdkEvent *,
                                         	 gpointer);
gboolean GtkWidget::selection-clear-event	(GtkWidget *,
                                         	 GdkEvent *,
                                         	 gpointer);
gboolean GtkWidget::selection-request-event	(GtkWidget *,
                                           	 GdkEvent *,
                                           	 gpointer);
gboolean GtkWidget::selection-notify-event	(GtkWidget *,
                                          	 GdkEvent *,
                                          	 gpointer);
void GtkWidget::selection-get	(GtkWidget *,
                             	 GtkSelectionData *,
                             	 gguint,
                             	 gpointer);
void GtkWidget::selection-received	(GtkWidget *,
                                  	 GtkSelectionData *,
                                  	 gguint,
                                  	 gpointer);
gboolean GtkWidget::proximity-in-event	(GtkWidget *,
                                      	 GdkEvent *,
                                      	 gpointer);
gboolean GtkWidget::proximity-out-event	(GtkWidget *,
                                       	 GdkEvent *,
                                       	 gpointer);
void GtkWidget::drag-begin	(GtkWidget *,
                          	 GdkDragContext *,
                          	 gpointer);
void GtkWidget::drag-end	(GtkWidget *,
                        	 GdkDragContext *,
                        	 gpointer);
void GtkWidget::drag-data-delete	(GtkWidget *,
                                	 GdkDragContext *,
                                	 gpointer);
void GtkWidget::drag-leave	(GtkWidget *,
                          	 GdkDragContext *,
                          	 gguint,
                          	 gpointer);
gboolean GtkWidget::drag-motion	(GtkWidget *,
                               	 GdkDragContext *,
                               	 ggint,
                               	 ggint,
                               	 gguint,
                               	 gpointer);
gboolean GtkWidget::drag-drop	(GtkWidget *,
                             	 GdkDragContext *,
                             	 ggint,
                             	 ggint,
                             	 gguint,
                             	 gpointer);
void GtkWidget::drag-data-get	(GtkWidget *,
                             	 GdkDragContext *,
                             	 GtkSelectionData *,
                             	 gguint,
                             	 gguint,
                             	 gpointer);
void GtkWidget::drag-data-received	(GtkWidget *,
                                  	 GdkDragContext *,
                                  	 ggint,
                                  	 ggint,
                                  	 GtkSelectionData *,
                                  	 gguint,
                                  	 gguint,
                                  	 gpointer);
gboolean GtkWidget::client-event	(GtkWidget *,
                                	 GdkEvent *,
                                	 gpointer);
gboolean GtkWidget::no-expose-event	(GtkWidget *,
                                   	 GdkEvent *,
                                   	 gpointer);
gboolean GtkWidget::visibility-notify-event	(GtkWidget *,
                                           	 GdkEvent *,
                                           	 gpointer);
void GtkWidget::debug-msg	(GtkWidget *,
                         	 GtkString *,
                         	 gpointer);

GTK+ 2.0 Tutorial
<<< PreviousGTK SignalsNext >>>

GtkData

void GtkData::disconnect	(GtkData *,
                        	 gpointer);

GtkContainer

void GtkContainer::add	(GtkContainer *,
                      	 GtkWidget *,
                      	 gpointer);
void GtkContainer::remove	(GtkContainer *,
                         	 GtkWidget *,
                         	 gpointer);
void GtkContainer::check-resize	(GtkContainer *,
                               	 gpointer);
GtkDirectionType GtkContainer::focus	(GtkContainer *,
                                    	 GtkDirectionType,
                                    	 gpointer);
void GtkContainer::set-focus-child	(GtkContainer *,
                                  	 GtkWidget *,
                                  	 gpointer);

GtkCalendar

void GtkCalendar::month-changed	(GtkCalendar *,
                               	 gpointer);
void GtkCalendar::day-selected	(GtkCalendar *,
                              	 gpointer);
void GtkCalendar::day-selected-double-click	(GtkCalendar *,
                                           	 gpointer);
void GtkCalendar::prev-month	(GtkCalendar *,
                            	 gpointer);
void GtkCalendar::next-month	(GtkCalendar *,
                            	 gpointer);
void GtkCalendar::prev-year	(GtkCalendar *,
                           	 gpointer);
void GtkCalendar::next-year	(GtkCalendar *,
                           	 gpointer);

GtkEditable

void GtkEditable::changed	(GtkEditable *,
                         	 gpointer);
void GtkEditable::insert-text	(GtkEditable *,
                             	 GtkString *,
                             	 ggint,
                             	 ggpointer,
                             	 gpointer);
void GtkEditable::delete-text	(GtkEditable *,
                             	 ggint,
                             	 ggint,
                             	 gpointer);
void GtkEditable::activate	(GtkEditable *,
                          	 gpointer);
void GtkEditable::set-editable	(GtkEditable *,
                              	 gboolean,
                              	 gpointer);
void GtkEditable::move-cursor	(GtkEditable *,
                             	 ggint,
                             	 ggint,
                             	 gpointer);
void GtkEditable::move-word	(GtkEditable *,
                           	 ggint,
                           	 gpointer);
void GtkEditable::move-page	(GtkEditable *,
                           	 ggint,
                           	 ggint,
                           	 gpointer);
void GtkEditable::move-to-row	(GtkEditable *,
                             	 ggint,
                             	 gpointer);
void GtkEditable::move-to-column	(GtkEditable *,
                                	 ggint,
                                	 gpointer);
void GtkEditable::kill-char	(GtkEditable *,
                           	 ggint,
                           	 gpointer);
void GtkEditable::kill-word	(GtkEditable *,
                           	 ggint,
                           	 gpointer);
void GtkEditable::kill-line	(GtkEditable *,
                           	 ggint,
                           	 gpointer);
void GtkEditable::cut-clipboard	(GtkEditable *,
                               	 gpointer);
void GtkEditable::copy-clipboard	(GtkEditable *,
                                	 gpointer);
void GtkEditable::paste-clipboard	(GtkEditable *,
                                 	 gpointer);

GtkNotebook

void GtkNotebook::switch-page	(GtkNotebook *,
                             	 ggpointer,
                             	 gguint,
                             	 gpointer);

GtkList

void GtkList::selection-changed	(GtkList *,
                               	 gpointer);
void GtkList::select-child	(GtkList *,
                          	 GtkWidget *,
                          	 gpointer);
void GtkList::unselect-child	(GtkList *,
                            	 GtkWidget *,
                            	 gpointer);

GtkMenuShell

void GtkMenuShell::deactivate	(GtkMenuShell *,
                             	 gpointer);
void GtkMenuShell::selection-done	(GtkMenuShell *,
                                 	 gpointer);
void GtkMenuShell::move-current	(GtkMenuShell *,
                               	 GtkMenuDirectionType,
                               	 gpointer);
void GtkMenuShell::activate-current	(GtkMenuShell *,
                                   	 gboolean,
                                   	 gpointer);
void GtkMenuShell::cancel	(GtkMenuShell *,
                         	 gpointer);

GtkToolbar

void GtkToolbar::orientation-changed	(GtkToolbar *,
                                    	 ggint,
                                    	 gpointer);
void GtkToolbar::style-changed	(GtkToolbar *,
                              	 ggint,
                              	 gpointer);

GtkButton

void GtkButton::pressed	(GtkButton *,
                       	 gpointer);
void GtkButton::released	(GtkButton *,
                        	 gpointer);
void GtkButton::clicked	(GtkButton *,
                       	 gpointer);
void GtkButton::enter	(GtkButton *,
                     	 gpointer);
void GtkButton::leave	(GtkButton *,
                     	 gpointer);

GtkItem

void GtkItem::select	(GtkItem *,
                    	 gpointer);
void GtkItem::deselect	(GtkItem *,
                      	 gpointer);
void GtkItem::toggle	(GtkItem *,
                    	 gpointer);

GtkWindow

void GtkWindow::set-focus	(GtkWindow *,
                         	 ggpointer,
                         	 gpointer);

GtkHandleBox

void GtkHandleBox::child-attached	(GtkHandleBox *,
                                 	 GtkWidget *,
                                 	 gpointer);
void GtkHandleBox::child-detached	(GtkHandleBox *,
                                 	 GtkWidget *,
                                 	 gpointer);

GtkToggleButton

void GtkToggleButton::toggled	(GtkToggleButton *,
                             	 gpointer);

GtkMenuItem

void GtkMenuItem::activate	(GtkMenuItem *,
                          	 gpointer);
void GtkMenuItem::activate-item	(GtkMenuItem *,
                               	 gpointer);

GtkCheckMenuItem

void GtkCheckMenuItem::toggled	(GtkCheckMenuItem *,
                              	 gpointer);

GtkInputDialog

void GtkInputDialog::enable-device	(GtkInputDialog *,
                                  	 ggint,
                                  	 gpointer);
void GtkInputDialog::disable-device	(GtkInputDialog *,
                                   	 ggint,
                                   	 gpointer);

GtkColorSelection

void GtkColorSelection::color-changed	(GtkColorSelection *,
                                     	 gpointer);

GtkStatusBar

void GtkStatusbar::text-pushed	(GtkStatusbar *,
                              	 gguint,
                              	 GtkString *,
                              	 gpointer);
void GtkStatusbar::text-popped	(GtkStatusbar *,
                              	 gguint,
                              	 GtkString *,
                              	 gpointer);

GtkCurve

void GtkCurve::curve-type-changed	(GtkCurve *,
                                 	 gpointer);

GtkAdjustment

void GtkAdjustment::changed	(GtkAdjustment *,
                           	 gpointer);
void GtkAdjustment::value-changed	(GtkAdjustment *,
                                 	 gpointer);

GtkDial

gtkdial.h


/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#ifndef __GTK_DIAL_H__
#define __GTK_DIAL_H__
#include <gdk/gdk.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtkwidget.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define GTK_DIAL(obj)          GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
#define GTK_DIAL_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
#define GTK_IS_DIAL(obj)       GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
typedef struct _GtkDial        GtkDial;
typedef struct _GtkDialClass   GtkDialClass;
struct _GtkDial
{
  GtkWidget widget;
  /* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
  guint policy : 2;
  /* Button currently pressed or 0 if none */
  guint8 button;
  /* Dimensions of dial components */
  gint radius;
  gint pointer_width;
  /* ID of update timer, or 0 if none */
  guint32 timer;
  /* Current angle */
  gfloat angle;
  gfloat last_angle;
  /* Old values from adjustment stored so we know when something changes */
  gfloat old_value;
  gfloat old_lower;
  gfloat old_upper;
  /* The adjustment object that stores the data for this dial */
  GtkAdjustment *adjustment;
};
struct _GtkDialClass
{
  GtkWidgetClass parent_class;
};
GtkWidget*     gtk_dial_new                    (GtkAdjustment *adjustment);
GtkType        gtk_dial_get_type               (void);
GtkAdjustment* gtk_dial_get_adjustment         (GtkDial      *dial);
void           gtk_dial_set_update_policy      (GtkDial      *dial,
						GtkUpdateType  policy);
void           gtk_dial_set_adjustment         (GtkDial      *dial,
						GtkAdjustment *adjustment);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __GTK_DIAL_H__ */

gtkdial.c


/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include <math.h>
#include <stdio.h>
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
#include "gtkdial.h"
#define SCROLL_DELAY_LENGTH  300
#define DIAL_DEFAULT_SIZE 100
/* Forward declarations */
static void gtk_dial_class_init               (GtkDialClass    *klass);
static void gtk_dial_init                     (GtkDial         *dial);
static void gtk_dial_destroy                  (GtkObject        *object);
static void gtk_dial_realize                  (GtkWidget        *widget);
static void gtk_dial_size_request             (GtkWidget      *widget,
					       GtkRequisition *requisition);
static void gtk_dial_size_allocate            (GtkWidget     *widget,
					       GtkAllocation *allocation);
static gint gtk_dial_expose                   (GtkWidget        *widget,
						GdkEventExpose   *event);
static gint gtk_dial_button_press             (GtkWidget        *widget,
						GdkEventButton   *event);
static gint gtk_dial_button_release           (GtkWidget        *widget,
						GdkEventButton   *event);
static gint gtk_dial_motion_notify            (GtkWidget        *widget,
						GdkEventMotion   *event);
static gint gtk_dial_timer                    (GtkDial         *dial);
static void gtk_dial_update_mouse             (GtkDial *dial, gint x, gint y);
static void gtk_dial_update                   (GtkDial *dial);
static void gtk_dial_adjustment_changed       (GtkAdjustment    *adjustment,
						gpointer          data);
static void gtk_dial_adjustment_value_changed (GtkAdjustment    *adjustment,
						gpointer          data);
/* Local data */
static GtkWidgetClass *parent_class = NULL;
GType
gtk_dial_get_type ()
{
  static GType dial_type = 0;
  if (!dial_type)
    {
      static const GTypeInfo dial_info =
      {
	sizeof (GtkDialClass),
	NULL,
	NULL,
	(GClassInitFunc) gtk_dial_class_init,
	NULL,
	NULL,
	sizeof (GtkDial),
        0,
	(GInstanceInitFunc) gtk_dial_init,
      };
      dial_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkDial", &dial_info, 0);
    }
  return dial_type;
}
static void
gtk_dial_class_init (GtkDialClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
  parent_class = gtk_type_class (gtk_widget_get_type ());
  object_class->destroy = gtk_dial_destroy;
  widget_class->realize = gtk_dial_realize;
  widget_class->expose_event = gtk_dial_expose;
  widget_class->size_request = gtk_dial_size_request;
  widget_class->size_allocate = gtk_dial_size_allocate;
  widget_class->button_press_event = gtk_dial_button_press;
  widget_class->button_release_event = gtk_dial_button_release;
  widget_class->motion_notify_event = gtk_dial_motion_notify;
}
static void
gtk_dial_init (GtkDial *dial)
{
  dial->button = 0;
  dial->policy = GTK_UPDATE_CONTINUOUS;
  dial->timer = 0;
  dial->radius = 0;
  dial->pointer_width = 0;
  dial->angle = 0.0;
  dial->old_value = 0.0;
  dial->old_lower = 0.0;
  dial->old_upper = 0.0;
  dial->adjustment = NULL;
}
GtkWidget*
gtk_dial_new (GtkAdjustment *adjustment)
{
  GtkDial *dial;
  dial = g_object_new (gtk_dial_get_type (), NULL);
  if (!adjustment)
    adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
  gtk_dial_set_adjustment (dial, adjustment);
  return GTK_WIDGET (dial);
}
static void
gtk_dial_destroy (GtkObject *object)
{
  GtkDial *dial;
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_DIAL (object));
  dial = GTK_DIAL (object);
  if (dial->adjustment)
    {
      g_object_unref (GTK_OBJECT (dial->adjustment));
      dial->adjustment = NULL;
    }
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
GtkAdjustment*
gtk_dial_get_adjustment (GtkDial *dial)
{
  g_return_val_if_fail (dial != NULL, NULL);
  g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
  return dial->adjustment;
}
void
gtk_dial_set_update_policy (GtkDial      *dial,
			     GtkUpdateType  policy)
{
  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));
  dial->policy = policy;
}
void
gtk_dial_set_adjustment (GtkDial      *dial,
			  GtkAdjustment *adjustment)
{
  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));
  if (dial->adjustment)
    {
      g_signal_handlers_disconnect_by_func (GTK_OBJECT (dial->adjustment), NULL, (gpointer) dial);
      g_object_unref (GTK_OBJECT (dial->adjustment));
    }
  dial->adjustment = adjustment;
  g_object_ref (GTK_OBJECT (dial->adjustment));
  g_signal_connect (GTK_OBJECT (adjustment), "changed",
		    GTK_SIGNAL_FUNC (gtk_dial_adjustment_changed),
		    (gpointer) dial);
  g_signal_connect (GTK_OBJECT (adjustment), "value_changed",
		    GTK_SIGNAL_FUNC (gtk_dial_adjustment_value_changed),
		    (gpointer) dial);
  dial->old_value = adjustment->value;
  dial->old_lower = adjustment->lower;
  dial->old_upper = adjustment->upper;
  gtk_dial_update (dial);
}
static void
gtk_dial_realize (GtkWidget *widget)
{
  GtkDial *dial;
  GdkWindowAttr attributes;
  gint attributes_mask;
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_DIAL (widget));
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  dial = GTK_DIAL (widget);
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.window_type = GDK_WINDOW_CHILD;
    GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
    GDK_POINTER_MOTION_HINT_MASK;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
  widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
  widget->style = gtk_style_attach (widget->style, widget->window);
  gdk_window_set_user_data (widget->window, widget);
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
}
gtk_dial_size_request (GtkWidget      *widget,
		       GtkRequisition *requisition)
{
  requisition->width = DIAL_DEFAULT_SIZE;
  requisition->height = DIAL_DEFAULT_SIZE;
}
static void
gtk_dial_size_allocate (GtkWidget     *widget,
			GtkAllocation *allocation)
{
  GtkDial *dial;
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_DIAL (widget));
  g_return_if_fail (allocation != NULL);
  widget->allocation = *allocation;
  dial = GTK_DIAL (widget);
  if (GTK_WIDGET_REALIZED (widget))
    {
      gdk_window_move_resize (widget->window,
			      allocation->x, allocation->y,
			      allocation->width, allocation->height);
    }
  dial->radius = MIN (allocation->width, allocation->height) * 0.45;
  dial->pointer_width = dial->radius / 5;
}
static gint
gtk_dial_expose (GtkWidget      *widget,
		 GdkEventExpose *event)
{
  GtkDial *dial;
  GdkPoint points[6];
  gdouble s,c;
  gdouble theta, last, increment;
  GtkStyle      *blankstyle;
  gint xc, yc;
  gint upper, lower;
  gint tick_length;
  gint i, inc;
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  if (event->count > 0)
    return FALSE;
  dial = GTK_DIAL (widget);
/*  gdk_window_clear_area (widget->window,
			 0, 0,
			 widget->allocation.width,
			 widget->allocation.height);
*/
  xc = widget->allocation.width / 2;
  yc = widget->allocation.height / 2;
  upper = dial->adjustment->upper;
  lower = dial->adjustment->lower;
  /* Erase old pointer */
  s = sin (dial->last_angle);
  c = cos (dial->last_angle);
  dial->last_angle = dial->angle;
  points[0].x = xc + s*dial->pointer_width/2;
  points[0].y = yc + c*dial->pointer_width/2;
  points[1].x = xc + c*dial->radius;
  points[1].y = yc - s*dial->radius;
  points[2].x = xc - s*dial->pointer_width/2;
  points[2].y = yc - c*dial->pointer_width/2;
  points[3].x = xc - c*dial->radius/10;
  points[3].y = yc + s*dial->radius/10;
  points[4].x = points[0].x;
  points[4].y = points[0].y;
  blankstyle = gtk_style_new ();
  blankstyle->bg_gc[GTK_STATE_NORMAL] =
                widget->style->bg_gc[GTK_STATE_NORMAL];
  blankstyle->dark_gc[GTK_STATE_NORMAL] =
                widget->style->bg_gc[GTK_STATE_NORMAL];
  blankstyle->light_gc[GTK_STATE_NORMAL] =
                widget->style->bg_gc[GTK_STATE_NORMAL];
  blankstyle->black_gc =
                widget->style->bg_gc[GTK_STATE_NORMAL];
  gtk_paint_polygon (blankstyle,
                    widget->window,
                    GTK_STATE_NORMAL,
                    GTK_SHADOW_OUT,
	            NULL,
                    widget,
                    NULL,
                    points, 5,
                    FALSE);
  g_object_unref (blankstyle);
  /* Draw ticks */
  if ((upper - lower) == 0)
    return FALSE;
  increment = (100*M_PI) / (dial->radius*dial->radius);
  inc = (upper - lower);
  while (inc < 100) inc *= 10;
  while (inc >= 1000) inc /= 10;
  last = -1;
  for (i = 0; i <= inc; i++)
    {
      theta = ((gfloat)i*M_PI / (18*inc/24.) - M_PI/6.);
      if ((theta - last) < (increment))
      last = theta;
      s = sin (theta);
      c = cos (theta);
      tick_length = (i%(inc/10) == 0) ? dial->pointer_width : dial->pointer_width / 2;
      gdk_draw_line (widget->window,
                     widget->style->fg_gc[widget->state],
                     xc + c*(dial->radius - tick_length),
                     yc - s*(dial->radius - tick_length),
                     xc + c*dial->radius,
                     yc - s*dial->radius);
    }
  /* Draw pointer */
  s = sin (dial->angle);
  c = cos (dial->angle);
  dial->last_angle = dial->angle;
  points[0].x = xc + s*dial->pointer_width/2;
  points[0].y = yc + c*dial->pointer_width/2;
  points[1].x = xc + c*dial->radius;
  points[1].y = yc - s*dial->radius;
  points[2].x = xc - s*dial->pointer_width/2;
  points[2].y = yc - c*dial->pointer_width/2;
  points[3].x = xc - c*dial->radius/10;
  points[3].y = yc + s*dial->radius/10;
  points[4].x = points[0].x;
  points[4].y = points[0].y;
  gtk_paint_polygon (widget->style,
		    widget->window,
		    GTK_STATE_NORMAL,
		    GTK_SHADOW_OUT,
	            NULL,
                    widget,
                    NULL,
		    points, 5,
		    TRUE);
  return FALSE;
}
static gint
gtk_dial_button_press (GtkWidget      *widget,
		       GdkEventButton *event)
{
  GtkDial *dial;
  gint dx, dy;
  double s, c;
  double d_parallel;
  double d_perpendicular;
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  dial = GTK_DIAL (widget);
     do this by computing the parallel and perpendicular distance of
     the point where the mouse was pressed from the line passing through
     the pointer */
  dx = event->x - widget->allocation.width / 2;
  dy = widget->allocation.height / 2 - event->y;
  s = sin (dial->angle);
  c = cos (dial->angle);
  d_parallel = s*dy + c*dx;
  d_perpendicular = fabs (s*dx - c*dy);
  if (!dial->button &&
      (d_perpendicular < dial->pointer_width/2) &&
      (d_parallel > - dial->pointer_width))
    {
      gtk_grab_add (widget);
      dial->button = event->button;
      gtk_dial_update_mouse (dial, event->x, event->y);
    }
  return FALSE;
}
static gint
gtk_dial_button_release (GtkWidget      *widget,
			  GdkEventButton *event)
{
  GtkDial *dial;
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  dial = GTK_DIAL (widget);
  if (dial->button == event->button)
    {
      gtk_grab_remove (widget);
      dial->button = 0;
      if (dial->policy == GTK_UPDATE_DELAYED)
	gtk_timeout_remove (dial->timer);
      if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
	  (dial->old_value != dial->adjustment->value))
	g_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
    }
  return FALSE;
}
static gint
gtk_dial_motion_notify (GtkWidget      *widget,
			 GdkEventMotion *event)
{
  GtkDial *dial;
  GdkModifierType mods;
  gint x, y, mask;
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);
  dial = GTK_DIAL (widget);
  if (dial->button != 0)
    {
      x = event->x;
      y = event->y;
      if (event->is_hint || (event->window != widget->window))
	gdk_window_get_pointer (widget->window, &x, &y, &mods);
      switch (dial->button)
	{
	case 1:
	  mask = GDK_BUTTON1_MASK;
	  break;
	case 2:
	  mask = GDK_BUTTON2_MASK;
	  break;
	case 3:
	  mask = GDK_BUTTON3_MASK;
	  break;
	default:
	  mask = 0;
	  break;
	}
      if (mods & mask)
	gtk_dial_update_mouse (dial, x,y);
    }
  return FALSE;
}
static gint
gtk_dial_timer (GtkDial *dial)
{
  g_return_val_if_fail (dial != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
  if (dial->policy == GTK_UPDATE_DELAYED)
    g_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
  return FALSE;
}
static void
gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
{
  gint xc, yc;
  gfloat old_value;
  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));
  xc = GTK_WIDGET(dial)->allocation.width / 2;
  yc = GTK_WIDGET(dial)->allocation.height / 2;
  old_value = dial->adjustment->value;
  dial->angle = atan2(yc-y, x-xc);
  if (dial->angle < -M_PI/2.)
    dial->angle += 2*M_PI;
  if (dial->angle < -M_PI/6)
    dial->angle = -M_PI/6;
  if (dial->angle > 7.*M_PI/6.)
    dial->angle = 7.*M_PI/6.;
  dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
    (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
  if (dial->adjustment->value != old_value)
    {
      if (dial->policy == GTK_UPDATE_CONTINUOUS)
	{
	  g_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
	}
      else
	{
	  gtk_widget_queue_draw (GTK_WIDGET (dial));
	  if (dial->policy == GTK_UPDATE_DELAYED)
	    {
	      if (dial->timer)
		gtk_timeout_remove (dial->timer);
	      dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
					     (GtkFunction) gtk_dial_timer,
					     (gpointer) dial);
	    }
	}
    }
}
static void
gtk_dial_update (GtkDial *dial)
{
  gfloat new_value;
  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));
  new_value = dial->adjustment->value;
  if (new_value < dial->adjustment->lower)
    new_value = dial->adjustment->lower;
  if (new_value > dial->adjustment->upper)
    new_value = dial->adjustment->upper;
  if (new_value != dial->adjustment->value)
    {
      dial->adjustment->value = new_value;
      g_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
    }
  dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
    (dial->adjustment->upper - dial->adjustment->lower);
  gtk_widget_queue_draw (GTK_WIDGET (dial));
}
static void
gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
			      gpointer       data)
{
  GtkDial *dial;
  g_return_if_fail (adjustment != NULL);
  g_return_if_fail (data != NULL);
  dial = GTK_DIAL (data);
  if ((dial->old_value != adjustment->value) ||
      (dial->old_lower != adjustment->lower) ||
      (dial->old_upper != adjustment->upper))
    {
      gtk_dial_update (dial);
      dial->old_value = adjustment->value;
      dial->old_lower = adjustment->lower;
      dial->old_upper = adjustment->upper;
    }
}
static void
gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
				    gpointer       data)
{
  GtkDial *dial;
  g_return_if_fail (adjustment != NULL);
  g_return_if_fail (data != NULL);
  dial = GTK_DIAL (data);
  if (dial->old_value != adjustment->value)
    {
      gtk_dial_update (dial);
      dial->old_value = adjustment->value;
    }
}

dial_test.c


#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include "gtkdial.h"
void value_changed( GtkAdjustment *adjustment,
                    GtkWidget     *label )
{
  char buffer[16];
  sprintf(buffer,"%4.2f",adjustment->value);
  gtk_label_set_text (GTK_LABEL (label), buffer);
}
int main( int   argc,
          char *argv[])
{
  GtkWidget *window;
  GtkAdjustment *adjustment;
  GtkWidget *dial;
  GtkWidget *frame;
  GtkWidget *vbox;
  GtkWidget *label;
  gtk_init (&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Dial");
  g_signal_connect (G_OBJECT (window), "destroy",
		    G_CALLBACK (exit), NULL);
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  vbox = gtk_vbox_new (FALSE, 5);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_widget_show (vbox);
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_container_add (GTK_CONTAINER (vbox), frame);
  adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 100, 0.01, 0.1, 0));
  dial = gtk_dial_new (adjustment);
  gtk_dial_set_update_policy (GTK_DIAL (dial), GTK_UPDATE_DELAYED);
  /*  gtk_widget_set_size_request (dial, 100, 100); */
  gtk_container_add (GTK_CONTAINER (frame), dial);
  gtk_widget_show (dial);
  label = gtk_label_new ("0.00");
  gtk_box_pack_end (GTK_BOX (vbox), label, 0, 0, 0);
  gtk_widget_show (label);
  g_signal_connect (G_OBJECT (adjustment), "value_changed",
		    G_CALLBACK (value_changed), (gpointer) label);
  gtk_widget_show (window);
  gtk_main ();
  return 0;
}

Scribble

scribble-simple.c


/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include <stdlib.h>
#include <gtk/gtk.h>
/* Backing pixmap for drawing area */
static GdkPixmap *pixmap = NULL;
/* Create a new backing pixmap of the appropriate size */
static gint configure_event( GtkWidget         *widget,
                             GdkEventConfigure *event )
{
  if (pixmap)
    g_object_unref (pixmap);
  pixmap = gdk_pixmap_new (widget->window,
			   widget->allocation.width,
			   widget->allocation.height,
			   -1);
  gdk_draw_rectangle (pixmap,
		      widget->style->white_gc,
		      TRUE,
		      0, 0,
		      widget->allocation.width,
		      widget->allocation.height);
  return TRUE;
}
/* Redraw the screen from the backing pixmap */
static gint expose_event( GtkWidget      *widget,
                          GdkEventExpose *event )
{
  gdk_draw_drawable (widget->window,
		     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
		     pixmap,
		     event->area.x, event->area.y,
		     event->area.x, event->area.y,
		     event->area.width, event->area.height);
  return FALSE;
}
/* Draw a rectangle on the screen */
static void draw_brush( GtkWidget *widget,
                        gdouble    x,
                        gdouble    y)
{
  GdkRectangle update_rect;
  update_rect.x = x - 5;
  update_rect.y = y - 5;
  update_rect.width = 10;
  update_rect.height = 10;
  gdk_draw_rectangle (pixmap,
		      widget->style->black_gc,
		      TRUE,
		      update_rect.x, update_rect.y,
		      update_rect.width, update_rect.height);
		      update_rect.x, update_rect.y,
		      update_rect.width, update_rect.height);
}
static gint button_press_event( GtkWidget      *widget,
                                GdkEventButton *event )
{
  if (event->button == 1 && pixmap != NULL)
    draw_brush (widget, event->x, event->y);
  return TRUE;
}
static gint motion_notify_event( GtkWidget *widget,
                                 GdkEventMotion *event )
{
  int x, y;
  GdkModifierType state;
  if (event->is_hint)
    gdk_window_get_pointer (event->window, &x, &y, &state);
  else
    {
      x = event->x;
      y = event->y;
      state = event->state;
    }
  if (state & GDK_BUTTON1_MASK && pixmap != NULL)
    draw_brush (widget, x, y);
  return TRUE;
}
void quit ()
{
  exit (0);
}
          char *argv[] )
{
  GtkWidget *window;
  GtkWidget *drawing_area;
  GtkWidget *vbox;
  GtkWidget *button;
  gtk_init (&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_set_name (window, "Test Input");
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_widget_show (vbox);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (quit), NULL);
  /* Create the drawing area */
  drawing_area = gtk_drawing_area_new ();
  gtk_widget_set_size_request (GTK_WIDGET (drawing_area), 200, 200);
  gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
  gtk_widget_show (drawing_area);
  /* Signals used to handle backing pixmap */
  g_signal_connect (G_OBJECT (drawing_area), "expose_event",
		    G_CALLBACK (expose_event), NULL);
  g_signal_connect (G_OBJECT (drawing_area),"configure_event",
		    G_CALLBACK (configure_event), NULL);
  /* Event signals */
  g_signal_connect (G_OBJECT (drawing_area), "motion_notify_event",
		    G_CALLBACK (motion_notify_event), NULL);
  g_signal_connect (G_OBJECT (drawing_area), "button_press_event",
		    G_CALLBACK (button_press_event), NULL);
  gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
			 | GDK_LEAVE_NOTIFY_MASK
			 | GDK_BUTTON_PRESS_MASK
			 | GDK_POINTER_MOTION_MASK
			 | GDK_POINTER_MOTION_HINT_MASK);
  /* .. And a quit button */
  button = gtk_button_new_with_label ("Quit");
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  g_signal_connect_swapped (G_OBJECT (button), "clicked",
			    G_CALLBACK (gtk_widget_destroy),
			    G_OBJECT (window));
  gtk_widget_show (button);
  gtk_widget_show (window);
  gtk_main ();
  return 0;
}

scribble-xinput.c


/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include <gtk/gtk.h>
/* Backing pixmap for drawing area */
static GdkPixmap *pixmap = NULL;
/* Create a new backing pixmap of the appropriate size */
static gint
configure_event (GtkWidget *widget, GdkEventConfigure *event)
{
  if (pixmap)
     g_object_unref (pixmap);
  pixmap = gdk_pixmap_new (widget->window,
                           widget->allocation.width,
                           widget->allocation.height,
                           -1);
  gdk_draw_rectangle (pixmap,
                      widget->style->white_gc,
                      TRUE,
                      0, 0,
                      widget->allocation.width,
                      widget->allocation.height);
  return TRUE;
}
/* Redraw the screen from the backing pixmap */
static gint
expose_event (GtkWidget *widget, GdkEventExpose *event)
{
  gdk_draw_drawable (widget->window,
                     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
                     pixmap,
                     event->area.x, event->area.y,
                     event->area.x, event->area.y,
                     event->area.width, event->area.height);
  return FALSE;
}
/* Draw a rectangle on the screen, size depending on pressure,
   and color on the type of device */
static void
draw_brush (GtkWidget *widget, GdkInputSource source,
            gdouble x, gdouble y, gdouble pressure)
{
  GdkGC *gc;
  GdkRectangle update_rect;
  switch (source)
    {
    case GDK_SOURCE_MOUSE:
      gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
      break;
    case GDK_SOURCE_PEN:
      gc = widget->style->black_gc;
      break;
    case GDK_SOURCE_ERASER:
      gc = widget->style->white_gc;
      break;
    default:
      gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
    }
  update_rect.x = x - 10 * pressure;
  update_rect.y = y - 10 * pressure;
  update_rect.width = 20 * pressure;
  update_rect.height = 20 * pressure;
  gdk_draw_rectangle (pixmap, gc, TRUE,
                      update_rect.x, update_rect.y,
                      update_rect.width, update_rect.height);
                      update_rect.x, update_rect.y,
                      update_rect.width, update_rect.height);
}
static void
print_button_press (GdkDevice *device)
{
  g_print ("Button press on device '%s'\n", device->name);
}
static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
{
  print_button_press (event->device);
  if (event->button == 1 && pixmap != NULL) {
    gdouble pressure;
    gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_PRESSURE, &pressure);
    draw_brush (widget, event->device->source, event->x, event->y, pressure);
  }
  return TRUE;
}
static gint
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
  gdouble x, y;
  gdouble pressure;
  GdkModifierType state;
    {
      gdk_device_get_state (event->device, event->window, NULL, &state);
      gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_X, &x);
      gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_Y, &y);
      gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_PRESSURE, &pressure);
    }
  else
    {
      x = event->x;
      y = event->y;
      gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_PRESSURE, &pressure);
      state = event->state;
    }
  if (state & GDK_BUTTON1_MASK && pixmap != NULL)
    draw_brush (widget, event->device->source, x, y, pressure);
  return TRUE;
}
void
input_dialog_destroy (GtkWidget *w, gpointer data)
{
  *((GtkWidget **)data) = NULL;
}
void
create_input_dialog ()
{
  static GtkWidget *inputd = NULL;
  if (!inputd)
    {
      inputd = gtk_input_dialog_new();
      g_signal_connect (G_OBJECT (inputd), "destroy",
                        G_CALLBACK (input_dialog_destroy), (gpointer) &inputd);
      g_signal_connect_swapped (G_OBJECT (GTK_INPUT_DIALOG (inputd)->close_button),
                                "clicked",
                                G_CALLBACK (gtk_widget_hide),
                                G_OBJECT (inputd));
      gtk_widget_hide (GTK_INPUT_DIALOG (inputd)->save_button);
      gtk_widget_show (inputd);
    }
  else
    {
      if (!GTK_WIDGET_MAPPED (inputd))
        gtk_widget_show (inputd);
      else
        gdk_window_raise (inputd->window);
    }
}
void
quit ()
{
  exit (0);
}
int
main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *drawing_area;
  GtkWidget *vbox;
  GtkWidget *button;
  gtk_init (&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_set_name (window, "Test Input");
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_widget_show (vbox);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (quit), NULL);
  /* Create the drawing area */
  drawing_area = gtk_drawing_area_new ();
  gtk_widget_set_size_request (GTK_WIDGET (drawing_area), 200, 200);
  gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
  gtk_widget_show (drawing_area);
  /* Signals used to handle backing pixmap */
  g_signal_connect (G_OBJECT (drawing_area), "expose_event",
                    G_CALLBACK (expose_event), NULL);
  g_signal_connect (G_OBJECT(drawing_area),"configure_event",
                    G_CALLBACK (configure_event), NULL);
  /* Event signals */
  g_signal_connect (G_OBJECT (drawing_area), "motion_notify_event",
                    G_CALLBACK (motion_notify_event), NULL);
  g_signal_connect (G_OBJECT (drawing_area), "button_press_event",
                    G_CALLBACK (button_press_event), NULL);
  gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
                         | GDK_LEAVE_NOTIFY_MASK
                         | GDK_BUTTON_PRESS_MASK
                         | GDK_POINTER_MOTION_MASK
                         | GDK_POINTER_MOTION_HINT_MASK);
  /* The following call enables tracking and processing of extension
     events for the drawing area */
  gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
  /* .. And some buttons */
  button = gtk_button_new_with_label ("Input Dialog");
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (button), "clicked",
                    G_CALLBACK (create_input_dialog), NULL);
  gtk_widget_show (button);
  button = gtk_button_new_with_label ("Quit");
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  g_signal_connect_swapped (G_OBJECT (button), "clicked",
                            G_CALLBACK (gtk_widget_destroy),
                            G_OBJECT (window));
  gtk_widget_show (button);
  gtk_widget_show (window);
  gtk_main ();
  return 0;
}