Calc(28)コンテクストメニューをカスタマイズする: その1

2017-10-22

旧ブログ

t f B! P L
LibreOffice5(2)コンテクストメニューをカスタマイズする例ではCalcのBasicの例、WriterのJavaの例をやりました。Calcでコンテクストメニューのカスタマイズをしていきます。

前の関連記事:Calc(27)ドキュメント内のすべてのシートでクリックを補足する


contextmenuinterceptor.pyのコードを整理

# -*- coding: utf-8 -*-
import unohelper  # オートメーションには必須(必須なのはuno)。
from com.sun.star.ui import XContextMenuInterceptor
from com.sun.star.ui import ActionTriggerSeparatorType  # 定数
from com.sun.star.ui.ContextMenuInterceptorAction import EXECUTE_MODIFIED  # enum
def macro(): 
 doc = XSCRIPTCONTEXT.getDocument()  # ドキュメントのモデルを取得。 
 controller = doc.getCurrentController()  # コントローラーを取得。
 contextmenuinterceptor = ContextMenuInterceptor()
 controller.registerContextMenuInterceptor(contextmenuinterceptor)
class ContextMenuInterceptor(unohelper.Base, XContextMenuInterceptor):
 def notifyContextMenuExecute(self, contextmenuexecuteevent):  # com.sun.star.ui.ActionTriggerにsetPropertyValuesでは設定できない。エラーも出ない。
  contextmenu = contextmenuexecuteevent.ActionTriggerContainer
  submenucontainer = contextmenu.createInstance("com.sun.star.ui.ActionTriggerContainer") 
  addMenuentry(submenucontainer, "ActionTrigger", 0, {"Text": "Content", "CommandURL": ".uno:HelpIndex", "HelpURL": "5401"})
  addMenuentry(submenucontainer, "ActionTrigger", 1, {"Text": "Tips", "CommandURL": ".uno:HelpTip", "HelpURL": "5404"})
  addMenuentry(contextmenu, "ActionTrigger", 0, {"Text": "Help", "CommandURL": ".uno:HelpMenu", "HelpURL": "5410", "SubContainer": submenucontainer})
  addMenuentry(contextmenu, "ActionTriggerSeparator", 1, {"SeparatorType": ActionTriggerSeparatorType.LINE})
  return EXECUTE_MODIFIED
def addMenuentry(menucontainer, menutype, i, props):  # i: index, propsは辞書。menutypeはActionTriggerかActionTriggerSeparator。
 menuentry = menucontainer.createInstance("com.sun.star.ui.{}".format(menutype))  # ActionTriggerContainerからインスタンス化する。
 [menuentry.setPropertyValue(key, val) for key, val in props.items()]  #setPropertyValuesでは設定できない。エラーも出ない。
 menucontainer.insertByIndex(i, menuentry)  # submenucontainer[i]やsubmenucontainer[i:i]は不可。挿入以降のメニューコンテナの項目のインデックスは1増える。
g_exportedScripts = macro, #マクロセレクターに限定表示させる関数をタプルで指定。
LibreOffice5(2)コンテクストメニューをカスタマイズする例の最後に追記したcontextmenuinterceptor.pyのコードをもっと整理しました。

Javaの例ではオートメーションでinput()でキー入力待ちにしていましたが、マクロで実行すると入力待ちにしなくてもコンテクストメニューの変更ができました。

コマンドURLはslotに代わってディスパッチコマンドにしました(Development/DispatchCommands - The Document Foundation Wiki)。

HelpURLプロパティは何を参照にして指定すればいいのか今のところわかりません。


ディスパッチコマンドにするとメニューアイコンも表示されました。

しかし、このアイコンやチェックボックスはディスパッチコマンドによるものであって、APIで設定したものではありません。

.uno:HelpMenuは何をするディスパッチコマンドなのかわかりませんでした。


SubContainerプロパティをもっているメニュー項目は動かなくなるようで、コマンドURLが.uno:HelpIndexのメニュー項目にSubContainerプロパティを設定すると、アイコンは表示されますがヘルプは起動しなくなりました。

