GTK+でファイルを選ぶ、gtk_file_chooser_dialog_new

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のバグのようだが。

 

ロジカルシンキングを考えてみる

ロジカルシンキングと言う言葉があり、主にビジネスの世界で使われている。数社のコンサルタント会社が使っているようである。
そのロジカルシンキングとは、何なのかを検証してみよう。

ロジカルシンキングの定義は

ロジカルシンキング(logical thinking)とは、一貫していて筋が通っている考え方、あるいは説明の仕方のことである。

とされている。筋が通っている?考え方?または説明の仕方?この定義だとロジカル関係なくないかな?筋が通っていればロジカルシンキングになるということなのか?甚だ疑問が浮かぶ。

日本語だと論理思考、論理的思考と言われるようだが、議論の余地があるとwikipediaでは主張されている。
また、英語圏で一般的に用いられる言葉でなく、日本で浸透してるという意味では、和製英語であると言える。

まずロジカルシンキングの「シンキング」、日本語では「思考」とは何か。

思考(しこう、英: Thinking)は、考えや思いを巡らせる行動[1]であり、結論を導き出す[2]など何かしら一定の状態に達しようとする過程において、筋道や方法など模索する精神の活動である[3] 出典Wikipedia

思考とは、思いを巡らせる行動であり、精神の活動であると定義されている。
ロジカルシンキング、論理思考とは、論理的である、思いを巡らす行動であり、精神の活動であることを意味しようとしているのか。

次にロジカルシンキングのロジカル、理論について考えてる。この場合の理論は理論学の理論ではなく、筋が通っているという意味合いで理論という言葉を使っている。具体的に何とか理論を使うというものでもなく、いくつかの方法を使用した、思考であると言っている。理論という言葉が誤って使われているのだ。wikipediaに改善の余地があると指定されているのは、その不正確な言葉の使用が理由であると伺えよう。
さらに、思考という単語も使っているが、思考は思いを巡らす行動、精神の活動であって、直接にロジカルと繋がらない、独立した別なものである。このロジカルは、思考をコントロールするものでも、影響を与えるものでもない。
つまり、ロジカルシンキングという言葉は、不正確で誤った意味に捉えかねない紛らわしく不適切な言葉であるといえる。

fvwmに、fvwm-menu-desktop を使用し XDG Menusをメニュー追加

fvwmのメニューはカスタマイズが簡単にできるが、自動的に生成されるメニューはDebianメニューを使っていたが、
もっと便利なXDG Menusがあったのでこれを使えるように設定した。
StartFunctionsに以下を追加 

+ I Test (!f $[FVWM_USERDIR]/.XDGMenu) XDGRegen
+ I Read $[FVWM_USERDIR]/.XDGMenu

.XDGMenuファイルがないと、XDGRegenを実行し、そのファイルを読み込む。
XDGRegenは、以下で設定する。

DestroyFunc XDGRegen
AddToFunc XDGRegen
+ I PipeRead 'fvwm-menu-desktop --regen-cmd XDGRegen > \
    $[FVWM_USERDIR]/.XDGMenu; echo "Nop"'
+ I Read $[FVWM_USERDIR]/.XDGMenu

https://www.mankier.com/1/fvwm-menu-desktop

$avrdude メモ

$avrdude -P /dev/ttyUSB0 -b 19200 -c avrisp -p m328p 
$avrdude -P /dev/ttyUSB0 -b 19200 -c avrisp -p m328p -v -e -U lfuse:w:0xe2:m  

/etc/avrdude.conf

programmer
  id="ftdi";
  desc = "FT232R Synchronous BitBang";
  #type = "ft245r";
  type = "ftdi_syncbb";
  miso = 3;  # CTS X3(1)
  sck = 5;  # DSR X3(2)
  mosi = 6;  # DCD X3(3)
  reset = 7;  # RI X3(4)
;
sudo avrdude -c ftdi -p m328p  -P ft0 -b 4800
$sudo avrdude -b 19200 -c ftdi  -p m328p -P ft0 -v -e -U lfuse:w:0xff:m 
$sudo avrdude -c ftdi -p m328p  -P ft0 -b 19200 -F -v -e -U flash:w:/usr/share/arduino/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168_atmega328.hex
http://eleccelerator.com/fusecalc/fusecalc.php?chip=atmega328p