Редактирование: GTK FAQ Events

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск
Внимание: Вы не представились системе. Ваш IP-адрес будет записан в историю изменений этой страницы.
Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия Ваш текст
Строка 1: Строка 1:
 +
In this part of the GTK+ programming tutorial, we will talk about the event system in GTK+ library.
 +
GTK+ library is an event driven system. All GUI applications are event driven. The applications start a main loop, which continuously checks for newly generated events. If there is no event, the application waits and does nothing.
 +
In GTK+ an <b>event</b> is a message from the X server. When the event reaches a wiget, it may react to this event by emitting a <b>signal</b>. The GTK+ programmer can connect a specific
 +
 +
<b>callback</b> to a signal. The callback is a handler function, that reacts to a signal.
 +
 +
<source lang="cpp">
 +
#include &lt;gtk/gtk.h&gt;
 +
 +
void button_clicked(GtkWidget *widget, gpointer data)
 +
{
 +
  g_print("clicked\n");
 +
}
 +
 +
int main( int argc, char *argv[])
 +
{
 +
  GtkWidget *window;
 +
  GtkWidget *fixed;
 +
  GtkWidget *button;
 +
 +
  gtk_init(&argc, &argv);
 +
 +
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 +
  gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
 +
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
 +
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
 +
 +
  fixed = gtk_fixed_new();
 +
  gtk_container_add(GTK_CONTAINER(window), fixed);
 +
 +
  button = gtk_button_new_with_label("Click");
 +
  gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50);
 +
  gtk_widget_set_size_request(button, 80, 35);
 +
 +
  g_signal_connect(G_OBJECT(button), "clicked",
 +
      G_CALLBACK(button_clicked), NULL);
 +
 +
  g_signal_connect(G_OBJECT(window), "destroy",
 +
      G_CALLBACK(gtk_main_quit), NULL);
 +
 +
  gtk_widget_show_all(window);
 +
 +
  gtk_main();
 +
 +
  return 0;
 +
}
 +
</source>
 +
 +
In our application, we have two signals. The <b>destroy</b> signal.
 +
 +
<source lang="cpp">
 +
g_signal_connect(G_OBJECT(button), "clicked",
 +
    G_CALLBACK(button_clicked), NULL);
 +
</source>
 +
 +
We use the <b>g_signal_connect()</b> function to connect the clicked signal to the  <b>button_clicked()</b> callback.
 +
 +
<source lang="cpp">
 +
void button_clicked(GtkWidget *widget, gpointer data)
 +
{
 +
  g_print("clicked\n");
 +
}
 +
</source>
 +
 +
The callback will pring "clicked" text to the console. The first parameter of the callback function is the object, which emitted the signal. In our case it is the Click button. The second parameter is optional. We may send some data to the callback. In our case, we did not send any data. We provided a NULL parameter in the g_signal_connect() function.
 +
 +
<source lang="cpp">
 +
g_signal_connect(G_OBJECT(window), "destroy",
 +
      G_CALLBACK(gtk_main_quit), NULL);
 +
</source>
 +
 +
If we press on the x button located in the upper right corner of the titlebar, or we press Atl + F4, a  <b>gtk_main_quit()</b> function, which will terminate the application.
 +
 +
== Moving window ==
 +
 +
The next example shows, how we react to move events of a window.
 +
 +
<source lang="cpp">
 +
#include &lt;gtk/gtk.h&gt;
 +
 +
void frame_callback(GtkWindow *window,
 +
      GdkEvent *event, gpointer data)
 +
{
 +
  int x, y;
 +
  char buf[10];
 +
  x = event->configure.x;
 +
  y = event->configure.y;
 +
  sprintf(buf, "%d, %d", x, y);
 +
  gtk_window_set_title(window, buf);
 +
}
 +
 +
 +
int main(int argc, char *argv[])
 +
{
 +
  GtkWidget *window;
 +
 +
  gtk_init(&argc, &argv);
 +
 +
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 +
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
 +
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
 +
  gtk_window_set_title(GTK_WINDOW(window), "Simple");
 +
  gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);
 +
 +
  g_signal_connect_swapped(G_OBJECT(window), "destroy",
 +
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));
 +
 +
  g_signal_connect(G_OBJECT(window), "configure-event",
 +
        G_CALLBACK(frame_callback), NULL);
 +
 +
  gtk_widget_show(window);
 +
  gtk_main();
 +
 +
  return 0;
 +
}
 +
</source>
 +
 +
In the example, we show the current position of the upper left corner of our window in the titlebar.
 +
 +
<source lang="cpp">
 +
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);
 +
 +
</source>
 +
 +
The event mask of the widget determines, what kind of event will a particular widget receive. Some event are preconfigured, other events have to be added to the event mask. The <b>gtk_widget_add_events()</b> adds a <b>GDK_CONFIGURE</b> event type accounts for all size, position and stack order events.
 +
 +
