試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

GtkSourceViewのビューとバッファに関する色々な設定に関して(例)

GtkSourceViewのビューとバッファに関する色々な設定に関して(メモ・前半)」「GtkSourceViewのビューとバッファに関する色々な設定に関して(メモ・後半)」の内容を踏まえた例を貼り付ける。

[任意]ファイル名: gtksourceviewtest3.py

#! /usr/bin/python
# -*- coding: utf-8 -*-

import sys
try:
  import pygtk
  pygtk.require('2.0')
except:
  pass
try:
  import pango
  import gtk
except:
  print >> sys.stderr, 'Error: PyGTK is not installed'
  sys.exit(1)
try:
  import gtksourceview2
except:
  print >> sys.stderr, 'Error: PyGtkSourceView is not installed'
  sys.exit(1)


class LabelWithMnemonic(gtk.Label):
  """
  ニーモニック・ウィジェットを初期化時に指定するラベル
  """
  def __init__(self, str, widget):
    gtk.Label.__init__(self, str)
    self.props.use_underline = True
    self.props.mnemonic_widget = widget

class MainWindow(gtk.Window):
  """
  メインウィンドウ
  """
  def __init__(self, *args, **kwargs):
    gtk.Window.__init__(self, *args, **kwargs)
    # 既定のフォント
    self.__default_font = 'Monospace 8'
    # ショートカットキー(アクセラレータ)
    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)
    # 高度な設定向けGUI部品
    self.__check_auto_indent = gtk.CheckButton('Auto _indent')
    self.__check_highlight_current_line = gtk.CheckButton('_Highlight current line')
    self.__check_indent_on_tab = gtk.CheckButton('Indent on _tab')
    self.__check_insert_spaces_instead_of_tabs = gtk.CheckButton('Insert _spaces instead of tabs')
    self.__check_show_line_numbers = gtk.CheckButton('Show line _numbers')
    self.__check_show_line_marks = gtk.CheckButton('Show line ma_rks')
    self.__check_show_right_margin = gtk.CheckButton('Show right _margin')
    self.__check_show_space = gtk.CheckButton('Show s_pace')
    self.__check_show_tab = gtk.CheckButton('Show t_ab')
    self.__check_show_newline = gtk.CheckButton('Show n_ewline')
    self.__check_highlight_matching_brackets = gtk.CheckButton('Highlight matching _brackets')
    self.__check_indent_width_use_tab_width = gtk.CheckButton('_Use tab width')
    self.__check_indent_width_use_tab_width.props.active = True
    self.__check_highlight_matching_brackets.props.active = True
    self.__spbtn_right_margin = gtk.SpinButton(gtk.Adjustment(lower=1, upper=200, step_incr=1, page_incr=2))
    self.__spbtn_indent_width = gtk.SpinButton(gtk.Adjustment(value=2, lower=1, upper=32, step_incr=1, page_incr=2))  # 既定値ではないがユーザ設定の初期値として「value=2」
    self.__spbtn_indent_width.props.sensitive = False  # 既定では-1(手動設定なし)
    self.__spbtn_tab_width = gtk.SpinButton(gtk.Adjustment(lower=1, upper=32, step_incr=1, page_incr=2))
    self.__spbtn_max_undo_levels = gtk.SpinButton(gtk.Adjustment(value=1000, lower=-1, upper=99999, step_incr=1, page_incr=2))
    self.__label_indent_width = LabelWithMnemonic('In_dent width:', self.__spbtn_indent_width)
    self.__label_tab_width = LabelWithMnemonic('Tab _width:', self.__spbtn_tab_width)
    self.__label_max_undo_levels = LabelWithMnemonic('Ma_x undo levels:', self.__spbtn_max_undo_levels)
    # ソースビュー関係
    # 言語マネージャとスタイルスキームマネージャ
    # 言語マネージャオブジェクトには言語の判別をするguess_language()もある
    self.__lm = gtksourceview2.language_manager_get_default()
    self.__ssm = gtksourceview2.style_scheme_manager_get_default()
    # 言語一覧
    # Pythonを初期値にするためにその番号を調べる
    index_python = -1; i = 0
    for lang_id in self.__lm.props.language_ids:
      if lang_id == 'python':
        index_python = i
      i += 1
    # tangoを初期値にするためにその番号を調べる
    index_tango = -1; i = 0
    for scheme_id in self.__ssm.props.scheme_ids:
      if scheme_id == 'tango':
        index_tango = i
      i += 1
    self.__combo_lang = gtk.combo_box_new_text()
    for lang_id in self.__lm.props.language_ids:
      self.__combo_lang.append_text(lang_id)
    self.__combo_lang.props.active = index_python
    self.__combo_scheme = gtk.combo_box_new_text()
    for scheme_id in self.__ssm.props.scheme_ids:
      self.__combo_scheme.append_text(scheme_id)
    self.__combo_scheme.props.active = index_tango
    self.__combo_home_end = gtk.combo_box_new_text()
    # Home/Endの項目
    for item in ['DISABLED', 'BEFORE', 'AFTER', 'ALWAYS']:
      self.__combo_home_end.append_text(item)
    self.__combo_home_end.props.active = 0  # DISABLED
    self.__label_lang = LabelWithMnemonic('_Language:', self.__combo_lang)
    self.__label_scheme = LabelWithMnemonic('S_cheme:', self.__combo_scheme)
    self.__fontbtn = gtk.FontButton(self.__default_font)
    self.__label_font = LabelWithMnemonic('F_ont:', self.__fontbtn)
    self.__label_home_end = LabelWithMnemonic('Smart Home/En_d:', self.__combo_home_end)
    self.__sourcebuf = gtksourceview2.Buffer(language=self.__lm.get_language(self.__combo_lang.get_active_text()))
    self.__sourcebuf.set_style_scheme(self.__ssm.get_scheme(self.__combo_scheme.get_active_text()))
    f_in = open(__file__, 'r')
    # begin_not_undoable_action()からnot_undoable_action()までの間の操作は
    # 取り消し(undo)可能なものとして記録されないようになる
    self.__sourcebuf.begin_not_undoable_action()
    self.__sourcebuf.props.text = f_in.read()
    self.__sourcebuf.end_not_undoable_action()
    f_in.close()
    self.__sourceview = gtksourceview2.View(self.__sourcebuf)
    self.__sourceview.props.auto_indent = self.__check_auto_indent.props.active = True
    self.__sourceview.props.highlight_current_line = self.__check_highlight_current_line.props.active = True
    self.__sourceview.props.show_line_numbers = self.__check_show_line_numbers.props.active = True
    self.__sourceview.props.show_right_margin = self.__check_show_right_margin.props.active = True
    self.__sourceview.props.indent_on_tab = self.__check_indent_on_tab.props.active = True
    self.__sourceview.props.insert_spaces_instead_of_tabs = self.__check_insert_spaces_instead_of_tabs.props.active = True
    self.__sourceview.props.right_margin_position = self.__spbtn_right_margin.props.value = 80
    self.__sourceview.props.indent_width = -1
    self.__sourceview.props.tab_width = self.__spbtn_tab_width.props.value = 8
    self.__sourceview.props.wrap_mode = gtk.WRAP_CHAR
    self.__sourceview.modify_font(pango.FontDescription(self.__default_font))
    self.__sw = gtk.ScrolledWindow()
    self.__sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    self.__sw.add(self.__sourceview)
    # ツールバー
    self.__toolbtn_undo = gtk.ToolButton(gtk.STOCK_UNDO)
    self.__toolbtn_undo.props.sensitive = False
    self.__toolbtn_undo.props.tooltip_text = 'undo'
    self.__toolbtn_redo = gtk.ToolButton(gtk.STOCK_REDO)
    self.__toolbtn_redo.props.sensitive = False
    self.__toolbtn_redo.props.tooltip_text = 'redo'
    self.__toolbar = gtk.Toolbar()
    self.__toolbar.insert(self.__toolbtn_undo, 0)
    self.__toolbar.insert(self.__toolbtn_redo, 1)
    # レイアウト用コンテナ
    self.__hbox = gtk.HBox()
    self.__hbox.pack_start(self.__label_lang, expand=False, fill=False)
    self.__hbox.pack_start(self.__combo_lang)
    self.__hbox.pack_start(self.__label_scheme, expand=False, fill=False)
    self.__hbox.pack_start(self.__combo_scheme)
    self.__hbox.pack_start(self.__label_font, expand=False, fill=False)
    self.__hbox.pack_end(self.__fontbtn, expand=False, fill=False)
    self.__hbox_right_margin = gtk.HBox()
    self.__hbox_right_margin.pack_start(self.__check_show_right_margin, expand=False, fill=False)
    self.__hbox_right_margin.pack_start(self.__spbtn_right_margin, expand=False, fill=False)
    self.__hbox_width = gtk.HBox()
    self.__hbox_width.pack_start(self.__label_indent_width, expand=False, fill=False)
    self.__hbox_width.pack_start(self.__spbtn_indent_width, expand=False, fill=False)
    self.__hbox_width.pack_start(self.__check_indent_width_use_tab_width, expand=False, fill=False)
    self.__hbox_width.pack_start(self.__label_tab_width, expand=False, fill=False)
    self.__hbox_width.pack_start(self.__spbtn_tab_width, expand=False, fill=False)
    self.__hbox_width.pack_start(self.__label_max_undo_levels, expand=False, fill=False)
    self.__hbox_width.pack_start(self.__spbtn_max_undo_levels, expand=False, fill=False)
    self.__hbox_home_end = gtk.HBox()
    self.__hbox_home_end.pack_start(self.__label_home_end, expand=False, fill=False)
    self.__hbox_home_end.pack_start(self.__combo_home_end, expand=False, fill=False)
    self.__vbox_advanced_left = gtk.VBox()
    self.__vbox_advanced_left.pack_start(self.__check_auto_indent, expand=False, fill=False)
    self.__vbox_advanced_left.pack_start(self.__check_highlight_current_line, expand=False, fill=False)
    self.__vbox_advanced_left.pack_start(self.__check_indent_on_tab, expand=False, fill=False)
    self.__vbox_advanced_left.pack_start(self.__check_insert_spaces_instead_of_tabs, expand=False, fill=False)
    self.__vbox_advanced_left.pack_start(self.__check_show_line_numbers, expand=False, fill=False)
    self.__vbox_advanced_left.pack_start(self.__check_show_line_marks, expand=False, fill=False)
    self.__vbox_advanced_left.pack_start(self.__hbox_right_margin, expand=False, fill=False)
    self.__vbox_advanced_right = gtk.VBox()
    self.__vbox_advanced_right.pack_start(self.__check_show_space, expand=False, fill=False)
    self.__vbox_advanced_right.pack_start(self.__check_show_tab, expand=False, fill=False)
    self.__vbox_advanced_right.pack_start(self.__check_show_newline, expand=False, fill=False)
    self.__vbox_advanced_right.pack_start(self.__check_highlight_matching_brackets, expand=False, fill=False)
    self.__vbox_advanced_right.pack_start(self.__hbox_home_end, expand=False, fill=False)
    self.__hbox_advanced = gtk.HBox()
    self.__hbox_advanced.pack_start(self.__vbox_advanced_left)
    self.__hbox_advanced.pack_start(self.__vbox_advanced_right)
    self.__vbox_advanced = gtk.VBox()
    self.__vbox_advanced.pack_start(self.__hbox_advanced)
    self.__vbox_advanced.pack_start(self.__hbox_width)
    self.__expander = gtk.Expander('Ad_vanced settings')
    self.__expander.props.use_underline = True
    self.__expander.add(self.__vbox_advanced)
    self.__vbox = gtk.VBox()
    self.__vbox.pack_start(self.__menubar, expand=False, fill=False)
    self.__vbox.pack_start(self.__toolbar, expand=False, fill=False)
    self.__vbox.pack_start(self.__hbox, expand=False, fill=False)
    self.__vbox.pack_start(self.__expander, expand=False, fill=False)
    self.__vbox.pack_start(self.__sw)
    # シグナル
    self.connect('delete_event', gtk.main_quit)
    self.__item_quit.connect('activate', gtk.main_quit)
    # undo/redo可否のツールバーボタンの状態は
    # can-undo/can-redoのGObjectプロパティの変更の通知を受け取って変更する
    self.__sourcebuf.connect('notify::can-undo', lambda gobj, property: self.__toolbtn_undo.set_sensitive(self.__sourcebuf.props.can_undo))
    self.__sourcebuf.connect('notify::can-redo', lambda gobj, property: self.__toolbtn_redo.set_sensitive(self.__sourcebuf.props.can_redo))
    # undo/redoのボタンが押されたときの処理
    self.__toolbtn_undo.connect('clicked', lambda widget: self.__sourcebuf.undo())
    self.__toolbtn_redo.connect('clicked', lambda widget: self.__sourcebuf.redo())
    # 高度な設定の適用処理
    self.__check_auto_indent.connect('toggled', lambda widget: self.__sourceview.set_auto_indent(widget.props.active))
    self.__check_highlight_current_line.connect('toggled', lambda widget: self.__sourceview.set_highlight_current_line(widget.props.active))
    self.__check_indent_on_tab.connect('toggled', lambda widget: self.__sourceview.set_indent_on_tab(widget.props.active))
    self.__check_insert_spaces_instead_of_tabs.connect('toggled', lambda widget: self.__sourceview.set_insert_spaces_instead_of_tabs(widget.props.active))
    self.__check_show_line_numbers.connect('toggled', lambda widget: self.__sourceview.set_show_line_numbers(widget.props.active))
    self.__check_show_line_marks.connect('toggled', lambda widget: self.__sourceview.set_show_line_marks(widget.props.active))
    self.__check_show_right_margin.connect('toggled', lambda widget: self.__sourceview.set_show_right_margin(widget.props.active))
    self.__check_show_space.connect('toggled', self.__on_check_draw_spaces_toggled)
    self.__check_show_tab.connect('toggled', self.__on_check_draw_spaces_toggled)
    self.__check_show_newline.connect('toggled', self.__on_check_draw_spaces_toggled)
    self.__check_indent_width_use_tab_width.connect('toggled', self.__on_check_indent_width_use_tab_width_toggled)
    self.__check_highlight_matching_brackets.connect('toggled', lambda widget: self.__sourcebuf.set_highlight_matching_brackets(widget.props.active))
    self.__spbtn_right_margin.connect('value-changed', lambda widget: self.__sourceview.set_right_margin_position(int(widget.props.value)))
    self.__spbtn_indent_width.connect('value-changed', lambda widget: self.__sourceview.set_indent_width(int(widget.props.value)))
    self.__spbtn_tab_width.connect('value-changed', lambda widget: self.__sourceview.set_tab_width(int(widget.props.value)))
    self.__spbtn_max_undo_levels.connect('value-changed', lambda widget: self.__sourcebuf.set_max_undo_levels(int(widget.props.value)))
    # 言語の切り替えは
    # バッファオブジェクトのset_language()に言語オブジェクトを入れることで行うか
    # GObjectプロパティlanguageへ言語オブジェクトを代入する
    # 言語オブジェクトは言語マネージャからget_language()に文字列名を入れて得る
    self.__combo_lang.connect('changed', lambda widget: self.__sourcebuf.set_language(self.__lm.get_language(widget.get_active_text())))
    # スキームの切り替えもスキームマネージャオブジェクトを用いて
    # 似たような流れで行う
    self.__combo_scheme.connect('changed', lambda widget: self.__sourcebuf.set_style_scheme(self.__ssm.get_scheme(widget.get_active_text())))
    # Home/End
    self.__combo_home_end.connect('changed', lambda widget: self.__sourceview.set_smart_home_end([gtksourceview2.SMART_HOME_END_DISABLED, gtksourceview2.SMART_HOME_END_BEFORE, gtksourceview2.SMART_HOME_END_AFTER, gtksourceview2.SMART_HOME_END_ALWAYS][widget.props.active]))
    self.__fontbtn.connect('font-set', lambda widget: self.__sourceview.modify_font(pango.FontDescription(widget.props.font_name)))
    # ウィンドウ
    self.add(self.__vbox)
    self.set_size_request(500, 300)
    self.resize(600, 400)
    self.props.title = 'Python GtkSourceView Test 3'
  def __on_check_draw_spaces_toggled(self, widget):
    """
    スペース描画に関するチェックが操作された
    """
    flags = 0
    if self.__check_show_space.props.active:
      flags |= gtksourceview2.DRAW_SPACES_SPACE
    if self.__check_show_tab.props.active:
      flags |= gtksourceview2.DRAW_SPACES_TAB
    if self.__check_show_newline.props.active:
      flags |= gtksourceview2.DRAW_SPACES_NEWLINE
    self.__sourceview.set_draw_spaces(flags)
    # この他、gtksourceview2.DRAW_SPACES_ALLで全てを表示するようにもできる
  def __on_check_indent_width_use_tab_width_toggled(self, widget):
    """
    「インデント幅にタブ幅を用いる」チェックが操作された
    """
    if widget.props.active:
      self.__spbtn_indent_width.set_sensitive(False)
      # GObjectプロパティindent-widthに-1を入れると
      # タブ幅(GObjectプロパティtab-width)の値が用いられる
      self.__sourceview.props.indent_width = -1
    else:
      self.__spbtn_indent_width.set_sensitive(True)
      # スピンボタンから値を読み込んで適用
      self.__sourceview.props.indent_width = self.__spbtn_indent_width.props.value

class PyGtkSourceViewTest3:
  """
  GtkSourceViewのテスト3
  """
  def main(self):
    """
    アプリケーションのメイン処理
    """
    win = MainWindow()
    win.show_all()
    gtk.main()


if __name__ == '__main__':
  app = PyGtkSourceViewTest3()
  app.main()


高度な設定を展開すると下のようになる。メモで扱った細かい設定はこの中のものとなる。ただし、「Line marks」についてはデータを用意していないため、場所は確保されるが何も表示はされない。

関連記事:

使用したバージョン:

  • Python 2.6.4
  • PyGTK 2.16.0
  • PyGtkSourceView 2.8.0