Evince/fwdevince/Python
の編集
https://texwiki.texjp.org/?Evince%2Ffwdevince%2FPython
[
トップ
] [
編集
|
差分
|
バックアップ
|
添付
|
リロード
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
[[Evince]] > [[fwdevince>Evince/fwdevince]] > Python *fwdevince の例示ソース [#u6122dc4] // ずっと疑問なんですが,なんで TeX Wiki に展示したがるんでしょうね. // Github にでも置いてリンクすればいいと思うのですが. // その方がバージョン管理も楽だし,コピペしないでダウンロードするだけでいいし. -- kmaeda (2016/01/09) fwdevince(Evince で forward and inverse search を行うツール)の Python による例示ソースです. [[Fedora>Linux/Fedora]] 26 の Evince 3.24.0, Python 3.6.2, dbus-python 1.2.4, PyGObject 3.24.1 で動作確認しています. なお https://www.freedesktop.org/wiki/Software/DBusBindings/ によると dbus-python は Obsolete libraries で New applications should use pydbus, txdbus or GDBus/QtDBus bindings. とのことです. うまく動作しない場合は -コマンドラインから python3 --version を実行して Python のバージョンを確認して Python 3.5 以降でなければ Python 3.5 以降にアップグレードする -[[Okular]], [[zathura]], [[qpdfview]] などの Evince 以外の SyncTeX 対応ビューアで forward and inverse search を実行する -[[TeXstudio]], [[Texmaker]], [[TeXworks]] の組み込みビューアで forward and inverse search を実行する -fwdevince をうまく動作するように修正する //--例えば端末から $ python -V とした時に Python 2.7.6 と表示される環境だと urllib.parse の読み込みに失敗します.その場合は python3 をインストールし,先頭行の #!/usr/bin/env python を #!/usr/bin/env python3 などと変更する. などを試みてください. // オリジナルが GPL なので,ライセンス表示を消してはならない. // 最初に書いた通り置き場所も検討されるべきだが,とりあえず. // -- kmaeda (2016/05/08) ---- -fwdevince ---- #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2010 Jose Aliste # 2011 Benjamin Kellermann # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public Licence as published by the Free Software # Foundation; either version 2 of the Licence, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public Licence for more # details. # # You should have received a copy of the GNU General Public Licence along with # this program; if not, write to the Free Software Foundation, Inc., 51 Franklin # Street, Fifth Floor, Boston, MA 02110-1301, USA from gi.repository import GLib, Gio import dbus import argparse import pathlib import traceback import urllib.parse class EvinceForwardSearch: def parse_args(self): parser = argparse.ArgumentParser(description='Forward search with Evince') parser.add_argument('pdf', nargs=1, help='PDF file') parser.add_argument('line', nargs=1, type=int, help='Line') parser.add_argument('tex', nargs=1, help='TeX file') return parser.parse_args() def run(self): args = self.parse_args() pdf = str(pathlib.Path(args.pdf[0]).resolve()).replace(" ", "%20") line = int(args.line[0]) tex = str(pathlib.Path(args.tex[0]).resolve().parent / pathlib.Path('./') / pathlib.Path(args.tex[0]).resolve().name) try: bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) daemon = Gio.DBusProxy.new_sync(bus, 0, None, 'org.gnome.evince.Daemon', '/org/gnome/evince/Daemon', 'org.gnome.evince.Daemon', None) dbus_name = daemon.call_sync('FindDocument', GLib.Variant('(sb)', ('file://' + urllib.parse.quote(pdf, safe="%/:=&?~#+!$,;'@()*[]"), True)), Gio.DBusSignalFlags.NONE, -1, None) dbus_name = dbus_name.unpack()[0] window = Gio.DBusProxy.new_sync(bus, 0, None, dbus_name, '/org/gnome/evince/Window/0', 'org.gnome.evince.Window', None) window.call_sync('SyncView', GLib.Variant('(s(ii)u)', (tex, (line, 1), 0)), Gio.DBusSignalFlags.NONE, -1, None) except Exception: traceback.print_exc() class EvinceInverseSearch: def parse_args(self): parser = argparse.ArgumentParser(description='Inverse search with Evince') parser.add_argument('pdf', nargs=1, help='PDF file') parser.add_argument('editor', nargs=1, help='Editor command') return parser.parse_args() def run(self): args = self.parse_args() pdf = str(pathlib.Path(args.pdf[0]).resolve()) editor = args.editor[0] import dbus.mainloop.glib dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) a = EvinceWindowProxy('file://' + pdf, editor, True) GLib.MainLoop().run() class EvinceWindowProxy: """A Dbus proxy for an Evince Window.""" daemon = None bus = None RUNNING = range(2) CLOSED = range(2) EV_DAEMON_PATH = '/org/gnome/evince/Daemon' EV_DAEMON_NAME = 'org.gnome.evince.Daemon' EV_DAEMON_IFACE = 'org.gnome.evince.Daemon' EVINCE_PATH = '/org/gnome/evince/Evince' EVINCE_IFACE = 'org.gnome.evince.Application' EV_WINDOW_IFACE = 'org.gnome.evince.Window' def __init__(self, uri, editor, spawn=False, logger=None): self._log = logger self.uri = uri.replace(" ", "%20") self.editor = editor self.status = self.CLOSED self.source_handler = None self.dbus_name = '' self._handler = None try: if EvinceWindowProxy.bus is None: EvinceWindowProxy.bus = dbus.SessionBus() if EvinceWindowProxy.daemon is None: EvinceWindowProxy.daemon = EvinceWindowProxy.bus.get_object(self.EV_DAEMON_NAME, self.EV_DAEMON_PATH, follow_name_owner_changes=True) EvinceWindowProxy.bus.add_signal_receiver(self._on_doc_loaded, signal_name='DocumentLoaded', dbus_interface=self.EV_WINDOW_IFACE, sender_keyword='sender') self._get_dbus_name(False) except dbus.DBusException: traceback.print_exc() if self._log: self._log.debug('Could not connect to the Evince Daemon') def _on_doc_loaded(self, uri, **keyargs): if uri == self.uri and self._handler is None: self.handle_find_document_reply(keyargs['sender']) def _get_dbus_name(self, spawn): EvinceWindowProxy.daemon.FindDocument(self.uri, spawn, reply_handler=self.handle_find_document_reply, error_handler=self.handle_find_document_error, dbus_interface = self.EV_DAEMON_IFACE) def handle_find_document_error(self, error): if self._log: self._log.debug('FindDocument DBus call has failed') def handle_find_document_reply(self, evince_name): if self._handler is not None: handler = self._handler else: handler = self.handle_get_window_list_reply if evince_name != '': self.dbus_name = evince_name self.status = self.RUNNING self.evince = EvinceWindowProxy.bus.get_object(self.dbus_name, self.EVINCE_PATH) self.evince.GetWindowList(dbus_interface = self.EVINCE_IFACE, reply_handler = handler, error_handler = self.handle_get_window_list_error) def handle_get_window_list_error (self, e): if self._log: self._log.debug("GetWindowList DBus call has failed") def handle_get_window_list_reply (self, window_list): if len(window_list) > 0: window_obj = EvinceWindowProxy.bus.get_object(self.dbus_name, window_list[0]) self.window = dbus.Interface(window_obj,self.EV_WINDOW_IFACE) self.window.connect_to_signal("SyncSource", self.on_sync_source) else: #That should never happen. if self._log: self._log.debug("GetWindowList returned empty list") def on_sync_source(self, input_file, source_link, timestamp): import subprocess import re print(input_file + ':' + str(source_link[0])) # This is probably useless input_file = input_file.replace("%20", " ") # This is to deal with source files with non-ascii names # We get url-quoted UTF-8 from dbus; convert to url-quoted ascii # and then unquote. If you don't first convert ot ascii, it fails. # It's a bit magical, but it seems to work #input_file = urllib.parse.unquote(input_file.encode('ascii')) input_file = urllib.parse.unquote(input_file) print(type(input_file), input_file) #cmd = re.sub("%f", input_file, self.editor) cmd = re.sub("%f", input_file.replace('file://', ''), self.editor) cmd = re.sub("%l", str(source_link[0]), cmd) print(cmd) subprocess.run(cmd, shell=True) if self.source_handler is not None: self.source_handler(input_file, source_link, timestamp) if __name__ == '__main__': import sys cmd = pathlib.Path(sys.argv[0]).name if cmd == 'fwdevince' or cmd == 'evince_forward_search': EvinceForwardSearch().run() elif cmd == 'invevince' or cmd == 'bwdevince' or cmd == 'evince_inverse_search' or cmd == 'evince_backward_search': EvinceInverseSearch().run() else: sys.stderr.write("rename 'fwdevince' or 'invevince'\n") sys.exit(1) ---- $ chmod +x fwdevince $ sudo cp -p fwdevince /usr/local/bin $ sudo ln -s /usr/local/bin/fwdevince /usr/local/bin/invevince fwdevince は例えば $ fwdevince hoge.pdf 30 hoge.tex のように実行すると Evince が起動して PDF ファイルの対応する行が赤い枠で囲まれて表示されます. invevince は例えば [[TeXstudio]] の場合は $ evince "hoge.pdf" & $ invevince "hoge.pdf" "texstudio '%f' -line %l" のように実行して Evince 上で Ctrl + 左クリックを実行すると TeXstudio が起動して TeX 文書の対応する行にジャンプします.~
[[Evince]] > [[fwdevince>Evince/fwdevince]] > Python *fwdevince の例示ソース [#u6122dc4] // ずっと疑問なんですが,なんで TeX Wiki に展示したがるんでしょうね. // Github にでも置いてリンクすればいいと思うのですが. // その方がバージョン管理も楽だし,コピペしないでダウンロードするだけでいいし. -- kmaeda (2016/01/09) fwdevince(Evince で forward and inverse search を行うツール)の Python による例示ソースです. [[Fedora>Linux/Fedora]] 26 の Evince 3.24.0, Python 3.6.2, dbus-python 1.2.4, PyGObject 3.24.1 で動作確認しています. なお https://www.freedesktop.org/wiki/Software/DBusBindings/ によると dbus-python は Obsolete libraries で New applications should use pydbus, txdbus or GDBus/QtDBus bindings. とのことです. うまく動作しない場合は -コマンドラインから python3 --version を実行して Python のバージョンを確認して Python 3.5 以降でなければ Python 3.5 以降にアップグレードする -[[Okular]], [[zathura]], [[qpdfview]] などの Evince 以外の SyncTeX 対応ビューアで forward and inverse search を実行する -[[TeXstudio]], [[Texmaker]], [[TeXworks]] の組み込みビューアで forward and inverse search を実行する -fwdevince をうまく動作するように修正する //--例えば端末から $ python -V とした時に Python 2.7.6 と表示される環境だと urllib.parse の読み込みに失敗します.その場合は python3 をインストールし,先頭行の #!/usr/bin/env python を #!/usr/bin/env python3 などと変更する. などを試みてください. // オリジナルが GPL なので,ライセンス表示を消してはならない. // 最初に書いた通り置き場所も検討されるべきだが,とりあえず. // -- kmaeda (2016/05/08) ---- -fwdevince ---- #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2010 Jose Aliste # 2011 Benjamin Kellermann # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public Licence as published by the Free Software # Foundation; either version 2 of the Licence, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public Licence for more # details. # # You should have received a copy of the GNU General Public Licence along with # this program; if not, write to the Free Software Foundation, Inc., 51 Franklin # Street, Fifth Floor, Boston, MA 02110-1301, USA from gi.repository import GLib, Gio import dbus import argparse import pathlib import traceback import urllib.parse class EvinceForwardSearch: def parse_args(self): parser = argparse.ArgumentParser(description='Forward search with Evince') parser.add_argument('pdf', nargs=1, help='PDF file') parser.add_argument('line', nargs=1, type=int, help='Line') parser.add_argument('tex', nargs=1, help='TeX file') return parser.parse_args() def run(self): args = self.parse_args() pdf = str(pathlib.Path(args.pdf[0]).resolve()).replace(" ", "%20") line = int(args.line[0]) tex = str(pathlib.Path(args.tex[0]).resolve().parent / pathlib.Path('./') / pathlib.Path(args.tex[0]).resolve().name) try: bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) daemon = Gio.DBusProxy.new_sync(bus, 0, None, 'org.gnome.evince.Daemon', '/org/gnome/evince/Daemon', 'org.gnome.evince.Daemon', None) dbus_name = daemon.call_sync('FindDocument', GLib.Variant('(sb)', ('file://' + urllib.parse.quote(pdf, safe="%/:=&?~#+!$,;'@()*[]"), True)), Gio.DBusSignalFlags.NONE, -1, None) dbus_name = dbus_name.unpack()[0] window = Gio.DBusProxy.new_sync(bus, 0, None, dbus_name, '/org/gnome/evince/Window/0', 'org.gnome.evince.Window', None) window.call_sync('SyncView', GLib.Variant('(s(ii)u)', (tex, (line, 1), 0)), Gio.DBusSignalFlags.NONE, -1, None) except Exception: traceback.print_exc() class EvinceInverseSearch: def parse_args(self): parser = argparse.ArgumentParser(description='Inverse search with Evince') parser.add_argument('pdf', nargs=1, help='PDF file') parser.add_argument('editor', nargs=1, help='Editor command') return parser.parse_args() def run(self): args = self.parse_args() pdf = str(pathlib.Path(args.pdf[0]).resolve()) editor = args.editor[0] import dbus.mainloop.glib dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) a = EvinceWindowProxy('file://' + pdf, editor, True) GLib.MainLoop().run() class EvinceWindowProxy: """A Dbus proxy for an Evince Window.""" daemon = None bus = None RUNNING = range(2) CLOSED = range(2) EV_DAEMON_PATH = '/org/gnome/evince/Daemon' EV_DAEMON_NAME = 'org.gnome.evince.Daemon' EV_DAEMON_IFACE = 'org.gnome.evince.Daemon' EVINCE_PATH = '/org/gnome/evince/Evince' EVINCE_IFACE = 'org.gnome.evince.Application' EV_WINDOW_IFACE = 'org.gnome.evince.Window' def __init__(self, uri, editor, spawn=False, logger=None): self._log = logger self.uri = uri.replace(" ", "%20") self.editor = editor self.status = self.CLOSED self.source_handler = None self.dbus_name = '' self._handler = None try: if EvinceWindowProxy.bus is None: EvinceWindowProxy.bus = dbus.SessionBus() if EvinceWindowProxy.daemon is None: EvinceWindowProxy.daemon = EvinceWindowProxy.bus.get_object(self.EV_DAEMON_NAME, self.EV_DAEMON_PATH, follow_name_owner_changes=True) EvinceWindowProxy.bus.add_signal_receiver(self._on_doc_loaded, signal_name='DocumentLoaded', dbus_interface=self.EV_WINDOW_IFACE, sender_keyword='sender') self._get_dbus_name(False) except dbus.DBusException: traceback.print_exc() if self._log: self._log.debug('Could not connect to the Evince Daemon') def _on_doc_loaded(self, uri, **keyargs): if uri == self.uri and self._handler is None: self.handle_find_document_reply(keyargs['sender']) def _get_dbus_name(self, spawn): EvinceWindowProxy.daemon.FindDocument(self.uri, spawn, reply_handler=self.handle_find_document_reply, error_handler=self.handle_find_document_error, dbus_interface = self.EV_DAEMON_IFACE) def handle_find_document_error(self, error): if self._log: self._log.debug('FindDocument DBus call has failed') def handle_find_document_reply(self, evince_name): if self._handler is not None: handler = self._handler else: handler = self.handle_get_window_list_reply if evince_name != '': self.dbus_name = evince_name self.status = self.RUNNING self.evince = EvinceWindowProxy.bus.get_object(self.dbus_name, self.EVINCE_PATH) self.evince.GetWindowList(dbus_interface = self.EVINCE_IFACE, reply_handler = handler, error_handler = self.handle_get_window_list_error) def handle_get_window_list_error (self, e): if self._log: self._log.debug("GetWindowList DBus call has failed") def handle_get_window_list_reply (self, window_list): if len(window_list) > 0: window_obj = EvinceWindowProxy.bus.get_object(self.dbus_name, window_list[0]) self.window = dbus.Interface(window_obj,self.EV_WINDOW_IFACE) self.window.connect_to_signal("SyncSource", self.on_sync_source) else: #That should never happen. if self._log: self._log.debug("GetWindowList returned empty list") def on_sync_source(self, input_file, source_link, timestamp): import subprocess import re print(input_file + ':' + str(source_link[0])) # This is probably useless input_file = input_file.replace("%20", " ") # This is to deal with source files with non-ascii names # We get url-quoted UTF-8 from dbus; convert to url-quoted ascii # and then unquote. If you don't first convert ot ascii, it fails. # It's a bit magical, but it seems to work #input_file = urllib.parse.unquote(input_file.encode('ascii')) input_file = urllib.parse.unquote(input_file) print(type(input_file), input_file) #cmd = re.sub("%f", input_file, self.editor) cmd = re.sub("%f", input_file.replace('file://', ''), self.editor) cmd = re.sub("%l", str(source_link[0]), cmd) print(cmd) subprocess.run(cmd, shell=True) if self.source_handler is not None: self.source_handler(input_file, source_link, timestamp) if __name__ == '__main__': import sys cmd = pathlib.Path(sys.argv[0]).name if cmd == 'fwdevince' or cmd == 'evince_forward_search': EvinceForwardSearch().run() elif cmd == 'invevince' or cmd == 'bwdevince' or cmd == 'evince_inverse_search' or cmd == 'evince_backward_search': EvinceInverseSearch().run() else: sys.stderr.write("rename 'fwdevince' or 'invevince'\n") sys.exit(1) ---- $ chmod +x fwdevince $ sudo cp -p fwdevince /usr/local/bin $ sudo ln -s /usr/local/bin/fwdevince /usr/local/bin/invevince fwdevince は例えば $ fwdevince hoge.pdf 30 hoge.tex のように実行すると Evince が起動して PDF ファイルの対応する行が赤い枠で囲まれて表示されます. invevince は例えば [[TeXstudio]] の場合は $ evince "hoge.pdf" & $ invevince "hoge.pdf" "texstudio '%f' -line %l" のように実行して Evince 上で Ctrl + 左クリックを実行すると TeXstudio が起動して TeX 文書の対応する行にジャンプします.~
テキスト整形のルールを表示する