GTK4   スケール

ホーム   C/C++チュートリアル  

スケール

スケールはスライダーと言ったほうが分かりやすいかもしれません。よくスライダーと呼ばれるものを、GTK ではスケールと呼びます。

次のファイルを scale.c というファイル名で任意の場所に保存してください。

コードが少し簡単になりました

scale.c


/*************************************
    scale.c
    copyright    :  vivacocoa.jp
    last modified:  Mar. 19, 2025
************************************/

#include <gtk/gtk.h>

guchar r, g, b;
	
static void
color (GtkWidget *widget, gpointer data)
{
	if (g_strcmp0 (gtk_widget_get_name (widget), "red") == 0)
	{
		r = gtk_range_get_value (GTK_RANGE (widget));
		gtk_widget_queue_draw (data);
	}
	if (g_strcmp0 (gtk_widget_get_name (widget), "green") == 0)
	{
		g = gtk_range_get_value (GTK_RANGE (widget));
		gtk_widget_queue_draw (data);
	}
	if (g_strcmp0 (gtk_widget_get_name (widget), "blue") == 0)
	{
		b = gtk_range_get_value (GTK_RANGE (widget));
		gtk_widget_queue_draw (data);
	}
}

static void
draw (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer data)
{
	GdkRGBA color;
	gchar   *rgb;
	
	rgb = g_strdup_printf ("rgb (%d,%d,%d)", r, g, b);
	gdk_rgba_parse              (&color, rgb);
	g_free                      (rgb);
	gdk_cairo_set_source_rgba   (cr, &color);
	cairo_paint                 (cr);
}

static void
activate (GApplication *app, gpointer data)
{
	GtkWidget * win    = gtk_application_window_new (GTK_APPLICATION (app));
	GtkWidget * vbox   = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
	GtkWidget * area   = gtk_drawing_area_new ();
	GtkWidget * grid   = gtk_grid_new ();
	GtkWidget * lred   = gtk_label_new ("Red");
	GtkWidget * lgreen = gtk_label_new ("Green");
	GtkWidget * lblue  = gtk_label_new ("Blue");
	GtkWidget * red    = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 255, 1);
	GtkWidget * green  = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 255, 1);
	GtkWidget * blue   = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 255, 1);
	
	gtk_window_set_child (GTK_WINDOW (win), vbox);
	gtk_window_set_title (GTK_WINDOW (win), "Scale");
	gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
	
	gtk_box_append (GTK_BOX (vbox), area);
	gtk_box_append (GTK_BOX (vbox), grid);
	
	gtk_grid_attach (GTK_GRID (grid), lred,   0, 0, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), lgreen, 0, 1, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), lblue,  0, 2, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), red,    1, 0, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), green,  1, 1, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), blue,   1, 2, 1, 1);
	
	gtk_widget_set_hexpand (area, TRUE);
	gtk_widget_set_vexpand (area, TRUE);
	gtk_widget_set_size_request (area, 100, 100);
	
	gtk_label_set_xalign (GTK_LABEL (lred),   1.0);
	gtk_label_set_xalign (GTK_LABEL (lgreen), 1.0);
	gtk_label_set_xalign (GTK_LABEL (lblue),  1.0);
	
	gtk_widget_set_hexpand (red, TRUE);
	
	gtk_widget_set_name (red,   "red");
	gtk_widget_set_name (green, "green");
	gtk_widget_set_name (blue,  "blue");
	
	gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw, NULL, NULL);
	g_signal_connect (red,   "value-changed", G_CALLBACK (color), area);
	g_signal_connect (green, "value_changed", G_CALLBACK (color), area);
	g_signal_connect (blue,  "value_changed", G_CALLBACK (color), area);
	
	gtk_window_present (GTK_WINDOW (win));
}

int
main (int argc, char **argv)
{
	GtkApplication *app;
	int stat;
	
	app = gtk_application_new ("jp.vivacocoa.scale", G_APPLICATION_DEFAULT_FLAGS);
	g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
	stat = g_application_run (G_APPLICATION (app), argc, argv);
	g_object_unref (app);
	
	return stat;
}

コード説明

  1. static void
    color (GtkWidget *widget, gpointer data)
    スケールが動かされたときに呼び出さる関数です。この関数で、グローバル変数 r, g, b の値を変更して、 実際の描画を gtk_widget_queue_draw 関数で draw 関数に依頼しています。
  2. static void
    draw (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer data)
    実際に描画を担当している関数です。この関数の説明は「GTK4 カイロ」で詳しく説明します。この関数は ドローイングエリアが最初に表示されたときと、ドローイングエリアがリサイズされたときと、gtk_widget_queue_draw 関数が呼びだれたときに呼び出されます。
  3. gtk_widget_set_name (red, "red");
    どのスケールかを識別できるように名前をつけています。static void color 関数では、この名前を利用して、 どの色の値を変更すれば良いかを判断しています。
  4. g_signal_connect (red, "value-changed", G_CALLBACK (color), area);
    red スケールで value-changed イベントが発生したときに color 関数を呼び出します。color 関数の第1引数には red スケールのポインターが渡され、color 関数の第2引数には g_signal_connect の第4引数のドローイングエリア (area) が渡されます。なお value-changed イベントは、スケールのつまみを動かしたときに発生します。

コンパイルと実行

以下のとおりです。Windows の場合は通常版でも ARM64版 でも mingw64 で行ってください。


// コンパイル
cc `pkg-config --cflags gtk4` scale.c -o scale `pkg-config --libs gtk4`

もしくは

gcc $(pkg-config --cflags gtk4) -o scale scale.c $(pkg-config --libs gtk4)

もしくは

gcc scale.c -o scale `pkg-config --cflags --libs gtk4`