<source lang="cpp">
 +
g_signal_connect(G_OBJECT(window), "configure-event",
 +
    G_CALLBACK(frame_callback), NULL);
 +
 +
</source>
 +
 +
The <b>configure-event</b> is emitted to size, position and stack order events.
 +
 +
<source lang="cpp">
 +
void frame_callback(GtkWindow *window,
 +
    GdkEvent *event, gpointer data)
 +
{
 +
  int x, y;
 +
  char buf[10];
 +
  x = event->configure.x;
 +
  y = event->configure.y;
 +
  sprintf(buf, "%d, %d", x, y);
 +
  gtk_window_set_title(window, buf);
 +
}
 +
</source>
 +
 +
The callback function has three parameters. The object that emitted the signal, <b>GdkEvent</b> and the optional data. We determine the x, y positions and set it to the title.
 +
 +
[[image: gtk_faq_moveevent.png | center]]
 +
 +
== The enter signal ==
 +
 +
The following examle will show, how we can react to an <b>enter</b> signal. The enter signal is emitted, when we enter the area of a widget with a mouse pointer.
 +
 +
<source lang="cpp">
 +
#include &lt;gtk/gtk.h&gt;
 +
 +
 +
void enter_button(GtkWidget *widget, gpointer data)
 +
{
 +
  GdkColor color;
 +
  color.red = 27000;
 +
  color.green = 30325;
 +
  color.blue = 34181;
 +
  gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, &color);
 +
}
 +
 +
 +
int main( int argc, char *argv[])
 +
{
 +
 +
  GtkWidget *window;
 +
  GtkWidget *fixed;
 +
  GtkWidget *button;
 +
 +
  gtk_init(&argc, &argv);
 +
 +
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 +
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
 +
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
 +
  gtk_window_set_title(GTK_WINDOW(window), "enter signal");
 +
 +
  fixed = gtk_fixed_new();
 +
  gtk_container_add(GTK_CONTAINER(window), fixed);
 +
 +
  button = gtk_button_new_with_label("Button");
 +
  gtk_widget_set_size_request(button, 80, 35);
 +
  gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50);
 +
 +
  g_signal_connect(G_OBJECT(button), "enter",
 +
      G_CALLBACK(enter_button), NULL);
 +
 +
  g_signal_connect_swapped(G_OBJECT(window), "destroy",
 +
      G_CALLBACK(gtk_main_quit), NULL);
 +
 +
  gtk_widget_show_all(window);
 +
 +
  gtk_main();
 +
 +
  return 0;
 +
}
 +
</source>
 +
 +
We will change the background color of the button widget, once we hover a mouse pointer over it.
 +
 +
<source lang="cpp">
 +
g_signal_connect(G_OBJECT(button), "enter",
 +
    G_CALLBACK(enter_button), NULL);
 +
</source>
 +
 +
We call the <b>enter</b> signal occurs.
 +
 +
<source lang="cpp">
 +
 +
GdkColor color;
 +
color.red = 27000;
 +
color.green = 30325;
 +
color.blue = 34181;
 +
gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, &color);
 +
</source>
 +
 +
Inside the callback, we change the background of the button by calling the <b>gtk_widget_modify_bg()</b> function.
 +
 +
== Disconnecting a callback ==
 +
 +
We can disconnect a callback from the signal. Next code example demonstrates such a case.
 +
 +
<source lang="cpp">
 +
#include &lt;gtk/gtk.h&gt;
 +
 +
 +
int handler_id;
 +
 +
void button_clicked(GtkWidget *widget, gpointer data)
 +
{
 +
  g_print("clicked\n");
 +
}
 +
 +
void toogle_signal(GtkWidget *widget, gpointer window)
 +
{
 +
  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
 +
    handler_id = g_signal_connect(G_OBJECT(window), "clicked",
 +
          G_CALLBACK(button_clicked), NULL);
 +
  } else {
 +
    g_signal_handler_disconnect(window, handler_id);
 +
  }
 +
}
 +
 +
 +
