GUIでファイルを探す処理に、GTK+を利用してみよう。
プログラムでファイルを選ぶ場合、あらかじめシェルかファイラーを用い、ファイル名を正確に代入する必要になる。選んだファイル名を返す、単純なGUIのプログラムがあれば汎用できるので、便利である。今回は、GTK+というツールボックスを使用して、GUIでファイルを探すプログラムの制作を行ってみよう。
設定として、簡単で単純なプログラム。汎用性が高いこと。カスタマイズ可能。GTK+の学習を目的とする。
filechooser.c
#include <gtk/gtk.h> int main(int argc, char *argv[]){ GtkWidget *dialog; gtk_init(&argc, &argv); GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; gint res; dialog = gtk_file_chooser_dialog_new ("Open File", NULL, action, ("Cancelする"), GTK_RESPONSE_CANCEL, ("Openする"), GTK_RESPONSE_ACCEPT, NULL); res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_ACCEPT) { char *filename; GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog); filename = gtk_file_chooser_get_filename (chooser); g_print("%s\n",filename); //open_file (filename); g_free (filename); } gtk_widget_destroy (dialog); return 0; }
コンパイルは以下で。
gcc filechooser.c -o filechooser `pkg-config --cflags --libs gtk+-3.0`
エラーが出る
Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged.
gtk_dialog_run (GTK_DIALOG (dialog)); このエラーはで生じてる。
gtk_file_chooser_dialog_new の二番目の引数、NULLだと生じるのか?
そのようだった。main関数から、呼び出すと消えた。
#include <gtk/gtk.h> int file_selection_dialog(GtkWidget *window ,gchar *str){ GtkWidget *dialog; GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; gint res;; dialog = gtk_file_chooser_dialog_new ("Open File", GTK_WINDOW (window), //NULL, action, ("Cancel"), GTK_RESPONSE_CANCEL, ("Open"), GTK_RESPONSE_ACCEPT, NULL); GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog); gtk_file_chooser_set_current_folder (chooser,str); res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_ACCEPT) { char *filename; filename = gtk_file_chooser_get_filename (chooser); g_print("%s\n",filename); g_free (filename); } gtk_widget_destroy (dialog); } int main(int argc, char** argv){ GtkWidget *window; gtk_init(&argc,&argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(window, "destroy",G_CALLBACK(gtk_main_quit), NULL); file_selection_dialog(window, argv[1]); return 0; }
main関数とは、別に関数を作り、mainから呼び出すと、GtkDialog mapped without a transient parentのエラーは消えた。mainの中でgtk_file_chooser_dialog_newウィジェットを実行してもエラーがでない方法がありそうだが。組み合わせをいじってみて、mainの中でも動くようになった。改めてみると、単純な表現であるが、初心者が戸惑うツボが垣間見れる。
#include <gtk/gtk.h> int main(int argc, char *argv[]){ GtkWidget *dialog; GtkWidget *window; gtk_init(&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gint res; dialog = gtk_file_chooser_dialog_new ("Open File", GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN, ("Cancelする"), GTK_RESPONSE_CANCEL, ("Openする"), GTK_RESPONSE_ACCEPT, NULL); GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog); if (argv[1]) gtk_file_chooser_set_current_folder (chooser,argv[1]); res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_ACCEPT) { char *filename; filename = gtk_file_chooser_get_filename (chooser); g_print("%s\n",filename); g_free (filename); } return 0; }
実行すると、引数のディレクトリーを指定できるので、そのところで開く。
ファイルを選ぶと、標準出力にファイル名を出すようしにした。
シェルスクリプトから呼び出すと、応用できる範囲は広い。
set filePath [exec /home/user/gtk/filechooser /home/user/Archives/Photos/Image]
このように、tclでfilePathにファイル名を代入することができる。Tkで使ってた、tk_getOpenFile をこちらに変えたのである。Tkではプレビューがないので画像ファイルは、呼び出して表示と手間がかかっていた。この方法だとプレビューと言うか、アイコンが見えるのあるが……。小さい。まったく小さい。大きさ変えられないし。もっと大きなアイコン表示ができる?プレビューが同じ窓でできない?
You can add a custom preview widget to a file chooser and then get notification about when the preview needs to be updated. To install a preview widget, use Gtk.FileChooser.set_preview_widget(). Then, connect to the Gtk.FileChooser ::update-preview signal to get notified when you need to update the contents of the preview.
gtk_file_chooser_set_preview_widget()を使ってみた。
#include <gtk/gtk.h> static void update_preview_cb (GtkFileChooser *file_chooser, gpointer data) { GtkWidget *preview; char *filename; GdkPixbuf *pixbuf; gboolean have_preview; preview = GTK_WIDGET (data); if (filename = gtk_file_chooser_get_preview_filename (file_chooser)) { pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 256, 256, NULL); have_preview = (pixbuf != NULL); g_free (filename); gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf); if (pixbuf) g_object_unref (pixbuf); gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview); } } int main(int argc, char *argv[]) { GtkWidget *dialog; GtkWidget *window; GtkWidget *preview; gtk_init(&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); dialog = gtk_file_chooser_dialog_new ("Open File", GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN, ("Cancelする"), GTK_RESPONSE_CANCEL, ("Openする"), GTK_RESPONSE_ACCEPT, NULL); preview = gtk_image_new (); GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog); gtk_file_chooser_set_preview_widget (chooser, preview); g_signal_connect (chooser, "update-preview", G_CALLBACK (update_preview_cb), preview); if (argv[1]) gtk_file_chooser_set_current_folder (chooser,argv[1]); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { char *filename; filename = gtk_file_chooser_get_filename (chooser); g_print("%s\n",filename); g_free (filename); } gtk_widget_destroy (dialog); return 0; }
これだと画像を選択すれば、右にプレビューが表示されるので便利だ。
https://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html#gtk-file-chooser-dialog-new
https://developer.gnome.org/gtk3/stable/GtkFileChooser.html
https://lazka.github.io/pgi-docs/Gtk-3.0/interfaces/FileChooser.html
追加
Couldn’t connect to accessibility bus: Failed to connect to socket /tmp/dbus
が出現するのを消すには、
export NO_AT_BRIDGE=1
を実行しておく、bashrcに記載しておく。fvwmのメニューの中で実行して、エラーが出るので、wish中に
set ::env(NO_AT_BRIDGE) 1
を追加するとエラーが出なくなった。dbusのバグのようだが。