// 実行
./scale

それぞれのスケールを動かして、いろいろな色を作れます。

光の三原色になっています。いろいろと試してみてください。

難しいほうのコードも残しておきます


/*************************************
    scale.c
    copyright    :  vivacocoa.jp
    last modified:  Mar. 19, 2025
************************************/

#include <gtk/gtk.h>

static cairo_surface_t *surface = NULL;
	
static void
color (GtkWidget *widget, gpointer data)
{
static double r, g, b;
if (g_strcmp0 (gtk_widget_get_name (widget), "red") == 0)
{
	r = gtk_range_get_value (GTK_RANGE (widget));
	gtk_widget_queue_draw (data);
}
if (g_strcmp0 (gtk_widget_get_name (widget), "green") == 0)
{
	g = gtk_range_get_value (GTK_RANGE (widget));
	gtk_widget_queue_draw (data);
}
if (g_strcmp0 (gtk_widget_get_name (widget), "blue") == 0)
{
	b = gtk_range_get_value (GTK_RANGE (widget));
	gtk_widget_queue_draw (data);
}
	cairo_t *cr;
	cr = cairo_create (surface);
	cairo_set_source_rgb (cr, r, g, b);
	cairo_paint (cr);
	cairo_destroy (cr);
}

static void
resize (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 = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
					gtk_widget_get_width (widget),
					gtk_widget_get_height (widget));
		color (widget, widget);
	}
}

static void
func (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer data)
{
	cairo_set_source_surface (cr, surface, 0, 0);
	cairo_paint (cr);
}

static void
activate (GApplication *app, gpointer data)
{
	GtkWidget * win    = gtk_application_window_new (GTK_APPLICATION (app));
	GtkWidget * vbox   = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
	GtkWidget * area   = gtk_drawing_area_new ();
	GtkWidget * grid   = gtk_grid_new ();
	GtkWidget * lred   = gtk_label_new ("Red");
	GtkWidget * lgreen = gtk_label_new ("Green");
	GtkWidget * lblue  = gtk_label_new ("Blue");
	GtkWidget * red    = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0.0, 1.0, 0.01);
	GtkWidget * green  = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0.0, 1.0, 0.01);
	GtkWidget * blue   = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0.0, 1.0, 0.01);
	
	gtk_window_set_child (GTK_WINDOW (win), vbox);
	gtk_window_set_title (GTK_WINDOW (win), "Scale");
	gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
	
	gtk_box_append (GTK_BOX (vbox), area);
	gtk_box_append (GTK_BOX (vbox), grid);
	
	gtk_grid_attach (GTK_GRID (grid), lred,   0, 0, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), lgreen, 0, 1, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), lblue,  0, 2, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), red,    1, 0, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), green,  1, 1, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), blue,   1, 2, 1, 1);
	
	gtk_widget_set_hexpand (area, TRUE);
	gtk_widget_set_vexpand (area, TRUE);
	gtk_widget_set_size_request (area, 100, 100);
	
	gtk_label_set_xalign (GTK_LABEL (lred),   1.0);
	gtk_label_set_xalign (GTK_LABEL (lgreen), 1.0);
	gtk_label_set_xalign (GTK_LABEL (lblue),  1.0);
	
	gtk_widget_set_hexpand (red, TRUE);
	
	gtk_widget_set_name (red,   "red");
	gtk_widget_set_name (green, "green");
	gtk_widget_set_name (blue,  "blue");
	
	g_signal_connect_after (area, "resize", G_CALLBACK (resize), NULL);
	gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), func, NULL, NULL);
	g_signal_connect (red,   "value-changed", G_CALLBACK (color), area);
	g_signal_connect (green, "value_changed", G_CALLBACK (color), area);
	g_signal_connect (blue,  "value_changed", G_CALLBACK (color), area);
	
	gtk_window_present (GTK_WINDOW (win));
}

int
main (int argc, char **argv)
{
	GtkApplication *app;
	int stat;
	
	app = gtk_application_new ("jp.vivacocoa.scale", G_APPLICATION_DEFAULT_FLAGS);
	g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
	stat = g_application_run (G_APPLICATION (app), argc, argv);
	g_object_unref (app);
	
	return stat;
}

ダブルクリックで起動する

Linux

Debian、Fedora、Kali でビルドしたアプリはどこに移動させてもダブルクリックで起動できます。 また違う Linuxに移動させても起動できます。

Ubuntu、Mint ではダブルクリックで起動できなかったと思います (Ubuntu、Mint はインストールしていません)。

さらに、Arm64 版でビルドしたアプリは x86_64 版では起動しないのではないかと思われます。 (x86_64 環境がないので試していません。)

macOS

ビルドしたアプリはどこに移動させてもダブルクリックで起動できますが、同時にターミナルも起動してしまいます。 これを回避するには、ターミナルで次のようにします。


mkdir scale.app

mv scale scale.app
もしくは
cp scale scale.app

以上で scale というアプリができていると思います。 これをダブルクリックで起動させてもターミナルは起動しません。どこへ移動させても起動できます。 他の Mac に移動させた場合は起動できません。

Windows

ビルドしたアプリはどこへ移動させても起動しますが、同時にコマンドプロンプトも起動します。 これを回避するには mingw64 で次のようにビルドするときに -mwindows オプションをつけます。


gcc scale.c -o scale `pkg-config --cflags --libs gtk4` -mwindows

これでビルドされた scale.exe はどこに移動させてもダブルクリックで起動することができますし、 コマンドプロンプトが起動することもありません。ただし違う Windows に移動させた場合は起動できません。


236 visits
Posted: Mar. 17, 2025
Update: Mar. 22, 2025

ホーム   C/C++チュートリアル   目次