pygtkで遊ぼう(20) gtksourceview.SourceView上でスクリプトを実行し、結果をクリップボードへ。

今制作中のエディタにスクリプトを実行させる機能を付けてみました。
実行結果をどうやって表示させようかなぁ。。と少し悩んだのですが、とりあえずクリップボードへ書き込むことにしました。


SourceViewのサブクラスに以下のようなメソッドを追加しました。
以下のメソッドの処理概要は、

(1) SourceView上の選択中のテキストを取得する。
(2) 標準出力(sys.stdout)の状態を退避後、StringIOオブジェクトにリダイレクト。
(3) 選択中のテキストをPythonのexec関数で実行。
(4) StringIOオブジェクトの内容をクリップボードに書き込む。
(5) 標準出力を退避しておいたものにもどし、StriongIOオブジェクトを破棄する。

といった感じです。

標準出力をリダイレクトしているので、選択されているテキスト中にprint文などがあると、
その実行結果がクリップボードに書き込まれるというスンポウです。

    def do_exec_current_selection(self):
        '''
        シグナル: exec-current-selection発生時に実行される処理。
        現在選択されている文字列をPythonとして評価する。
        選択されている文字列がない場合には何もしない。

        print文などの標準出力への出力は、全てクリップボードに
        テキストとして書き込まれるので、評価した結果を使用するには
        ペーストを行えばよい。
        '''
        bf = self.get_buffer()
        if bf == None:
            return

        # 選択中のテキスト領域を取得。
        # テキストが未選択の場合には処理を中断。
        sel = bf.get_selection_bounds()
        if len(sel) == 0:
            return

        # sel[0] : startIter
        # sel[1] : endIter
        text = bf.get_text(sel[0], sel[1])

        # 標準出力先を一時的にバッファに変更
        saveStdOut = sys.stdout
        strio = StringIO()
        sys.stdout = strio

        # 以下のTry, except文中(exec用のテキスト中も同様)で標準出力への
        # 書き込み(print文など)が行われると、一時的なバッファに書き出される
        # ため、stdoutへは書き出されない。また実行完了時には、一時的な
        # バッファの内容はクリップボードに移される。
        try:
            exec(text)
            clipboard = self.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD)
            if clipboard:
                clipboard.set_text(strio.getvalue())
        except Exception, err:
            dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
                                    buttons=gtk.BUTTONS_OK,
                                    message_format=str(err))
            response = dlg.run()
            dlg.destroy()

        # 標準出力先をもとにもどし、一時的なバッファも破棄する。
        sys.stdout = saveStdOut
        strio.close()

なかなかいい勉強になりました。