ということはSubContainerプロパティがあるメニュー項目にCommandURLプロパティを設定する意味はディスパッチコマンドのアイコンを表示させるという意味しかないことになります。

.uno:HelpIndexのチェックボックスはアイコンではなく、機能として提供されていいるようで、SubContainerプロパティを設定するとそのメニュー項目ではチェックボックスは表示されなくなりました。

コンテクストメニューをカスタマイズするマクロをドキュメントに埋め込む

# -*- coding: utf-8 -*-
import unohelper  # オートメーションには必須(必須なのはuno)。
from com.sun.star.ui import XContextMenuInterceptor
from com.sun.star.ui import ActionTriggerSeparatorType  # 定数
from com.sun.star.ui.ContextMenuInterceptorAction import EXECUTE_MODIFIED  # enum
def macro(documentevent):
 doc = documentevent.Source  # ドキュメントの取得。
 controller = doc.getCurrentController()  # コントローラの取得。
 controller.registerContextMenuInterceptor(ContextMenuInterceptor())  # コントローラにContextMenuInterceptorを登録する。
class ContextMenuInterceptor(unohelper.Base, XContextMenuInterceptor):  # コンテクストメニューに割り込ませる。
 def notifyContextMenuExecute(self, contextmenuexecuteevent):  # 引数はContextMenuExecuteEvent Struct。
  contextmenu = contextmenuexecuteevent.ActionTriggerContainer  # すでにあるコンテクストメニュー(アクショントリガーコンテナ)を取得。
  submenucontainer = contextmenu.createInstance("com.sun.star.ui.ActionTriggerContainer")  # サブメニューにするアクショントリガーコンテナをインスタンス化。
  addMenuentry(submenucontainer, "ActionTrigger", 0, {"Text": "Content", "CommandURL": ".uno:HelpIndex", "HelpURL": "5401"})  # アクショントリガーコンテナのインデックス0にアクショントリガーを挿入。
  addMenuentry(submenucontainer, "ActionTrigger", 1, {"Text": "Tips", "CommandURL": ".uno:HelpTip", "HelpURL": "5404"})  # アクショントリガーコンテナのインデックス1にアクショントリガーを挿入。
  addMenuentry(contextmenu, "ActionTrigger", 0, {"Text": "Help", "CommandURL": ".uno:HelpMenu", "HelpURL": "5410", "SubContainer": submenucontainer})  # アクショントリガーコンテナのインデックス0にアクショントリガーを挿入。サブメニューも挿入。
  addMenuentry(contextmenu, "ActionTriggerSeparator", 1, {"SeparatorType": ActionTriggerSeparatorType.LINE})  # アクショントリガーコンテナのインデックス1にセパレーターを挿入。
  return EXECUTE_MODIFIED  
def addMenuentry(menucontainer, menutype, i, props):  # i: index, propsは辞書。menutypeはActionTriggerかActionTriggerSeparator。
 menuentry = menucontainer.createInstance("com.sun.star.ui.{}".format(menutype))  # ActionTriggerContainerからインスタンス化する。
 [menuentry.setPropertyValue(key, val) for key, val in props.items()]  #setPropertyValuesでは設定できない。エラーも出ない。
 menucontainer.insertByIndex(i, menuentry)  # submenucontainer[i]やsubmenucontainer[i:i]は不可。挿入以降のメニューコンテナの項目のインデックスは1増える。
g_exportedScripts = macro, #マクロセレクターに限定表示させる関数をタプルで指定。
このマクロをCalc(27)ドキュメント内のすべてのシートでクリックを補足すると同様にしてCalcドキュメントに埋め込んで、「文書を開いた時」に割り当てて保存します。

ActionTriggerサービスとActionTriggerSeparatorサービスはActionTriggerContainerサービスのインスタンスであればそれからインスタンス可能と分かったので、addMenuentry()の高階関数をやめました。

CalcExamples - p--qのcontextmenuinterceptor.odsが保存したCalcファイルになります。

Windows版で実行してみるとショートカットキーにF1が追加されていました。

これもマクロで設定したものではなく、ディスパッチコマンドによるものです。

