モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

GtkSourceViewを表示専用にする

GtkSourceViewを表示専用にする、その方法が分からなかったのでメモ

Gtk+3入門

Gtk+3入門

はいアフィ入りました〜w

というか他のGTK関連書籍は正直10年いや20年前のもので役に立たないと思われたからです。ていうか昔は盛んだったんですなぁ(´・ω・`)

まぁそれはともかく、このアフィリンクの書籍とは一切関係ない話なのですが

以下は普通の知能を持っていれば当たり前の話なのですが、GtkSourceViewに対して変更を禁止しようと思ったのですが 自分はgtksourceview read-onlyなどで延々とググり続けてしまった罠。 なにかset_read_onlyみたいなメソッドがあるのかな〜と…無いですね。*1

そしてその方法について誰も言及していない。何故か。その理由はあまりにも基本的だからだと推察します。

つまり…単にGtkウィジェットとしてキー入力を無効化してしまえばいいだけではないか?と思いついたからです。

しかし単純にset_sensitive()でFalseにするとナビゲーションすら不能になるという。 そこで、キーボードイベントをconnectしてナビゲーションキー以外はTrueを返してバブリングを抑制。キー入力を遮断してしまうという方法を取りました。

マウスボタンも右ボタンを許すとメニューが飛び出てしまうので潰しておきます。 ただしこれだとテキスト選択してコピーが出来ないので(いや別に出来なくてもいいけど)何か自前の「コピー」だけあるメニューでも表示するとかしたほうが良いかもですね。

他にスマートな方法があるのかも知れませんが、こんな単純なことを思いつきました的な話でした。

サンプルコード

面倒くさいので、GtkSourceViewテスト用コードの全体をコピペ!

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '3.0')
from gi.repository import Gtk, Gdk, GtkSource, GObject, GLib, Gio

# For reference of GtkSourceView(but 4.0):
# https://lazka.github.io/pgi-docs/GtkSource-4/classes.html

class Myclass:

    allowed_keys = (
        Gdk.KEY_Up,
        Gdk.KEY_Left,
        Gdk.KEY_Down,
        Gdk.KEY_Right,
        Gdk.KEY_Page_Up,
        Gdk.KEY_Page_Down,
        Gdk.KEY_Home,
        Gdk.KEY_End,
    )

    def __init__(self):
        self.win = Gtk.Window(Gtk.WindowType.TOPLEVEL)
        self.win.set_title("title")
        self.win.set_resizable(True)
        self.win.show()
        self.win.connect("destroy",self.destroy)
        self.win.resize(1024,768)

        # widgets baseically added this self.basebox
        basebox = Gtk.VBox(False,8)
        basebox.show()
        self.win.add(basebox)
        self.basebox = basebox

        # Building:
        # You need `from gi.repository import GtkSource`, `GLib` and `Gio`

        # Coding:
        # Buffer : You need buffer for actual text contents.
        #          The source-view is only `view`, actually you must use
        #          buffer to load or set file type or something like that.
        #          it can be get from source.get_buffer() (empty one)
        #          or, create from GtkSource.Buffer().
        #
        # LanguageManager : To specify the `programming language` for `buffer`.
        #
        # FileLoader: To load file into buffer. Need GLib module to set priority
        #             when use async load.
        #             Also Gio for File (filename) object.
        #
        source = GtkSource.View()
        self.buffer = source.get_buffer()
        # Use Language Manager, to set language (i.e. file type) to buffer.
        self.lm = GtkSource.LanguageManager()
        self.buffer.set_language(self.lm.get_language('python'))
        
        # Set readonly 
        # To make view read-only, prohibit all key inputs except for navigation keys.
        source.connect("key-press-event", self.view_key_handler)
        source.connect("key-release-event", self.view_key_handler)
        source.connect("button-press-event", self.view_button_handler)
        source.connect("button-release-event", self.view_button_handler)

        # Load text contents

        # FileLoader does not accept string filename. use GtkSource.File object.
        filename = GtkSource.File()
        filename.set_location(Gio.File.new_for_path('srcviewtest.py')) # This Needs Gio module.

        fileloader= GtkSource.FileLoader.new(self.buffer, filename) # Not use constructor, use this class method. 
        fileloader.load_async(GLib.PRIORITY_LOW, None, None, None, None, None)

        source.set_show_line_numbers(True)
        basebox.add(source)
        source.show()
        self.source = source

    def view_key_handler(self,widget,data):
        if not data.keyval in self.allowed_keys:
            return True # Prohibit bubbling

    def view_button_handler(self, widget, event):
        if event.button != 1:
            return True # Prohibit bubbling

    def destroy(self, widget, data=None):
        Gtk.main_quit()

    def main(self):
        Gtk.main()

if __name__ == '__main__':
    m=Myclass()
    m.main()

*1:もしあるのなら教えて欲しいガチで…割と困っています