int main( int argc, char *argv[])
 +
{
 +
 +
  GtkWidget *window;
 +
  GtkWidget *fixed;
 +
  GtkWidget *button;
 +
  GtkWidget *check;
 +
 +
 +
  gtk_init(&argc, &argv);
 +
 +
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 +
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
 +
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 150);
 +
  gtk_window_set_title(GTK_WINDOW(window), "Disconnect");
 +
 +
  fixed = gtk_fixed_new();
 +
  gtk_container_add(GTK_CONTAINER(window), fixed);
 +
 +
  button = gtk_button_new_with_label("Click");
 +
  gtk_widget_set_size_request(button, 80, 30);
 +
  gtk_fixed_put(GTK_FIXED(fixed), button, 30, 50);
 +
 +
  check = gtk_check_button_new_with_label("Connect");
 +
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
 +
  gtk_fixed_put(GTK_FIXED(fixed), check, 130, 50);
 +
 +
  handler_id = g_signal_connect(G_OBJECT(button), "clicked",
 +
        G_CALLBACK(button_clicked), NULL);
 +
 +
  g_signal_connect(G_OBJECT(check), "clicked",
 +
        G_CALLBACK(toogle_signal), (gpointer) button);
 +
 +
  g_signal_connect_swapped(G_OBJECT(window), "destroy",
 +
        G_CALLBACK(gtk_main_quit), NULL);
 +
 +
  gtk_widget_show_all(window);
 +
 +
  gtk_main();
 +
 +
  return 0;
 +
}
 +
</source>
 +
 +
In the code example, we have a button and a check box. The check box connects or disconnects a callback from the clicked signal of the button.
 +
 +
<source lang="cpp">
 +
handler_id = g_signal_connect(G_OBJECT(button), "clicked",
 +
    G_CALLBACK(button_clicked), NULL);
 +
</source>
 +
 +
The <b>g_signal_connect()</b> returns the handler id, which uniquely identifies the callback.
 +
 +
<source lang="cpp">
 +
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
 +
    handler_id = g_signal_connect(G_OBJECT(window), "clicked",
 +
          G_CALLBACK(button_clicked), NULL);
 +
} else {
 +
    g_signal_handler_disconnect(window, handler_id);
 +
}
 +
</source>
 +
 +
This code determines the state of the check box. It connects the callback if it is checked, or disconnects otherwise.
 +
 +
[[image: gtk_faq_disconnect.png | center]]
 +
 +
== Drag and Drop example ==
 +
 +
In the next example, we show an interesting feature. We will show a borderless window and learn, how we can drag and move such a window.
 +
 +
<source lang="cpp">
 +
#include &lt;gtk/gtk.h&gt;
 +
 +
gboolean on_button_press (GtkWidget* widget,
 +
  GdkEventButton * event, GdkWindowEdge edge)
 +
{
 +
  if (event->type == GDK_BUTTON_PRESS)
 +
  {
 +
    if (event->button == 1) {
 +
      gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
 +
          event->button,
 +
  event->x_root,
 +
  event->y_root,
 +
  event->time);
 +
    }
 +
  }
 +
 +
  return FALSE;
 +
}
 +
 +
 +
int main( int argc, char *argv[])
 +
{
 +
 +
  GtkWidget *window;
 +
 +
  gtk_init(&argc, &argv);
 +
 +
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 +
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
 +
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
 +
  gtk_window_set_title(GTK_WINDOW(window), "Drag & drop");
 +
  gtk_window_set_decorated(GTK_WINDOW (window), FALSE);
 +
  gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
 +
 +
  g_signal_connect(G_OBJECT(window), "button-press-event",
 +
      G_CALLBACK(on_button_press), NULL);
 +
 +
  g_signal_connect_swapped(G_OBJECT(window), "destroy",
 +
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));
 +
 +
  gtk_widget_show(window);
 +
 +
  gtk_main();
 +
 +
  return 0;
 +
}
 +
</source>
 +
 +
The example demonstrates a drag and drop of a borderless window.
 +
 +
<source lang="cpp">
 +
gtk_window_set_decorated(GTK_WINDOW (window), FALSE);
 +
 +
</source>
 +
 +
We remove the decoration of the window. This means, that the window will not have borders and titlebar.
 +
 +
<source lang="cpp">
 +
  g_signal_connect(G_OBJECT(window), "button-press-event",
 +
      G_CALLBACK(on_button_press), NULL);
 +
</source>
 +
 +
We connect the window to the <b>button-press-event</b> signal.
 +
 +
<source lang="cpp">
 +
gboolean on_button_press (GtkWidget* widget,
 +
  GdkEventButton * event, GdkWindowEdge edge)
 +
{
 +
  if (event->type == GDK_BUTTON_PRESS)
 +
  {
 +
    if (event->button == 1) {
 +
      gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
 +
          event->button,
 +
  event->x_root,
 +
  event->y_root,
 +
  event->time);
 +
    }
 +
  }
 +
 +
  return FALSE;
 +
}
 +
</source>
 +
 +
Inside the <b>on_button_press()</b>, we do perform the drag and drop operation.
 +
We check if the left mouse button was pressed. Then we call the <b>gtk_window_begin_move_drag()</b> function.
 +
 +
== A timer example ==
 +
 +
The following example demostrates a timer example. Timers are used when we have some repeating tasks.
 +
It could be a clock, a count down, visual effects or animations.
 +
 +
<source lang="cpp">
 +
