2014年1月7日火曜日

MacとLinuxの挙動の違いについて

昨日ポストした,mac上で作ったGtk+を使って計算するプログラムを,Linuxでも動かそうと思ってコンパイルしてみました.
プラットフォームに依存することはしていないので完全に互換のはず,と思って単純にコンパイルだけしました.

しかし,コンパイラではerrorもwarningも出なかったのですが,実行してみたらセグメンテーション違反になってしまう.
がっくりきて小一時間いろいろ試したら,動くようになりました.

理由が二つありました.


  1. パイプを使ってgnuplotを開くときのパスの指定の仕方
  2. ファイルオープンが失敗したときにfcloseするべきかどうか


前者は,mac上ではgnuplotにパスが通っていれば,

FILE *gp;
gp = popen("gnuplot", "w");

で動いたのですが,Linuxでは

FILE *gp;
gp = popen("/usr/bin/gnuplot", "w");

としないと動きません.
これはすぐに分かったのですが,問題は次のファイルオープンが失敗した時の処理です.

計算し終わったデータを格納するファイルが,同じファイル名で既に存在する場合,基本的にはアペンドモードで追記しているので問題ないのですが,念のため以下のように確認するダイアログを出していました.

  if((fp = fopen (filename, "r")) == NULL){
    fclose(fp);
    start_or_stop=1;
  }
  else{
  fclose(fp);
  GtkWidget *dialog;
  GtkWidget *parent;
  GtkWidget *labeld;
  gint response;
.
.
.
(以下にダイアログを出すコードが続きます.)
これは単純にファイルが存在するかどうかを確認するために付け加えたコードでした.
以前,c言語ではファイルのオープンに失敗しても,そのポインタにNULLが格納されて,以後書き込む時にNULLに書き込んでしまい危険だから云々...
と勉強した記憶があったので,fcloseを書いていました.

macではこれで実行しても問題ないし,linuxでもコンパイラではerrorもwarningも出なかったのですが,実行すると無情のセグメンテーション違反とのお答えが..

おかしい場所はここしかないと思ったので,fpがNULLのときのfcloseをコメントアウトすると,無事に動きました.

なぜでしょう??

2014年1月6日月曜日

Macでgtk2のテーマをかえてみたい!(finkです)

Finkでgtk+のテーマを変更する方法を見つけたのでメモしておきます.

まずは,finkでgtk-change-theme,gtk2-engineをインストール.
これで最低限テーマを変更できるようになります.
起動は,

$ gtk-chtheme

です.GUIのテーマセレクタが現れて,いくつか選べるようになります.



上の画像は,すでに他のテーマをいくつかインストールしたあとなので,選択肢が豊富です.
単純に,finkでgnome-themesとgnome-themes-glossy-pをインストールするだけでもかなりの数のテーマが使えるようになりますが,ウェブから落としてきたテーマを/sw/share/themes以下にコピーしても使えます.

見た目はかなりいじれるようになり,


こんな感じになったり,


こんな感じにすることも出来ます.

ちなみにこの画像は前のブログエントリで書いたマルチスレッドのGTKプログラムですが,fileメニューはただQuitだけがあるシンプルなものです.
グラフはgnuplotに描かせているのですが,これはプログラムの中からパイプで呼び出しています.
今回,gnuplotの出力ターミナルをwxtにしています.

これには理由が二つあり,

  1. 右クリックでグラフのレンジを変えられる(拡大が可能)
  2. デフォルトだとaquatermになってしまうので,プロットするたびにマウスフォーカスがaquaにうつってしまい,煩わしい(wxtならマウスフォーカスは常にX11のまま)

からです.



C言語で,Gtk+とgthread

gtk+2.0とgthreadでマルチスレッドのプログラムを作りたいのですが,意外と情報が少なく困っています.

たとえばGUIで測定プログラムを動かしていて,途中で電流値などのパラメータを変えたいと思っても,マルチスレッド化しなければ普通はループが終るまで変数の変更を許してくれません.

そこで,測定ルーチンは別のスレッドで無限ループにしておいて,メインのスレッドではボタンをクリックしたりentryウィジェットで数値を入力したりしてループ条件やパラメータを変更する,と言う風にスレッドを分けることが必要になります.

具体的には,メイン関数の中で
GThread* thread;

g_thread_init(NULL);
gdk_threads_init();

と初期化した後,
thread = g_thread_create ( thread_func, (gpointer)label3, FALSE, &error );
という風にしてthread_funcを別スレッドで立ち上げる.ここでlabel3を引数として与えているのは,thread_funcのなかでlabel3のテキストをリアルタイムで書き換えているからです.

thread_funcはgpointer型にする必要があるらしく,実際の中身は
static gpointer thread_func ( gpointer data )
{
  while( TRUE ){
  g_usleep(500000);
   
  if(start_or_stop != 0){
  .
  .
  .
  gtk_label_set_text(GTK_LABEL (data), buf);
  }
  
  }
みたいな感じにしています.

ここで,start_or_stopはintで宣言していて,0のときは何も実行されない.
メイン関数のボタンをクリックするとstart_or_stopが0になったり1になったりする,という仕組みです.
gboolean型でも良かったのかもしれないのですが,caseで処理を分けるかもしれないことを考えて,intにしておきました.

実際はこんな感じ



これは実際に測定しているわけではなく,0.5秒おきにスピン1/2三角格子ハイゼンベルグ反強磁性体の帯磁率を1ケルビンおきに計算してプロットするというプログラムを測定ルーチンのかわりに入れています.