前几天学习GDB,下载了张老师写的一个GTK图形程序,在我ubuntu机器上运行不了,界面直接死掉了。所以今天准备简单学习一下gtk
背景:
linux最常用的两种X客户端是KDE和GNOME.
KDE指的是K桌面环境(K Desktop Environment),是一种运行于UNIX以及Linux, FreeBSD等类UNIX操作系统上面的*图形工作环境。
KDE是一个综合的桌面环境,建立在XFree86和QT的基础上.
GNOME桌面系统使用C语言编程.
Linux系统下常用的图形界面开发环境有Qt和GTK两种.GTK(GIMP Toolkit)是一套跨多种平台的图形工具包,完全按照LGPL许可协议发布。
yum install gtk+
现在已经是gtk4了,安装方法:
sudo apt-get install libgtk-4-1 libgtk-4-dev gtk-4-examples
这样就可以使用gtk4了。
搜索了一下,看官方文档靠谱:
Gtk – 4.0: Getting Started with GTK
目录
第一个例子:生成一个Windows窗口。
带按钮的例子
排列
自定义画布
用ui文件描述界面布局的方法
第一个例子:生成一个Windows窗口。
#include <gtk/gtk.h>
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
gtk_window_present (GTK_WINDOW (window));
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
编译:
$( pkg-config --cflags gtk4 ) -o example-0 example-0.c $( pkg-config --libs gtk4 )
编译不过,参考:The GTK Project - A free and open-source cross-platform widget toolkit
sudo apt-get install libgtk-4-1 libgtk-4-dev gtk-4-examples
再编译就通过了,也可以运行。
下面解析一下这个小程序:
#include <gtk/gtk.h>
All GTK applications will, of course, include gtk/gtk.h,
which declares functions, types and macros required by GTK applications.
GtkApplication *app;
In a GTK application, the purpose of the main() function is to create a GtkApplication object and run it.
In this example a GtkApplication pointer named app is declared and then initialized using gtk_application_new().
app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
When creating a GtkApplication,
you need to pick an application identifier (a name) and pass it to gtk_application_new() as parameter.
For this example org.gtk.example is used. For choosing an identifier for your application, see this guide.
Lastly, gtk_application_new() takes GApplicationFlags as input for your application,
if your application would have special needs.
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
Next the activate signal is connected to the activate() function above the main() function.
The activate signal will be emitted when your application is launched with g_application_run() on the line below.
The g_application_run() call also takes as arguments the command line arguments (the argc count and the argv string array).
Your application can override the command line handling, e.g. to open files passed on the commandline.
activate (GtkApplication* app, gpointer user_data)
GtkWidget *window;
window = gtk_application_window_new (app);
Within g_application_run() the activate signal is sent and we then proceed into the activate() function of the application.
This is where we construct our GTK window, so that a window is shown when the application is launched.
The call to gtk_application_window_new() will create a new GtkApplicationWindow and store it inside the window pointer.
The window will have a frame, a title bar, and window controls depending on the platform.
gtk_window_set_title (GTK_WINDOW (window), "Window");
A window title is set using gtk_window_set_title().
This function takes a GtkWindow pointer and a string as input.
As our window pointer is a GtkWidget pointer, we need to cast it to GtkWindow;
instead of casting window via a typical C cast like (GtkWindow*),
window can be cast using the macro GTK_WINDOW().
GTK_WINDOW() will check if the pointer is an instance of the GtkWindow class, before casting, and emit a warning if the check fails.
More information about this convention can be found in the GObject documentation.
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
gtk_window_present (GTK_WINDOW (window));
Finally the window size is set using gtk_window_set_default_size() and the window is then shown by GTK via gtk_widget_show().
g_object_unref (app);
When you close the window, by (for example) pressing the X button,
the g_application_run() call returns with a number which is saved inside an integer variable named status.
Afterwards, the GtkApplication object is freed from memory with g_object_unref().
Finally the status integer is returned and the application exits.
带按钮的例子
#include <gtk/gtk.h>
static void
print_hello (GtkWidget *widget,
gpointer data)
{
g_print ("Hello World\n");
}
static void
activate (GtkApplication *app,
gpointer user_data)
{
GtkWidget *window;
GtkWidget *button;
GtkWidget *box;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
gtk_window_set_child (GTK_WINDOW (window), box);
button = gtk_button_new_with_label ("Hello World");
g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window);
gtk_box_append (GTK_BOX (box), button);
gtk_window_present (GTK_WINDOW (window));
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
GtkWidget *box;
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_window_set_child (GTK_WINDOW (window), box);
The GtkBox widget is created with gtk_box_new(), which takes a GtkOrientation enumeration value as parameter.
The buttons which this box will contain can either be laid out horizontally or vertically.
This does not matter in this particular case, as we are dealing with only one button.
After initializing box with the newly created GtkBox,
the code adds the box widget to the window widget using gtk_window_set_child().
button = gtk_button_new_with_label ("Hello World");
gtk_box_append (GTK_BOX (box), button);
Next the button variable is initialized in similar manner. gtk_button_new_with_label() is called which returns a GtkButton to be stored in button.
Afterwards button is added to our box.
g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
Using g_signal_connect(), the button is connected to a function in our app called print_hello(),
so that when the button is clicked, GTK will call this function.
As the print_hello() function does not use any data as input, NULL is passed to it.
print_hello() calls g_print() with the string “Hello World” which will print Hello World in a terminal if the GTK application was started from one.
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window); --可以传参数
After connecting print_hello(),
another signal is connected to the “clicked” state of the button using g_signal_connect_swapped().
This functions is similar to a g_signal_connect(), with the difference lying in how the callback function is treated;
g_signal_connect_swapped() allows you to specify what the callback function should take as parameter by letting you pass it as data.
In this case the function being called back is gtk_window_destroy() and the window pointer is passed to it.
This has the effect that when the button is clicked, the whole GTK window is destroyed.
In contrast if a normal g_signal_connect() were used to connect the “clicked” signal with gtk_window_destroy(),
then the function would be called on button (which would not go well, since the function expects a GtkWindow as argument).
排列
多个widget需要有一定的排列规则。
#include <gtk/gtk.h>
static void
print_hello (GtkWidget *widget,
gpointer data)
{
g_print ("Hello World\n");
}
static void
activate (GtkApplication *app,
gpointer user_data)
{
GtkWidget *window;
GtkWidget *grid;
GtkWidget *button;
/* create a new window, and set its title */
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Window");
/* Here we construct the container that is going pack our buttons */
grid = gtk_grid_new ();
/* Pack the container in the window */
gtk_window_set_child (GTK_WINDOW (window), grid);
button = gtk_button_new_with_label ("Button 1");
g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
/* Place the first button in the grid cell (0, 0), and make it fill
* just 1 cell horizontally and vertically (ie no spanning)
*/
gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1);
button = gtk_button_new_with_label ("Button 2");
g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
/* Place the second button in the grid cell (1, 0), and make it fill
* just 1 cell horizontally and vertically (ie no spanning)
*/
gtk_grid_attach (GTK_GRID (grid), button, 1, 0, 1, 1);
button = gtk_button_new_with_label ("Quit");
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window);
/* Place the Quit button in the grid cell (0, 1), and make it
* span 2 columns.
*/
gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 2, 1);
gtk_window_present (GTK_WINDOW (window));
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
自定义画布
#include <gtk/gtk.h>
/* Surface to store current scribbles */
static cairo_surface_t *surface = NULL;
static void
clear_surface (void)
{
cairo_t *cr;
cr = cairo_create (surface);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_paint (cr);
cairo_destroy (cr);
}
/* Create a new surface of the appropriate size to store our scribbles */
static void
resize_cb (GtkWidget *widget,
int width,
int height,
gpointer data)
{
if (surface)
{
cairo_surface_destroy (surface);
surface = NULL;
}
if (gtk_native_get_surface (gtk_widget_get_native (widget)))
{
surface = gdk_surface_create_similar_surface (gtk_native_get_surface (gtk_widget_get_native (widget)),
CAIRO_CONTENT_COLOR,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget));
/* Initialize the surface to white */
clear_surface ();
}
}
/* Redraw the screen from the surface. Note that the draw
* callback receives a ready-to-be-used cairo_t that is already
* clipped to only draw the exposed areas of the widget
*/
static void
draw_cb (GtkDrawingArea *drawing_area,
cairo_t *cr,
int width,
int height,
gpointer data)
{
cairo_set_source_surface (cr, surface, 0, 0);
cairo_paint (cr);
}
/* Draw a rectangle on the surface at the given position */
static void
draw_brush (GtkWidget *widget,
double x,
double y)
{
cairo_t *cr;
/* Paint to the surface, where we store our state */
cr = cairo_create (surface);
cairo_rectangle (cr, x - 3, y - 3, 6, 6);
cairo_fill (cr);
cairo_destroy (cr);
/* Now invalidate the drawing area. */
gtk_widget_queue_draw (widget);
}
static double start_x;
static double start_y;
static void
drag_begin (GtkGestureDrag *gesture,
double x,
double y,
GtkWidget *area)
{
start_x = x;
start_y = y;
draw_brush (area, x, y);
}
static void
drag_update (GtkGestureDrag *gesture,
double x,
double y,
GtkWidget *area)
{
draw_brush (area, start_x + x, start_y + y);
}
static void
drag_end (GtkGestureDrag *gesture,
double x,
double y,
GtkWidget *area)
{
draw_brush (area, start_x + x, start_y + y);
}
static void
pressed (GtkGestureClick *gesture,
int n_press,
double x,
double y,
GtkWidget *area)
{
clear_surface ();
gtk_widget_queue_draw (area);
}
static void
close_window (void)
{
if (surface)
cairo_surface_destroy (surface);
}
static void
activate (GtkApplication *app,
gpointer user_data)
{
GtkWidget *window;
GtkWidget *frame;
GtkWidget *drawing_area;
GtkGesture *drag;
GtkGesture *press;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
frame = gtk_frame_new (NULL);
gtk_window_set_child (GTK_WINDOW (window), frame);
drawing_area = gtk_drawing_area_new ();
/* set a minimum size */
gtk_widget_set_size_request (drawing_area, 100, 100);
gtk_frame_set_child (GTK_FRAME (frame), drawing_area);
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), draw_cb, NULL, NULL);
g_signal_connect_after (drawing_area, "resize", G_CALLBACK (resize_cb), NULL);
drag = gtk_gesture_drag_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (drag));
g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), drawing_area);
g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), drawing_area);
g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), drawing_area);
press = gtk_gesture_click_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (press), GDK_BUTTON_SECONDARY);
gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (press));
g_signal_connect (press, "pressed", G_CALLBACK (pressed), drawing_area);
gtk_window_present (GTK_WINDOW (window));
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
用ui文件描述界面布局的方法
#include <gtk/gtk.h>
#include <glib/gstdio.h>
static void
print_hello (GtkWidget *widget,
gpointer data)
{
g_print ("Hello World\n");
}
static void
quit_cb (GtkWindow *window)
{
gtk_window_close (window);
}
static void
activate (GtkApplication *app,
gpointer user_data)
{
/* Construct a GtkBuilder instance and load our UI description */
GtkBuilder *builder = gtk_builder_new ();
gtk_builder_add_from_file (builder, "builder.ui", NULL);
/* Connect signal handlers to the constructed widgets. */
GObject *window = gtk_builder_get_object (builder, "window");
gtk_window_set_application (GTK_WINDOW (window), app);
GObject *button = gtk_builder_get_object (builder, "button1");
g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
button = gtk_builder_get_object (builder, "button2");
g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
button = gtk_builder_get_object (builder, "quit");
g_signal_connect_swapped (button, "clicked", G_CALLBACK (quit_cb), window);
gtk_widget_set_visible (GTK_WIDGET (window), TRUE);
/* We do not need the builder any more */
g_object_unref (builder);
}
int
main (int argc,
char *argv[])
{
#ifdef GTK_SRCDIR
g_chdir (GTK_SRCDIR);
#endif
GtkApplication *app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
int status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object id="window" class="GtkWindow">
<property name="title">Grid</property>
<child>
<object id="grid" class="GtkGrid">
<child>
<object id="button1" class="GtkButton">
<property name="label">Button 1</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object id="button2" class="GtkButton">
<property name="label">Button 2</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object id="quit" class="GtkButton">
<property name="label">Quit</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
</object>
</child>
</object>
</interface>
注意grid一层是没有代码的,纯粹靠文件解析。我敲入时漏掉了,没有显示出来,后来才发现少了grid这一层。