Programming」カテゴリーアーカイブ

Virtual Terminal Emulator

VTE といってもホンダのバイクじゃありません。
Virtual Terminal Emulator の略であります。

Gedit 標準プラグインに Python コンソールがありますが全然違う。
gnome-terminal そのものみたいなエミュレーターです。

GNOME wiki の VTE Terminal Widget Library ページ。
Apps/Terminal/VTE – GNOME Wiki!

バージョンがややこいけど 2.91=0.39 のようだ。
例によって GNOME プロジェクト作だから合わせたかったのかな。
vte291-devel-0.39.90-1.fc22.armv7hl.rpm | RPM Info | koji

vte01

Fedora にはしっかり gir で入っていますね。
多分 gnome-terminal 採用のディストリなら大抵あると思う。
2.91 は見当たらないので 0.39 のドキュメントを見ればいいだろう。
VTE Reference Manual: VTE Reference Manual

参考が欲しいのでサンプルコードを探す。

c – gtk+ vte scrollback not working – Stack Overflow
python – How to add vte terminal widget in GTK3? – Ask Ubuntu

C や Python から使う方法は海外でアッサリ見つかった。
日本語情報は今のところ皆無、いつものことだ。

しかし古いようで vte_terminal_fork_command 関数は 2.91 で使えない。
どうやら現行は vte_terminal_spawn_sync 関数を使うようだ。

ということで。
早速 Python バインディングにて書いてみよう。

#!/usr/bin/env python3

from gi.repository import Gtk, Vte, GLib

class VteWindow(Gtk.Window):
    """
        Vte Sample (v2.91)
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # Create a virtual terminal 
        terminal = Vte.Terminal.new()
        # Exit of Ctrl+D
        terminal.connect("child-exited", self.on_terminal_child_exited)
        # Sync
        terminal.spawn_sync(
                Vte.PtyFlags.DEFAULT,
                GLib.get_home_dir(),
                #["/bin/sh"], @ List of Execute Command
                ["/usr/bin/python3"],
                None,
                GLib.SpawnFlags.DO_NOT_REAP_CHILD,
                None,
                None,
                None)
        self.add(terminal)
        self.set_title("Virtual Terminal")
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_terminal_child_exited(self, vteterminal, status):
        Gtk.main_quit()

VteWindow()
Gtk.main()

vte02

spawn_sync の引数については devhelp で詳しく。
三番目引数を sh や perl に変更すれば当然それらが起動する。
それと上記は起動したコマンドの終了を検知して自分も終了させている。
後はお好みで弄ってみてください。
今後バージョンで変わると思うので 2.91 と念を押して。

/bin/sh で起動してソコから Python を始めるとかも可能。
Ctrl+D で Python を終了すると sh に戻る、マジで普通の端末。

しかし自分で試して驚いた、こんなにアッサリ実装できるとは。
自分好みに拡張したオリジナル端末を作るだけでも面白そう。
ただ一言、日本語の情報がまったく無いのはキビシイze!

GtkSourceLanguageManager

テキストエディタの色分け表示については Gedit は超強力です。
実はコレ Gedit ではなく GtkSourceView 自体の機能だったりする。
下記ディレクトリに定義 XML ファイルがある時点で解るだろうけど。

/usr/share/gtksourceview-3.0/language-specs

プログラミングは基本や計算ばかりじゃ面白くないよね。
やはり GUI のほうが楽しいですよね。
今回は GtkSourceView でこの色分け機能を使う方法でも。

GtkLanguageManager を使います、驚くほど簡単です。
読み込むファイルを用意するのが面倒なので自分の Python コードを。

#!/usr/bin/env python3
  
import sys
from gi.repository import Gtk, GLib, Gio, GtkSource, Pango

# My src
FILENAME = __file__
  
class ColorLang(Gtk.Window):
    """
        GtkSourceLanguageManager Sample
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        #
        # GtkSourceView
        self.view = GtkSource.View()
        self.view.set_show_line_numbers(True)
        # Set Monospace Font
        font_desc = Pango.font_description_from_string("Monospace 10")
        self.view.override_font(font_desc)
        #
        # LanguageManager
        manager = GtkSource.LanguageManager.new()
        lang = manager.guess_language(FILENAME)
        """ or ContentType
        contype, result = Gio.content_type_guess(FILENAME)
        lang = manager.guess_language(None, contype)
        """
        buf = self.view.get_buffer()
        buf.set_language(lang)
        # Read
        with open(FILENAME) as f:
            s = f.read()
            buf.set_text(s)
        sw = Gtk.ScrolledWindow()
        sw.add(self.view)
        # append the filename and ContentType
        hbar = Gtk.HeaderBar()
        hbar.set_title(GLib.path_get_basename(FILENAME))
        hbar.set_subtitle(Gio.content_type_guess(FILENAME)[0])
        hbar.set_show_close_button(True)
        self.set_titlebar(hbar)
        self.add(sw)
        self.resize(400, 400)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