汎用関数: addMenuentry(menucontainer, menutype, i, props)

def addMenuentry(menucontainer, menutype, i, props):  # i: index, propsは辞書。menutypeはActionTriggerかActionTriggerSeparator。
 menuentry = menucontainer.createInstance("com.sun.star.ui.{}".format(menutype))  # ActionTriggerContainerからインスタンス化する。
 [menuentry.setPropertyValue(key, val) for key, val in props.items()]  #setPropertyValuesでは設定できない。エラーも出ない。
 menucontainer.insertByIndex(i, menuentry)  # submenucontainer[i]やsubmenucontainer[i:i]は不可。挿入以降のメニューコンテナの項目のインデックスは1増える。
ActionTriggerContainerサービスのインスタンスをmenucontainerに受け取り、ActionTriggerActionTriggerSeparatorのどちらを挿入するのかmenutypeで文字列で指定します。

menutypeのプロパティはpropsで辞書で渡します。

iはmenucontainerに挿入するインデックスを指定します。

インデックスを同じ値で挿入するとそこから下の項目は下にずれていきます。

インデックス0は常に挿入できますが、飛び番になるインデックス(例えば2までしかないのに4に挿入する)では挿入できず、IndexOutOfBoundsExceptionがでて、そこで終了します。

ActionTriggerActionTriggerSeparatorはsetPropertyValues()があるのですが、これを使ってもプロパティは設定できませんでした。

またinsertByIndex()メソッドはLibreOffice 5.1からの方法(linuxBean14.04(131)LibreOfiice5.2のインストール参照)で置き換えられると思ったのですが、replaceByIndex()メソッドに相当する場合にしか使えませんでしたので、すべて。insertByIndex()メソッドにしています。

(2017.11.5追記。いちいちインデックスを指定するのは面倒なので、コードに書いた順にインデックスが増えていくように高階関数にしました。
def menuentryCreator(menucontainer):  # 引数のActionTriggerContainerにインデックス0から項目を挿入する関数を取得。
 i = 0  # インデックスを初期化する。
 def addMenuentry(menutype, props):  # i: index, propsは辞書。menutypeはActionTriggerかActionTriggerSeparator。
  menuentry = menucontainer.createInstance("com.sun.star.ui.{}".format(menutype))  # ActionTriggerContainerからインスタンス化する。
  [menuentry.setPropertyValue(key, val) for key, val in props.items()]  #setPropertyValuesでは設定できない。エラーも出ない。
  menucontainer.insertByIndex(i, menuentry)  # submenucontainer[i]やsubmenucontainer[i:i]は不可。挿入以降のメニューコンテナの項目のインデックスは1増える。
  nonlocal i
  i += 1  # インデックスを増やす。
 return addMenuentry
Calc(40)コンテクストメニューをカスタマイズする: その8参照。)

XContextMenuInterceptorインターフェイスのメソッドのデバッグ


XContextMenuInterceptorインターフェイスのメソッドもリスナーのメソッドと同様にオートメーションでブレイクポイントを設定しても引っかからないので、Eclipse: PyDevメモ: LibreOfficeのPythonマクロのデバッグのenableRemoteDebuggingデコレーターを使いました。

だけど、マウスがらみのイベントのせいか、EclipseがGUIに反応しなくなることもあります。

しかし、そのときショートカットキー(Eclipse: PyDevメモ: LibreOfficeのPythonマクロのリスナーの問題点参照)で操作できないことが多くて困ります。

いまのところはうまくいかない規則性がよくわかりませんが、とりあえず困ったら全部再起動しています。

LibreOfficeで発火させてから、PyDevデバッグサーバーを起動した方がうまくいくような気もします。

参考にしたサイト


Development/DispatchCommands - The Document Foundation Wiki
ディスパッチコマンド一覧。

Framework/Tutorial/Context Menu Interception - Apache OpenOffice Wiki
コンテクストインターセプターはリスナーでしょうか。戻り値で制御するのでハンドラのような気もします。

次の関連記事:Calc(29)コンテクストメニューをカスタマイズする: その2

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