PyGTKでクリップボード上のテキストの内容を扱う
ここではPyGTKを用いたクリップボード上のテキストの処理についてを扱う。また、テキストビューでクリップボードのテキストを処理するための便利な方法についても扱う。
クリップボードを管理するオブジェクト
gtk.Clipboardオブジェクトを用いることにより、クリップボードのデータを簡単にやりとりできるようになっている。文字列以外にも画像とリッチテキストといったものも扱えるが、それぞれの種類ごとに処理ができる。
テキストを扱う場合には
の2つを主に使用する。request_text()はwait_for_text()に近いが値をクリップボードに要求した後、値が得られると別のハンドラを呼ぶ形となる。
記憶の種類について
「mlterm,Emacsと外部アプリケーション間のコピー/貼り付けについて」やまとめドキュメントなどでも書いているが、X Window System上のクリップボード的な「記憶」には幾つかの種類があり、その中でも
- PRIMARY selection(選択/ドラッグ範囲・マウス中クリックで貼り付け)
- CLIPBOARD(多くのGUIアプリケーションがコピペ作業を行うのに使用される)
がよく使用される。gtk.Clipboardオブジェクトの既定の指定ではCLIPBOARDが使用されるが
clipboard = gtk.Clipboard(selection='PRIMARY')
あるいは
clipboard = gtk.clipboard_get(selection='PRIMARY')
のように引数selectionを「PRIMARY」に指定することにより、PRIMARY用のクリップボードオブジェクトを生成することもできる。
クリップボードの内容に関する変更の監視について
クリップボードの内容が変更されるとシグナル「owner_change」を受け取るため、このシグナルとハンドラを関連付けることにより、クリップボードの監視が簡単に行える。
テキストビューのバッファへの操作
テキストビューに表示されるバッファであるgtk.TextBufferオブジェクトには
- cut_clipboard()
- copy_clipboard()
- paste_clipboard()
といったメンバ関数があり、クリップボードオブジェクトを渡してこれらの操作を行うことが簡単に行える。これらはテキストビュー上の選択範囲に対して操作を行う。
貼り付け(paste_clipboard())は、選択範囲がなくてもテキストビュー上のカーソル位置に対して行われる。
例
テキストビューの横に切り取り/コピー/貼り付け/クリアのボタンが並び、テキストビュー上の選択範囲に対してそれぞれの操作が行える。
クリップボードの内容が更新されると端末に「owner_changed」と表示される。
#! /usr/bin/python # -*- coding: utf-8 -*- import sys try: import pygtk pygtk.require('2.0') except: pass try: import gtk except: print >> sys.stderr, 'Error: PyGTK is not installed' sys.exit(1) if gtk.pygtk_version < (2,2,0): print >> sys.stderr, 'PyGTK >= 2.2.0 required' sys.exit(1) class MainWindow(gtk.Window): """ メインウィンドウ """ def __init__(self, *args, **kwargs): gtk.Window.__init__(self, *args, **kwargs) # ショートカットキー(アクセラレータ) self.__accelgroup = gtk.AccelGroup() self.add_accel_group(self.__accelgroup) # メニュー項目 self.__item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.__accelgroup) self.__menu_file = gtk.Menu() self.__menu_file.add(self.__item_quit) self.__item_file = gtk.MenuItem('_File') self.__item_file.props.submenu = self.__menu_file self.__menubar = gtk.MenuBar() self.__menubar.append(self.__item_file) # テキストビュー self.__textbuf = gtk.TextBuffer() self.__textview = gtk.TextView(self.__textbuf) self.__textview.props.wrap_mode = gtk.WRAP_CHAR # テキストビュー向けスクロールウィンドウ self.__sw = gtk.ScrolledWindow() self.__sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) self.__sw.add(self.__textview) # ボタン self.__button_cut = gtk.Button(stock=gtk.STOCK_CUT) self.__button_copy = gtk.Button(stock=gtk.STOCK_COPY) self.__button_paste = gtk.Button(stock=gtk.STOCK_PASTE) self.__button_clear = gtk.Button(stock=gtk.STOCK_CLEAR) # レイアウト用コンテナ self.__vbox_btn = gtk.VBox() # ボタンを縦に並べる self.__vbox_btn.pack_start(self.__button_cut, expand=False, fill=False) self.__vbox_btn.pack_start(self.__button_copy, expand=False, fill=False) self.__vbox_btn.pack_start(self.__button_paste, expand=False, fill=False) self.__vbox_btn.pack_start(self.__button_clear, expand=False, fill=False) self.__hbox = gtk.HBox() # テキストビューとボタン群 self.__hbox.pack_start(self.__sw) self.__hbox.pack_start(self.__vbox_btn, expand=False, fill=False) self.__vbox = gtk.VBox() # 全体 self.__vbox.pack_start(self.__menubar, expand=False, fill=False) self.__vbox.pack_start(self.__hbox) # クリップボード # 既定では「CLIPBOARD」が使用されるが # 「selection='PRIMARY'」で選択範囲を扱うこともできる # 一部の端末エミュレータやエディタなどはPRIMARYのみを使用 # X11上のEmacsは両対応 # * clipboard-kill-regionでCLIPBOARD切り取り # * clipboard-kill-ring-saveでCLIPBOARDコピー # * clipboard-yankでCLIPBOARD貼り付け # これらの「clipboard-」を除いた名前のバージョンではPRIMARYを使用 self.__clipboard = gtk.Clipboard() # self.__clipboard = gtk.Clipboard(selection='PRIMARY') # シグナル self.connect('delete_event', gtk.main_quit) self.__item_quit.connect('activate', gtk.main_quit) self.__button_cut.connect('clicked', self.__on_button_cut_clicked) self.__button_copy.connect('clicked', self.__on_button_copy_clicked) self.__button_paste.connect('clicked', self.__on_button_paste_clicked) self.__button_clear.connect('clicked', self.__on_button_clear_clicked) self.__clipboard.connect('owner-change', self.__on_clipboard_owner_change) # ウィンドウ self.add(self.__vbox) self.set_size_request(350, 300) def __on_button_cut_clicked(self, widget): """ 切り取りボタンが押されたら テキストビューの内容をクリップボードにコピーしてからクリア """ # 方法1 # テキストバッファではcut_clipboard()が便利 self.__textbuf.cut_clipboard(self.__clipboard, True) # 方法2 # 手動で切り取る場合 # selection = self.__textbuf.get_selection_bounds() # if selection: # self.__clipboard.set_text(self.__textbuf.get_text(selection[0], selection[1])) # self.__textbuf.delete_selection(False, True) def __on_button_copy_clicked(self, widget): """ コピーボタンが押されたら テキストビューの内容をクリップボードにコピー """ # 方法1 # テキストバッファではcopy_clipboard()が便利 self.__textbuf.copy_clipboard(self.__clipboard) # 方法2 # 手動でコピーする場合 # selection = self.__textbuf.get_selection_bounds() # if selection: # self.__clipboard.set_text(self.__textbuf.get_text(selection[0], selection[1])) def __on_button_paste_clicked(self, widget): """ 貼り付けボタンが押されたら クリップボードにテキストデータを要求 """ # 方法1 # テキストバッファではpaste_clipboard()が便利 self.__textbuf.paste_clipboard(self.__clipboard, None, True); # 方法2 # request_text()はデータを受け取ると別の関数を呼ぶが # wait_for_text()は直接値を取れる(受け取るまでの間はメインループ内で待つ) # self.__clipboard.request_text(self.__on_clipboard_text_received, None) # 方法3 # wait_for_text()は戻り値として内容が得られる # text = self.__clipboard.wait_for_text() # if text: # Noneの場合がある # self.__textbuf.insert_at_cursor(text) def __on_button_clear_clicked(self, widget): """ クリアボタンが押されたら テキストビューの内容をクリア """ self.__textbuf.props.text = '' def __on_clipboard_text_received(self, clipboard, text, data): """ クリップボードからテキストを取得したらテキストビューに表示する """ if text: self.__textbuf.insert_at_cursor(text) def __on_clipboard_owner_change(self, clipboard, event): """ クリップボードの内容が変わったときの処理 """ # クリップボードに何かをコピーすると下のメッセージが端末に表示される print 'owner_changed' # ここでrequest_text()やwait_for_text()を呼ぶと # クリップボードマネージャのように履歴を管理することもできる class PyGTKClipboardTest: """ クリップボードのテスト """ def main(self): """ アプリケーションのメイン処理 """ win = MainWindow() win.show_all() gtk.main() if __name__ == '__main__': app = PyGTKClipboardTest() app.main()
関連記事:
参考URL:
使用したバージョン:
- Python 2.6.4
- PyGTK 2.16.0