ColorLang()
Gtk.main()

languagemanager

ファイル名か ContentType を指定すると推測して定義ファイルから探し当てる。
ソレを set_language で突っ込む、マジでこれだけ。

でも g_content_type_guess だと ContentType は拡張子依存みたい。
本気で拡張子否依存にするなら自力で取得する必要有り、のようです。

で、まあ当然のように Gedit とまったく同じ色分けになりました。
これじゃつまんない、自分で定義したいという人は定義 XML を作って
gtk_source_language_manager_set_search_path でセットする。

GtkSourceView 3 Reference Manual: GtkSourceLanguageManager

しかし language という名ではつい gettext 関連と思ってしまいます。
いや、筆者が今までそう思い違いしていたというオチだyo!

GFileMonitor

最近 GUI をやっていない。
コマンドラインばかりじゃつまんない。

で、実用的かもしれないディレクトリ内容の変更を監視し出力するウインドウを。
いや、随分前にやって上手くいかなかったのだが原因がやっと解ったので。
GUI は C では面倒すぎなので Python で。

#!/usr/bin/env python3

"""
    $HOME を監視するサンプル
"""

from gi.repository import Gtk, Gio, GLib

class Win(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.view = Gtk.TextView()
        self.view.set_editable(False)
        sw = Gtk.ScrolledWindow()
        sw.add(self.view)
        self.add(sw)
        #
        # ファイル監視も同様、現在存在していない場合でも監視してくれる
        #f = Gio.File.new_for_path(GLib.get_home_dir() + "/test.txt")
        f = Gio.File.new_for_path(GLib.get_home_dir())
        #
        # GFileMonitor は他メソッドで使わなくても必ずアトリビュートにする
        # しないと __init__ 抜けた時点で破棄されてしまう
        #
        self.monitor = f.monitor(Gio.FileMonitorFlags.NONE)
        #self.monitor = f.monitor(Gio.FileMonitorFlags.SEND_MOVED)
        self.monitor.connect("changed", self.on_monitor)
        # self
        self.connect("delete-event", Gtk.main_quit)
        self.resize(300, 200)
        self.show_all()

    def on_monitor(self, monitor, f, other_f, event):
        """
            ディレクトリ監視は隠しファイルの変更も感知します
            ( /.bash.history 等 )
        """
        buf = self.view.get_buffer()
        s = f.get_path()
        if event == Gio.FileMonitorEvent.CHANGED:
            # ファイルの内容が変更された時
            buf.insert_at_cursor("CHANGED: {0}\n".format(s))
        elif event == Gio.FileMonitorEvent.CHANGES_DONE_HINT:
            # ファイルの情報が変更された時
            buf.insert_at_cursor("CHANGES_DONE_HINT: {0}\n".format(s))
        elif event == Gio.FileMonitorEvent.DELETED:
            # 削除(リネーム、移動含む)された時
            buf.insert_at_cursor("DERETED: {0}\n".format(s))
        elif event == Gio.FileMonitorEvent.CREATED:
            # 作成(リネーム、移動含む)された時
            buf.insert_at_cursor("CREATED: {0}\n".format(s))
        elif event == Gio.FileMonitorEvent.ATTRIBUTE_CHANGED:
            # パーミッションが変わった時
            buf.insert_at_cursor("ATTRIBUTE_CHANGED: {0}\n".format(s))
        elif event == Gio.FileMonitorEvent.PRE_UNMOUNT:
            # Gio.FileMonitorFlags.WATCH_MOUNTS 指定時のみ
            buf.insert_at_cursor("PRE_UNMOUNT: {0}\n".format(s))
        elif event == Gio.FileMonitorEvent.UNMOUNTED:
            # Gio.FileMonitorFlags.WATCH_MOUNTS 指定時のみ
            buf.insert_at_cursor("UNMOUNTED: {0}\n".format(s))
        elif event == Gio.FileMonitorEvent.MOVED:
            # Gio.FileMonitorFlags.SEND_MOVED 指定時のみ
            buf.insert_at_cursor("MOVED: {0}\n".format(s))
        # Gio.FileMonitorFlags.WATCH_MOUNTS 指定時に、あれ?
        if other_f:
            buf.insert_at_cursor("other: {0}\n".format(f.get_path()))

Win()
Gtk.main()

gfilemonitor

で、結論。
GFileMonitor のインスタンスは self のアトリビュートにする必要がある。

今迄は他メソッドから参照する必要が無いものは self にはくっつけなかった。
すると全然シグナルが飛んでこない、バグだとずっと思っていた。
しかし今日 self を付けただけであら不思議。

よく考えたらガベージコレクションだから当然である。
GUI 部品なら親ウインドウにくっついているのだから何かしら参照がある。
しかしメインループで動いているだけの部品はどこからも参照されていない。
結果ガベージコレクタの破棄対象に、何かで参照しないといけない。

PyGObject を作っている皆様、コッソリ疑っていてごめんなさい。

もしかしてアレも、と思い付くのがチラホラ。

しかしファイル監視は存在しないファイルを指定してもいいんだね。
新規作成で CREATE と CHANGES_DONE_HINT のシグナルが飛んでくる。
CHANGES_DONE_HINT はちょっとうっとうしい。

それと $HOME を監視したまま gnome-terminal を終了すると解るけど。
~/.bash_history とかの隠しファイルへの書き出しにも反応する。
コレはハンドラ側で見分け等をする、でいいのかな。

gfilemonitor2

SEND_MOVED 指定も試してみた、なるほどそうなるか。
ディレクトリ移動だと結局 DELETED になる、リネームしか意味が無い。
しかし *other_file 引数は何だろう?

それよりリネーム後のファイル名が得られないと困るんだが。
バ…いやいや、コレも手段が悪いだけなのかもしれないし。
というか、使い道が思いつかないしどうでもいいかなと。

おまけで、GDataOutputStream での上書き保存のみ。

#!/usr/bin/env python3
 
from gi.repository import Gio

f = Gio.file_new_for_path("output_stream.txt")
fstream = f.replace("", False, Gio.FileCreateFlags.NONE)
dstream = Gio.DataOutputStream.new(fstream)
dstream.put_string("BKB BKB")
fstream.close()

output_stream

OutputStream ってこんなことをやっていたの!
当然の話だが Gedit の上書き保存もまったく同じ。
いやいや、監視してみると色々面白いことが解るNE!

\033[

今日の覚書

【第十二回】エスケープシーケンス(2) – あらほりLinuxメモ

#!/bin/sh

# \033[数値A で戻りたい行数の数値
# \033[0J    でカーソル以降の行を削除

echo いくぞー

for s in いーち にー さーん だぁー!
do
    sleep 1
    printf "\033[1A\033[0J"
    echo $s
done

daa

なるほど、使い道は微妙だけど。
もっと更新してほしいな、忙しいのだろうけど。

1 ページ前の色分けはも含め C や Python からも使えるのね。
しかし cmd.exe はそもそも色分けができないだろ、なんだが。
エスケープシーケンス

Python

#!/usr/bin/env python3

print("\033[32m緑色おおおお");
print("\033[31m赤色おおおお");
# 戻す
print("\033[39m", end='');

C (glib)

#include <glib.h>

int
main (int argc, char* argv[]) {

    g_printf("\033[32m緑色おおおお\n");
    g_printf("\033[31m赤色おおおお\n");
    // 戻す
    g_printf("\033[39m");
    return 0;
}

033

コッチは警告表示で赤で出力とか便利そう。
sh で探して C, Python も使えたので得した気分。

Bash Increment

シェルスクリプトの変数って ++ でインクリメントできたんですね。
Bash – シェルのループ処理内でのインクリメント – Qiita
これも凄いがコメント(一番下)がもっと凄い。

while (( count < 10000 ));

こんなのまでできたのか、マジで速いし。

試すと (( count– )) で普通にデクリメントするね。
Ramen Timer for Ubuntu sh | PaePoi
これを使えば seq を逆順ソートとかしていたラーメンタイマーを

#!/bin/sh
 
# カップラーメン用にどうぞ
 
read -p 完成までの残り時間を秒で指定: count
echo 止めるには Ctrl+C
 
while (( count > 0 )); do
    printf "残り%02g分%02g秒  \r" $(( count / 60 )) $(( count % 60 ))
    (( count-- ))
    sleep 1
done
echo できあがりぃ?

inc

速いうえに圧倒的に可読性の良いコードになりました。
C と似た感じにするだけで随分と違うものだ。
分と秒を分けるのはコレでいいです。

Fedora の sh (Bash) のみでの動作確認ですが。
シェルが違う人は bash 指定に書き換えしてね。

しかし覚書を qiita に書き込んでいる人達はなんだろう?
それよりこういう有用なコードを詰め込んだサイトを作ってほしいな。
覚書色々 – L’Isola di Niente
自力でチビチビ作っているんだけどまだ少ないよな。。。