Vala言語で外部プロセスを実行する(スレッドを使用してGTK+のテキストビューに実行結果を表示・コード例)
「Vala言語で外部プロセスを実行する(スレッドを使用してGTK+のテキストビューに実行結果を表示・メモ)」の内容を踏まえた上で、GTK+上のテキストビュー上に入力したコマンドを実行してテキストビューに結果を表示するためのコード例を貼り付ける。
今回、子プロセスのステータス値はPosix.waitpid()で取得し、子プロセスの監視(GLib.ChildWatch.add())は行っていない。
[任意]ファイル名: spawnasyncwithpipestest3.vala
using GLib; using Gtk; using Gdk; using Posix; using Pango; /* * valac --thread --pkg posix --pkg gtk+-2.0 -o spawnasyncwithpipestest3 spawnasyncwithpipestest3.vala */ namespace SpawnAsyncWithPipesTest3 { class MainWindow : Gtk.Window { Gtk.AccelGroup accelgroup; Gtk.ImageMenuItem item_quit; Gtk.Menu menu_file; Gtk.MenuItem item_file; Gtk.MenuBar menubar; Gtk.Entry entry; Gtk.Button button; Gtk.TextView textview; Gtk.TextBuffer textbuf; Gtk.ScrolledWindow sw; Gtk.HBox hbox; Gtk.VBox vbox; GLib.Pid child_pid; int child_stdin; int child_stdout; int child_stderr; public MainWindow () { /* ショートカットキー(アクセラレータ) */ this.accelgroup = new Gtk.AccelGroup (); this.add_accel_group (this.accelgroup); /* メニュー項目 */ this.item_quit = new Gtk.ImageMenuItem.from_stock (Gtk.STOCK_QUIT, accelgroup); this.menu_file = new Gtk.Menu (); this.menu_file.add (item_quit); this.item_file = new Gtk.MenuItem.with_mnemonic ("_File"); this.item_file.set_submenu (menu_file); this.menubar = new Gtk.MenuBar (); this.menubar.append (item_file); /* 出力のテキストビューの下に1行テキスト入力欄と停止ボタンを横に並べる */ this.entry = new Gtk.Entry (); this.button = new Gtk.Button.from_stock (Gtk.STOCK_STOP); this.button.sensitive = false; // 最初は無効 this.hbox = new Gtk.HBox (false, 0); this.hbox.pack_start (this.entry, true, true, 0); this.hbox.pack_start (this.button, false, false, 0); this.textview = new Gtk.TextView (); this.textbuf = this.textview.buffer; this.textview.editable = false; // 編集不可 this.textview.modify_font (Pango.FontDescription.from_string ("Monospace, Normal 10")); /* レイアウトなど */ this.sw = new Gtk.ScrolledWindow (null, null); this.sw.add (this.textview); this.sw.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC); this.vbox = new Gtk.VBox (false, 0); this.vbox.pack_start (this.menubar, false, false, 0); this.vbox.pack_start (this.sw, true, true, 0); this.vbox.pack_start (this.hbox, false, false, 0); this.add (this.vbox); this.set_size_request (600, 360); /* テキスト入力欄をフォーカス */ this.entry.grab_focus (); /* シグナル */ this.entry.activate += this.on_entry_activated; this.button.clicked += (source) => { /* Windows上ではプロセスIDから終了するには * http://support.microsoft.com/kb/409542/ja * のようにするが、Windows APIのバインディングはVala 0.7.4の時点ではない */ Posix.kill ((Posix.pid_t) this.child_pid, Posix.SIGTERM); source.sensitive = false; }; this.item_quit.activate += Gtk.main_quit; this.destroy += Gtk.main_quit; } void on_entry_activated (Gtk.Entry source) { weak GLib.Thread th_stdout, th_stderr; string[] argv; /* テキスト入力欄から文字列を取り出す */ string cmdline = source.text; source.text = ""; // クリア try { /* コマンド行文字列をリストの形式に変換 */ GLib.Shell.parse_argv (cmdline, out argv); } catch (GLib.ShellError e) { GLib.warning ("Cannot parse command line: %s\n(%s)", cmdline, e.message); return; } GLib.debug ("cmdline: %s", cmdline); try { if (GLib.Process.spawn_async_with_pipes (null, argv, null, GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, null, out this.child_pid, out this.child_stdin, out this.child_stdout, out this.child_stderr)) this.button.sensitive = true; try { th_stdout = GLib.Thread.create (this.display_stdout, false); th_stderr = GLib.Thread.create (this.display_stderr, false); } catch (GLib.ThreadError e) { GLib.warning ("ThreadError: %s", e.message); } } catch (GLib.SpawnError e) // 子プロセス起動に失敗 { Gtk.TextIter iter; GLib.warning ("spawn failed: %s", e.message); this.textbuf.get_end_iter (out iter); this.textbuf.place_cursor (iter); this.textbuf.insert_at_cursor ("Failed to execute \"%s\"\n(%s)\n".printf (cmdline, e.message), -1); this.textview.scroll_to_mark (this.textbuf.get_insert (), 0, false, 0, 0); } } void *display_stdout () { int status; Gtk.TextIter iter; string statusmsg; this.display_output (this.child_stdout); Posix.waitpid ((Posix.pid_t) this.child_pid, out status, 0); GLib.Process.close_pid (this.child_pid); statusmsg = "child exited, raw status: %d".printf (status); if (GLib.Process.if_exited (status)) statusmsg += " exit status: %d\n".printf (GLib.Process.exit_status (status)); Gdk.threads_enter (); this.textbuf.get_end_iter (out iter); this.textbuf.place_cursor (iter); this.textbuf.insert_at_cursor (statusmsg, -1); this.textview.scroll_to_mark (this.textbuf.get_insert (), 0, false, 0, 0); this.button.sensitive = false; Gdk.threads_leave (); /* * 条件によって(?)コンパイルエラーが出るので記述 * "missing return statement at end of method body" */ return null; } void *display_stderr () { this.display_output (this.child_stderr); return null; } void display_output (int fd) { string line; // 出力の1行を格納 string encoding; GLib.IOStatus iostatus; // 出力の読み込みで使用 size_t length, terminator_pos; // IOチャンネルから書き込まれる Gtk.TextIter iter; /* ファイル記述子を開くのにIOチャンネルを使用する */ GLib.IOChannel ioch = new GLib.IOChannel.unix_new (fd); /* UTF-8以外の場合にエンコーディングを指定 */ if (GLib.get_charset (out encoding) == false) { try { ioch.set_encoding (encoding); } catch (GLib.IOChannelError e) { ; } } for (;;) { try { /* 出力から1行読み込む */ iostatus = ioch.read_line (out line, out length, out terminator_pos); } catch (GLib.IOChannelError e) { break; } catch (GLib.ConvertError e) { break; } if (iostatus != GLib.IOStatus.NORMAL) break; Gdk.threads_enter (); this.textbuf.get_end_iter (out iter); this.textbuf.place_cursor (iter); this.textbuf.insert_at_cursor (line, -1); this.textview.scroll_to_mark (this.textbuf.get_insert (), 0, false, 0, 0); Gdk.threads_leave (); } /* 後始末 */ try { ioch.shutdown (false); } catch (GLib.IOChannelError e) { ; } } } class MainClass { public static int main (string[] args) { if (! GLib.Thread.supported ()) { GLib.error ("This program needs threading support.\n"); return 1; } Gtk.init (ref args); Gdk.threads_init (); MainWindow win = new MainWindow (); win.show_all (); Gtk.main (); return 0; } } }
関連記事:
- 外部プロセスをバックグラウンド実行しつつ、出力をGTK+のテキストビューにリアルタイム表示する - PyGTKでの例
- Vala言語で外部プロセスを実行する(簡単な例・メモ)
- Vala言語で外部プロセスを実行する(簡単な例・コード例と出力結果)
- Vala言語で外部プロセスを実行する(GLib.Process.spawn_async_with_pipes()を使用・メインループを用いた例・メモ)
- Vala言語で外部プロセスを実行する(GLib.Process.spawn_async_with_pipes()を使用・メインループを用いた例・コード例)
- Vala言語で外部プロセスを実行する(GLib.Process.spawn_async_with_pipes()を使用・メインループを用いない場合と出力メッセージの処理)
- Vala言語で外部プロセスを実行する(スレッドを使用してGTK+のテキストビューに実行結果を表示・メモ)
- Vala言語で外部プロセスを実行する(スレッドを使用せずにGTK+のテキストビューに実行結果を表示するテスト)
使用したバージョン:
- Vala 0.7.4, 0.7.5