#include &lt;cairo.h&gt;
 +
#include &lt;gtk/gtk.h&gt;
 +
#include &lt;time.h&gt;
 +
 +
 +
static char buffer[256];
 +
 +
 +
static gboolean
 +
on_expose_event(GtkWidget *widget,
 +
    GdkEventExpose *event,
 +
    gpointer data)
 +
{
 +
  cairo_t *cr;
 +
 +
  cr = gdk_cairo_create(widget->window);
 +
 +
  cairo_move_to(cr, 30, 30);
 +
  cairo_show_text(cr, buffer);
 +
 +
  cairo_destroy(cr);
 +
 +
  return FALSE;
 +
}
 +
 +
static gboolean
 +
time_handler(GtkWidget *widget)
 +
{
 +
  if (widget->window == NULL) return FALSE;
 +
 +
  time_t curtime;
 +
  struct tm *loctime;
 +
 +
  curtime = time(NULL);
 +
  loctime = localtime(&curtime);
 +
  strftime(buffer, 256, "%T", loctime);
 +
 +
  gtk_widget_queue_draw(widget);
 +
  return TRUE;
 +
}
 +
 +
int
 +
main (int argc, char *argv[])
 +
{
 +
 +
  GtkWidget *window;
 +
  GtkWidget *darea;
 +
 +
  gtk_init(&argc, &argv);
 +
 +
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 +
 +
  darea = gtk_drawing_area_new();
 +
  gtk_container_add(GTK_CONTAINER (window), darea);
 +
 +
  g_signal_connect(darea, "expose-event",
 +
      G_CALLBACK(on_expose_event), NULL);
 +
  g_signal_connect(window, "destroy",
 +
      G_CALLBACK(gtk_main_quit), NULL);
 +
 +
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
 +
  gtk_window_set_default_size(GTK_WINDOW(window), 170, 100);
 +
 +
  gtk_window_set_title(GTK_WINDOW(window), "timer");
 +
  g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
 +
  gtk_widget_show_all(window);
 +
  time_handler(window);
 +
 +
  gtk_main();
 +
 +
  return 0;
 +
}
 +
</source>
 +
 +
We will display a current local time on the window. We use the Cairo 2D library.
 +
 +
<source lang="cpp">
 +
g_signal_connect(darea, "expose-event",
 +
    G_CALLBACK(on_expose_event), NULL);
 +
</source>
 +
 +
We will draw the time inside the <b>on_expose_event()</b> callback. The callback is connected to the <b>expose-event</b> signal. The signal is emitted, when the window is going to be redrawn.
 +
 +
<source lang="cpp">
 +
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
 +
</source>
 +
 +
This function registers the timer. The <b>time_handler()</b> function is called repeteadly at regular intervals. In our case in every second. The timer function is called until it returns FALSE.
 +
 +
<source lang="cpp">
 +
time_handler(window);
 +
 +
</source>
 +
 +
This calles the timer function immediately. Otherwise, there would be one sec delay.
 +
 +
<source lang="cpp">
 +
cairo_t *cr;
 +
 +
cr = gdk_cairo_create(widget->window);
 +
 +
cairo_move_to(cr, 30, 30);
 +
cairo_show_text(cr, buffer);
 +
 +
cairo_destroy(cr);
 +
</source>
 +
 +
This code draws the current time on the window. For more information about the Cairo 2D library, see [[Cairo_FAQ | Cairo graphics]].
 +
 +
<source lang="cpp">
 +
if (widget->window == NULL) return FALSE;
 +
</source>
 +
 +
When the window is destroyed, it may happen, that the timer function is called. This line will prevent working on already destroyed widget.
 +
 +
<source lang="cpp">
 +
time_t curtime;
 +
struct tm *loctime;
 +
 +
curtime = time(NULL);
 +
loctime = localtime(&curtime);
 +
strftime(buffer, 256, "%T", loctime);
 +
</source>
 +
 +
These lines determine the current local time.
 +
 +
<source lang="cpp">
 +
gtk_widget_queue_draw(widget);
 +
</source>
 +
 +
This will invalidate the window area, which will emit the <b>expose-event</b> signal.
 +
 +
[[Категория:GTK+]]

Пожалуйста, обратите внимание, что все ваши добавления могут быть отредактированы или удалены другими участниками. Если вы не хотите, чтобы кто-либо изменял ваши тексты, не помещайте их сюда.
Вы также подтверждаете, что являетесь автором вносимых дополнений, или скопировали их из источника, допускающего свободное распространение и изменение своего содержимого (см. Wiki.crossplatform.ru:Авторское право). НЕ РАЗМЕЩАЙТЕ БЕЗ РАЗРЕШЕНИЯ ОХРАНЯЕМЫЕ АВТОРСКИМ ПРАВОМ МАТЕРИАЛЫ!