Top > OOoPython > ComplexToolbar

複雑なツールバー Edit

OOo 2.2 からアドオンでも容易にボタン以外のコントロール (テキストフィールドやドロップダウンボタン、リストボックスなど) をツールバーに組み込めるようになりました。しかし、組み込みは簡単ですが利用が少し面倒です。ここでは Py-UNO を利用して複雑なツールバーを作成していきます。

Generic UNO Interfaces for complex toolbar controls で SDK 付属の例に関してアドオンでの複雑なツールバーの利用が説明されています。しかし、利用されていないコードが含まれていたりといったことで不必要にごちゃごちゃしています。

準備 Edit

以下を理解するには次のことを知っているといいかもしれません。

  • Addons.xcu ファイル (アドオンツールバーの定義)
    DevGuide Add-Ons および shared/registry/schema/org/openoffice/Office/Addons.xcs
  • Dispatch Provider と Dispatch (コマンドの実行)
    DevGuide - Using the Dispatch Framework
  • Protocol Handler (プロトコルのプロセス関連)
    DevGuide - Protocol Handler

作成するのは主に次のファイルです。これらのファイル名は一例で変更できます。

  • Addons.xcu - アドオン用のツールバー
  • ProtocolHandler.xcu - プロトコルハンドラ定義
  • handler.py - プロトコルハンドラ実装

コマンド Edit

OpenOffice.org のコマンド実行環境に関する説明です。

OpenOffice.org ではメニューなどからコマンドを実行し、そのコマンドをコンポーネントが受け取ることで (ユーザーなどからの) 処理が行われます。このコマンドは URL 形式をしています。そのため、その URL にはプロトコル (http: や .uno: など) とパス (localhost や Copy など) が含まれます。API でこの URL を取り扱うときには com.sun.star.util.URL 構造体を利用します。

OOo に組み込まれているコマンドはどこかのコンポーネントによって処理されます。組み込み型のコマンドはコマンド URL として .uno:Copy といった形で実行されます。.uno: 部分がプロトコル、Copy 部分がパスに相当します。ここではそういったコマンドを受け取り、処理するコンポーネントを新たに作成しなければいけません。

新たに作成するコンポーネントが取り扱うプロトコルは勝手に決めてかまいませんが、簡単なものだと他の人が作成したものと同じになり正しく動作しないかもしれないので、ドメイン名を逆からといったように長めにするほうがいいとされています。

ここではプロトコルを "mytools.handler:" とします。パスの部分はツールバーに配置するコントロールと関連して付けます。

XDispatchProvider Edit

あるコマンドがメニューの項目やツールバーのボタンなどに割り当てられていたとします。ツールバーマネージャによりその項目もしくはボタンが作成されたときにそれに対応したコンポーネントの com.sun.star.frame.XDispatchProvider インターフェースの queryDispatch メソッドが呼ばれます。このとき、コンポーネントは状況に応じて com.sun.star.frame.XDispatch インターフェースを戻します。大抵は次のようなことを確認します。

  • プロトコルやパス名の確認
  • その他の状況に依存しているのであれば、その確認

実際にコマンドが実行されるときに XDispatch インターフェースの dispatch メソッドが呼ばれます。そのため、queryDispatch メソッドが XDispatch インターフェースを戻さなければそのコマンドは使用できなくなります。こういった場合にはツールバーボタンが灰色に無効化された状態になります。

XDispatch インターフェースを戻さないのではなくボタンや項目を無効にする処理をするべきです。

XDispatchProvider

XDispatch Edit

コマンドが実行されるとき、queryDispatch メソッドにより返された XDispatch インターフェースの dispatch メソッドが呼ばれ、コンポーネントが処理を行います。

プロトコルハンドラを実装するだけであれば XDispatch の役目はほとんどこれだけです。しかし、ツールバーのさまざまな種類のコントロールを取り扱うにはこのインターフェースのほかのメソッドを使わなければいけません。

queryDispatch の後、XDispatch インターフェースの addStatusListener メソッドがリスナーを登録するために呼ばれます。このとき、ツールバーのコントロールに変更を加えられるリスナーがメソッドの引数として含まれます。これを利用してリストボックスに項目を追加したりといったことを行います。

XDispatch

XControlNotificationListener Edit

com.sun.star.frame.XControlNotificationListener インターフェースはコントロールでイベントが起きた際のリスナーです。このインターフェースを XDispatch を実装するコンポーネントに実装しておくとイベントを受け取ることができます。テキストフィールドの文字列が変更されたときなどに値を受け取るために利用します。

XControlNotificationListener

ツールバーと機能 Edit

さっぱり例が思いつかなかったので次のようなツールバーを作成することにします。

  • 次のようにテキストフィールド、ボタン、スピンコントロールを備えたツールバー。
  • 各コントロールは Writer ドキュメントでのみ表示される。

ボタンを押すとテキストフィールドに入力された文字がスピンコントロールで指定された回数だけテキストカーソル位置に入力される。

パッケージ

complextoolbar-1.png

アドオンツールバー Edit

ツールバーでは次のコントロールを作成、利用できます。

コントロールオプション説明
Buttonボタン
Comboboxコンボボックス
Dropdownboxリストボックス
DropdownButtonドロップダウンボタン
Editfieldテキストフィールド
ImageButton画像ボタン
Spinfieldスピンフィールド
ToggleButtonトグルボタン
ToggleDropdownButton切り替えドロップボタン

*: Width 指定可

ツールバーの設定時に利用するコントロールのプロパティは次のものです。

プロパティ説明
URLstringコントロールの URL
Titlestringラベルまたはツールチップテキスト(ロカライズ可)
Contextstringコントロールを表示するモジュール名
ImageIdentifierstring画像指定
Targetstringターゲットフレーム
ControlTypestringコントロールの種類
Widthlongコントロールの幅 (種類に依存)

アドオンのツールバーを Addons.xcu に記述して作成します。アドオンファイルの OOo レジストリ内での名称は org.openoffice.Office.Addons です。

AddonUI の OfficeToolBar に新しいノードを追加してやると新しいツールバーが作成できます。ノード名は独自のものになるように付けます、ここでは "mytools.test.toolbar2" とします。

Addons.xcu ファイル

  0
  1
  2
  3
  4
  5
<?xml version='1.0' encoding='UTF-8'?>
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Addons" 
  oor:package="org.openoffice.Office">
<node oor:name="AddonUI">
  <node oor:name="OfficeToolBar">
    <node oor:name="mytools.test.toolbar2" oor:op="replace">

ツールバーにコントロールを追加していきます。コントロールごとに新しいノードを作成し、プロパティとして URL、Title、Context などを設定します。

テキストフィールド Edit

一つ目はテキストフィールドです。

  • URL: mytools.handler:edit。このコントロールが実行されたときなどにこの、プロトコル+パスが利用されます。
  • Title: Text。コントロールのツールチップに表示されます。
  • Context: com.sun.star.text.TextDocument。Writer ドキュメントのときのみこのコントロールを表示します。
  • ControlType: Editfield。テキストフィールドを作成します。
  • Width: 100。テキストフィールドなどは幅の指定が可能です。
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
      <node oor:name="a1_0" oor:op="replace">
        <prop oor:name="URL">
          <value>mytools.handler:edit</value>
        </prop>
        <prop oor:name="Title">
          <value>Text</value>
        </prop>
        <prop oor:name="Context" oor:type="xs:string">
          <value>com.sun.star.text.TextDocument</value>
        </prop>
        <prop oor:name="ControlType">
          <value>Editfield</value>
        </prop>
        <prop oor:name="Width">
          <value>100</value>
        </prop>
      </node>

ボタン Edit

二つ目のコントロールはボタンです。

  • URL: mytools.handler:button
  • ImageIdentifier: %origin%/write。表示画像として、この Addons.xcu ファイルを配置したディレクトリにある writer_16.bmp を指定。
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
      <node oor:name="a1_1" oor:op="replace">
        <prop oor:name="URL">
          <value>mytools.handler:button</value>
        </prop>
        <prop oor:name="Title">
          <value>Insert</value>
        </prop>
        <prop oor:name="Context" oor:type="xs:string">
          <value>com.sun.star.text.TextDocument</value>
        </prop>
        <prop oor:name="ImageIdentifier" oor:type="xs:string">
        <value>%origin%/write</value>
      </prop>
      </node>

スピンフィールド Edit

三つ目のコントロールとしてスピンコントロールです。

  • URL: mytools.handler:spin
  • ControlType: Spinfield
  • Width: 50
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
      <node oor:name="a1_2" oor:op="replace">
        <prop oor:name="URL">
          <value>mytools.handler:spin</value>
        </prop>
        <prop oor:name="Title">
          <value>Number of times</value>
        </prop>
        <prop oor:name="Context" oor:type="xs:string">
          <value>com.sun.star.text.TextDocument</value>
        </prop>
        <prop oor:name="ControlType">
          <value>Spinfield</value>
        </prop>
        <prop oor:name="Width">
          <value>50</value>
        </prop>
      </node>
    </node>
  </node>
</node>
</oor:component-data>

プロトコルハンドラ Edit

プロトコルハンドラの設定を ProtocolHandler.xcu に記述します。このファイルのスキーマは shared/schema/org/openoffice/Office/ProtocolHandler.xcs にあります。

このファイルでは、どのプロトコルハンドラ (コンポーネント) がどのようなプロトコルをハンドルするかを定義します。プロトコルハンドラはそのコンポーネントを実装するコンポーネントの実装名で指定します。また、プロトコルは文字列で指定しますが、プロトコル以降の部分はワイルドカードとして * が利用できます。

  • 実装名 (6 行目): mytools.handler.Test
  • プロトコル (8 行目): mytools.handler:*

プロトコルはアドオンツールバーでコントロールの URL プロパティに指定したものと同じにします。

ProtocolHandler.xcu

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
<?xml version='1.0' encoding='UTF-8'?>
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="ProtocolHandler" 
  oor:package="org.openoffice.Office">
<node oor:name="HandlerSet">
  <node oor:name="mytools.handler.Test" oor:op="replace">
    <prop oor:name="Protocols" oor:type="oor:string-list">
      <value>mytools.handler:*</value>
    </prop>
  </node>
</node>
</oor:component-data>

ここで定義したプロトコルハンドラは mytools.handler: につづくすべての URL を受け取ります。プロトコルハンドラの本体のコンポーネントは実装名 mytools.handler.Test のコンポーネントとして以下で作成します。

コンポーネント Edit

プロトコルハンドラを実際に実装する UNO コンポーネントを作成します。

注意点

  • ボタンを押したときにテキストフィールドの値を取得して・・・といったことはできない。が、コントロールの各種イベント時にその値を取得できるので、値をどこかに保存しておいて利用する。
  • ツールバーはフレームごとに作成される。そのため、保存する値はフレームごとに管理する。
  • フレームが閉じられたとき、保存していた値などを削除する。フレームが閉じられたことを検出するためにフレームの XComponent インターフェースの addEventListener メソッドを利用して com.sun.star.lang.XEventListener インターフェースをリスナーとして追加する。このリスナーはフレームが閉じられたときに disposing メソッドが呼ばれるため、そのイベントでフレームが閉じられるときを知ります。

UNO を実装する Python のコードは UTF-8 で改行コードを LF にしておくとエラー無く利用できます (改行コードに CR が含まれると不明なエラーの基になります)。

拡張機能に含まれているコードと行番号が違っているかもしれません。

宣言部 Edit

uno.py を createUnoStruct などの利用のために、unohelper.py を登録のためにインポートします。また、UNO コンポーネントなどが継承するインターフェースもインポートしておきます。

handler.py ファイル

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
#!
# -*- coding: utf_8 -*-
import uno
import unohelper
 
#interfaces
from com.sun.star.frame import XControlNotificationListener
from com.sun.star.frame import XDispatch
from com.sun.star.frame import XDispatchProvider
from com.sun.star.lang import XEventListener
from com.sun.star.lang import XInitialization
from com.sun.star.lang import XServiceInfo
 
# handled protocol by this component
HANDLED_PROTOCOL = "mytools.handler:"
# implementation name of this component
PROTOCOLHANDLER_IMPLEMENTATIONNAME = "mytools.handler.Test"

コード内で利用するプロトコルは一律 (mytools.handler:) なので定義しておきます。実装名も同様です。

ヘルパークラス Edit

値を保存しておくために利用するクラスです。あとでグローバル変数としてインスタンス化する必要があるので先にクラスを定義しておきます。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
# this helper retains something for toolbar control
class Helper(unohelper.Base):
	def __init__(self):
		# self.listeners store something for each frame
		self.listeners = []
		return
	
	
	# set value of the control indicated by Path
	def setValue(self,xFrame,Path,Value):
		for listener in self.listeners:
			if listener["frame"] == xFrame:
				listener["values"][Path] = Value
		return
	
	
	# get value of the control of Path
	def getValue(self,xFrame,Path):
		for listener in self.listeners:
			if listener["frame"] == xFrame:
				if listener["values"].has_key(Path):
					return listener["values"][Path]
		return None
	
	

setValue と getValue でフレームごと、パス指定でコントロールの値 (など) を保存、取得します。

XDispatch インターフェースを持つクラスのインスタンスが作成されたとき addDispatch メソッドでフレームを追加しておきます。さらに、フレームにイベントリスナーを追加します。フレームに追加するリスナーが disposing されるときが、フレームが閉じられるときです。

新しい XDispatch が登録されるとき一つのフレームに対して self.listeners リストに辞書を一つ用意します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
	# when new frame is created (new toolbar with it controls is created)
	# container: stores key: Path, value: XStatusListener
	def addDispatch(self,xDispatch,xFrame,Path):
		for listener in self.listeners:
			if listener["frame"] == xFrame:
				listener["dispatch"][Path] = xDispatch
				return
		# new item
		elements = {"frame": xFrame, "dispatch": {}, "container": {},"values": {} }
		elements["dispatch"][Path] = xDispatch
		self.listeners.append(elements)
		xFrame.addEventListener(ItemEventListener(xFrame))
		return
	
	
	# get its dipatch
	def getDispatch(self,xFrame,Path):
		for listener in self.listeners:
			if listener["frame"] == xFrame:
				if listener["dispatch"].has_key(Path):
					return listener["dispatch"][Path]
				break
		return None
	
	

コントロールに対してコマンドを実行するときに次のメソッドを利用します。これは、イベントが起きたり dispatch が実行されたコントロール以外のコントロールに対して操作するときに利用します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
	# change status of the control of Path
	def notify(self,xFrame,Path,aEvent):
		for listener in self.listeners:
			if listener["frame"] == xFrame:
				if listener["container"].has_key(Path):
					xControl = listener["container"][Path]
					xControl.statusChanged(aEvent)
					break
		return
	
	

XStatusListener を追加します。このようにコントロールのリスナーを変数に保持しておくことで後から利用できるようにします。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
	# add control to container
	def addListener(self,xFrame,xControl,Path):
		for listener in self.listeners:
			if listener["frame"] == xFrame:
				listener["container"][Path] = xControl
				break
		return
	
	
	# remove control from container
	def removeListener(self,xFrame,xControl,Path):
		for listener in self.listeners:
			if listener["frame"] == xFrame:
				container = listener["container"][Path]
				for i in range(len(container)):
					if container[i] is xControl:
						try:
							del container[i]
						except:
							pass
				break
		return

フレームに追加してフレームが閉じるときを知るために利用するリスナーです。フレーム (ドキュメント) が閉じるときにこのリスナーの disposing メソッドが呼ばれます。そのとき、所定のフレームの変数を開放、削除します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
# this listener could be dispose listeners when frame is closed
class ItemEventListener(
	unohelper.Base, 
	XEventListener):
	
	def __init__(self,xFrame):
		self.frame = xFrame
	
	
	# when frame closing, registerd listener is removed
	def disposing(self,aEvent):
		listeners = ListenerHelper.listeners
		for i in range(len(listeners)):
			if listeners[i]["frame"] == self.frame:
				del listeners[i]
		return
 

データなどの保存のための変数 Edit

上記 Helper クラスのインスタンスをグローバル変数に入れて利用します。

  0
  1
# to keep something
ListenerHelper = Helper()

プロトコルハンドラコンポーネント Edit

プロトコルハンドラを実装するメインのコンポーネントです。メインのはずですが簡単です。

プロトコルハンドラ (サービス com.sun.star.frame.ProtocolHandler) 実装には次のインターフェースが必要です。

  • XDispatchProvider
  • XInitialization: 初期化用

また、XServiceInfo インターフェースはサービスを実装するときにコンポーネントに必要です。実際は XTypeProvider も実装するべきです。(Py-UNO でのコンポーネントは unohelper.Base を継承すれば、登録情報から自動的に登録されます。)

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
# component implementation class
class TestProtocolHandler(unohelper.Base,
	XServiceInfo,
	XDispatchProvider,
	XInitialization):
	
	def __init__(self,ctx):
		self.ctx = ctx
		self.smgr = ctx.ServiceManager
		self.frame = None
		return
	
	
	# XInitialization
	def initialize(self,objects):
		if len(objects) > 0:
			self.frame = objects[0]
		return
	
	

inilialize メソッドがプロトコルハンドラ初期化時に呼ばれ、フレームオブジェクトへの参照が渡されます。ドキュメントにアクセスするためにこのフレームへの参照を保存しておきます。フレーム - コントローラ - (ドキュメント) モデルと順を経てドキュメントにアクセスできます。

XDispatchProvider インターフェースのメソッド queryDispatch では、正しいプロトコル+パスの URL が渡されたとき XDispatch インターフェースを返します (その他の条件が必要であれば判断する)。ツールバーで作成した三つのコントロールのパスはそれぞれ "edit"、"button"、"spin" です。XDispatch インターフェースを実装するのは別のクラスとしています。

queryDispatch メソッドが呼ばれるのは、ドキュメントのフレームが作成されてツールバーマネージャからツールバーが作成されたときです。この時点でフレームが利用できるので、このフレームへの参照を ListenerHelper 変数に保存しておきます。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
	# XDispatchProvider
	def queryDispatch(self,aURL,name,flag):
		dispatch = None
		if aURL.Protocol == HANDLED_PROTOCOL:
			if aURL.Path in ["button","edit","spin"]:
				dispatch = BaseDispatch(self.ctx,self.frame)
				ListenerHelper.addDispatch(dispatch,self.frame,aURL.Path)
		return dispatch
	
	
	# XDispatchProvider
	def queryDispatches(self,descs):
		dispatchers = []
		for desc in descs:
			dispatchers.append(
				self.queryDispatch(desc.FeatureURL,desc.FrameName,desc.SearchFlags))
		return tuple(dispatchers)
	
	

サービスの情報定義部分です。

  0
  1
  2
  3
  4
  5
  6
  7
  8
	# XServiceInfo
	def supportsService(self,name):
		return (name == "com.sun.star.frame.ProtocolHandler")
	# XServiceInfo
	def getImplementationName(self):
		return PROTOCOLHANDLER_IMPLEMENTATIONNAME
	# XServiceInfo
	def getSupportedServiceNames(self):
		return ("com.sun.star.frame.ProtocolHandler",)

XDispatch インターフェースなど Edit

コントロールが実行されたり、コントロールのイベントの通知を処理するために利用するクラスです。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
# dispatch base
class BaseDispatch(unohelper.Base,
	XDispatch, 
	XControlNotificationListener):
	
	def __init__(self,ctx,frame):
		self.ctx = ctx
		self.smgr = ctx.ServiceManager
		self.frame = frame
		self.doc = None
		controller = self.frame.Controller
		if controller:
			self.doc = controller.Model
	
	

コントロールが実行されるときに呼ばれる dispatch メソッドです。button はボタンがマウスでクリックされたときに、edit はテキストフィールドでエンターキーが押されたときに実行されます。このときの二番目の引数は com.sun.star.beans.PropertyValue のシークエンスです。実行時の引数関してはコントロール実行時引数を参照してください。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
	# XDispatch
	# do dispatch when control actioned
	def dispatch(self,aURL,args):
		if aURL.Protocol == HANDLED_PROTOCOL:
			if aURL.Path == "button":
				self.insertText()
				
			elif aURL.Path == "edit":
				self.insertText()
				
			elif aURL.Path == "spin":
				for arg in args:
					# value changed by the buttons of the spin control
					if arg.Name == "Value":
						value = int(arg.Value)
						ListenerHelper.setValue(
							self.frame,aURL.Path,value)
		return
	
	

ボタンが押されたとき、またはテキストフィールドでエンターキーが押されたときに insertText で文字列を Writer ドキュメントに書き込みます。スピンボタンが押されたときスピンコントロールが実行されたことになります。

コントロールの初期化と値の変更 Edit

ここでツールバーコントロールを初期化できます。コントロールにリスナーを追加するために addStatusListener メソッドが呼ばれます。そのとき XStatusListener が引数として渡さされます。コントロールへの変更はステータスの変更とにより行います。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
	# add status listener
	# this is called when the toolbar is instantiated
	def addStatusListener(self,xControl,aURL):
		if aURL.Protocol == HANDLED_PROTOCOL:
			if aURL.Path == "edit":
				aNamedValue = self.makeNamedValue("Text","string ")
				self.sendCommand(xControl,aURL,"SetText",(aNamedValue,))
				
			elif aURL.Path == "spin":
				aNamedValue1 = self.makeNamedValue("Value",1)
				aNamedValue2 = self.makeNamedValue("Step",1)
				aNamedValue3 = self.makeNamedValue("LowerLimit",1)
				aNamedValue4 = self.makeNamedValue("UpperLimit",100)
				#aNamedValue5 = self.makeNamedValue("OutputFormat","0.0")
				self.sendCommand(xControl,aURL,"SetValues",
					(aNamedValue1,aNamedValue2,aNamedValue3,aNamedValue4))
				
			ListenerHelper.addListener(self.frame,xControl,aURL.Path)
		return
	
	
	def removeStatusListener(self,xControl,aURL):
		ListenerHelper.removeListener(self.frame.xControl,aURL.Path)
		return
	
	

ステータス変更を com.sun.star.frame.FeatureStateEvent 構造体で知らせることで変更します。それぞれの値は次のようになります。

  • FeatureURL: 変更するコントロールの URL
  • IsEnabled: コントロールの有効化
  • Requery: XDispatch が必要かどうか
  • State: 変更後の新しい状態
  • Source: XDispatch インターフェース

State にコントロールコマンドを com.sun.star.frame.ControlCommand で指定します。

  • Command: コントロールを操作するコマンド
  • Arguments: 操作時の値

コマンドは各コントロールにより異なります。コントロールコマンドの項目を参照してください。

上記ではテキストフィールドに値を設定するために次のようにしています。

 if aURL.Path == "edit":
     aNamedValue = self.makeNamedValue("Text","string ")
     self.sendCommand(xControl,aURL,"SetText",(aNamedValue,))

コマンド "SetText" でテキストの変更を送信、引数は NamedValue で Name: Text、Value: "string " です。これを実行するとテキストフィールドに "string " の文字が設定されます。

コマンドの送信によるステータスの変更は次のメソッドで statusChanged メソッドを呼び出すことで実際に行います。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
	# send command to control herself
	def sendCommand(self,xControl,aURL,aCmd,Args,Enabled=True):
		aEvent = uno.createUnoStruct(
			"com.sun.star.frame.FeatureStateEvent")
		aControlCmd = uno.createUnoStruct(
			"com.sun.star.frame.ControlCommand")
		aControlCmd.Command = aCmd
		aControlCmd.Arguments = Args
		aEvent.FeatureURL = aURL
		aEvent.IsEnabled = Enabled
		aEvent.Requery = False
		aEvent.Source = self
		aEvent.State = aControlCmd
		
		xControl.statusChanged(aEvent)
		return
	
	

イベントによる操作の時にはコントロールの XStatusListener が直接利用できます。しかし、イベントが発生したコントロール以外の操作では保存しておいたコントロールの XStatusListener を利用します。sendCommandTo では変数に入れておいたものを利用してステータスの変更を指定したコントロールに通知します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
	# send command to the control indicated by aURL.Path
	def sendCommandTo(self,aURL,aCmd,Args,Enabled=True):
		aEvent = uno.createUnoStruct(
			"com.sun.star.frame.FeatureStateEvent")
		aControlCmd = uno.createUnoStruct(
			"com.sun.star.frame.ControlCommand")
		if aCmd != "":
			aControlCmd.Command = aCmd
			aControlCmd.Arguments = Args
			aEvent.State = aControlCmd
		else:
			aEvent.State = None
		aEvent.FeatureURL = aURL
		aEvent.IsEnabled = Enabled
		aEvent.Requery = False
		aEvent.Source = self
		
		ListenerHelper.notify(self.frame,aURL.Path,aEvent)
		return
	
	
	# create new com.sun.star.beans.NamedValue
	def makeNamedValue(self,name,value):
		aNamedValue = uno.createUnoStruct("com.sun.star.beans.NamedValue")
		aNamedValue.Name = name
		aNamedValue.Value = value
		return aNamedValue

コントロールのイベント Edit

コントロールでイベントが発生したときに呼ばれる controlEvent メソッドです。メソッドの引数は com.sun.star.frame.ControlEvent です。ツールバーのコントロールの値を取得できるのはここでのみです。そのため、ここで値の変更を受け取ったら、値を変数に保存しておきます。

テキストフィールド edit とスピンボタン spin の値の変更を受け取り、ListenerHelper に値を保存しています。このとき、フレーム、コントロールのパスに対して値を保持します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
	# XControlNotificationListener
	def controlEvent(self,Event):
		if Event.aURL.Protocol == HANDLED_PROTOCOL:
			if Event.aURL.Path == "edit":
				if Event.Event == "TextChanged":
					for named in Event.aInformation:
						if named.Name == "Text":
							ListenerHelper.setValue(
								self.frame,Event.aURL.Path,str(named.Value))
			
			elif Event.aURL.Path == "spin":
				if Event.Event == "TextChanged":
					for named in Event.aInformation:
						if named.Name == "Text":
							value = int(named.Value)
							if 0 < value <= 100:
								ListenerHelper.setValue(
									self.frame,Event.aURL.Path,value)
		return
	
	
	
	

コントロールが実行されたとき dispatch から利用するプライベート関数です。保存してあるコントロールの値を取り出して Writer ドキュメントに書き込みます。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
	# insert some text
	def insertText(self):
		txt = ListenerHelper.getValue(self.frame,"edit")
		value = int(ListenerHelper.getValue(self.frame,"spin"))
		if txt and value:
			self.WriteText(txt * value)
		return
	
	
	# insert text at the selected position
	def WriteText(self,txt):
		controller = self.frame.Controller
		if controller:
			doc = controller.getModel()
			text = doc.getText()
			viewCursor = controller.getViewCursor()
			viewCursor.setString(txt)
			viewCursor.collapseToEnd()
		return

UNO コンポーネントの登録 Edit

Py-UNO のコンポーネント登録のための情報です。unohelper.py で定義されている ImplementationHelper を利用して容易に登録情報を作成できます。addImplementation メソッドに、コンポーネントを実際に実装するクラス、実装名、サービス名 (タプルで) を与えます。

  0
  1
  2
  3
  4
# register information
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(
	TestProtocolHandler,
	PROTOCOLHANDLER_IMPLEMENTATIONNAME,
	("com.sun.star.frame.ProtocolHandler",),)

コントロール操作用コマンド Edit

操作用コマンドの一覧です。コマンドは OOo のソースコード framework/source/uielement 以下のファイルなどからから抜き出したものです。

コントロールコマンド引数説明
Buttonなし
ComboboxSetTextText = stringテキストを設定する
SetListList = []stringリストを設定する
AddEntryText = string項目を追加する
InsertEntryText = string,
Pos = long
項目を挿入する
RemoveEntryPosPos = long項目を削除する
RemoveEntryTextText = string項目を削除する
SetDropDownLinesLines = longドロップダウン行数を設定する
SetBackgroundColorColor = Long背景色を設定する
SetTextColorColor = Long文字色を設定する
DropdownboxSetListList = []stringリストを設定する
AddEntryText = string項目を追加する
InsertEntryText = string,
Pos = long
項目を挿入する
RemoveEntryPosPos = long項目を削除する
RemoveEntryTextText = string項目を削除する
SetDropDownLinesLines = longドロップダウン行数を設定する
DropdownButtonSetListList = []stringリストを設定する
CheckItemPosPos = longアイテムにチェックを入れる
AddEntryText = string項目を追加する
InsertEntryText = string,
Pos = long
項目を挿入する
RemoveEntryPosPos = long項目を削除する
RemoveEntryTextText = string項目を削除する
EditfieldSetTextText = string文字列を設定する
ImageButtonSetImageURL = string画像を変更する
SpinfieldSetStepStep = longステップ値を変更する
SetValueValue = long値を設定する
SetValuesValue = long,
Step = long,
LowerLimit = long,
UpperLimit = long,
OutputFormat = string
設定値を変更する
SetLowerLimitLowerLimit = long下限値を変更する
SetUpperLimitUpperLimit = long上限値を変更する
SetOutputFormatOutputFormat = string表示フォーマットを変更する
ToggleDropdownButtonSetListList = []stringリストを設定する
CheckItemPosPos = longアイテムにチェックを入れる
AddEntryText = string項目を追加する
InsertEntryText = string,
Pos = long
項目を挿入する
RemoveEntryPosPos = long項目を削除する
RemoveEntryTextText = string項目を削除する

コントロールのイベント Edit

コントロールで発生するイベント名と引数の一覧です。イベント名と引数のセットは com.sun.star.beans.NamedValue のシークエンスです。

これらは OOo のソースコード framework/source/uielement 以下のファイルなどからから抜き出したものです。

コントロールイベント引数説明
Button
ComboboxListChangedList = []stringリストの変更
TextChangedText = stringテキストの変更
FocusSetフォーカスイン
FocusLostフォーカスアウト
DropdownBoxListChangedList = []stringリストの変更
FocusSetフォーカスイン
FocusLostフォーカスアウト
DropdownButtonListChangedList = []stringリストの変更
PosItemChecked = long選択アイテムの変更
EditfieldTextChangedText = stringテキストの変更
FocusSetフォーカスイン
FocusLostフォーカスアウト
ImageButtonImageChangedURL = string画像の変更
SpinfieldTextChangedText = stringテキストの変更
FocusSetフォーカスイン
FocusLostフォーカスアウト
ToggleDropdownButtonListChangedList = []stringリストの変更
PosItemChecked = long選択アイテムの変更

コントロール実行時引数 Edit

コントロールが実行されたとき dispatch メソッドが呼ばれます。そのときの引数の値の種類一覧です。引数は com.sun.star.beans.PropertyValue のシークエンスです。

これらは OOo のソースコード framework/source/uielement 以下のファイルなどからから抜き出したものです。

コントロール引数説明
ButtonKeyModifire = longボタン動作時
ComboboxText = string,
KeyModifire = long
コンボボックス動作時
DropdownBoxText = string,
KeyModifire = long
ドロップダウンボックス項目選択時
EditfieldText = string,
KeyModifire = long
テキストフィールド動作時
ImageButtonKeyModifire = longボタン動作時
SpinfieldValue = long/double,
KeyModifire = long
スピン値変更時
ToggleDropdownButtonText = string,
KeyModifire = long
ボタン動作時

拡張機能パッケージの作成 Edit

UNO コンポーネント、アドオンツールバー、プロトコルハンドラを使えるようにするには拡張機能パッケージを作成して OpenOffice.org にインストールしなければいけません。

上記で作成した各ファイル以外に拡張機能パッケージに使うファイルを用意します。

各ファイルの詳細は拡張機能パッケージ?を参照してください。

description.xml Edit

拡張機能ファイルの識別子やバージョン、依存性を設定するファイルです。必ずしも必要というわけではありませんが作成するべきです。

  • 識別子 identifier: 拡張機能のパッケージに独自の ID を与えて他の拡張機能と区別するためのものです。ここでは mytools.complextoolbar.test としています。
  • バージョン version: 拡張機能マネージャが認識するパッケージのバージョンです。0.0.1
  • 依存性 dependencies: OOo のバージョンに依存する場合に利用します。複雑なツールバーの Addons.xcu ファイルは OOo 2.2 以降でサポートされてるので 2.2 以降としています。
  0
  1
  2
  3
  4
  5
  6
  7
  8
<?xml version="1.0" encoding="UTF-8"?>
<description xmlns="http://openoffice.org/extensions/description/2006"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:d="http://openoffice.org/extensions/description/2006">
  <identifier value="mytools.complextoolbar.test" />
  <version value="0.0.1" />
  <dependencies>
    <OpenOffice.org-minimal-version value="2.2" d:name="OpenOffice.org 2.2" />
  </dependencies>
</description>

META-INF/manifest.xml Edit

拡張機能パッケージに含まれるファイルの種類を拡張機能マネージャに教える必要があるファイルに関して記述します。

設定が必要なのは UNO コンポーネントのファイル、設定ファイル (Addons.xcu および ProtocolHandler.xcu) です。それぞれ次のような種類になります。

  • handler.py: application/vnd.sun.star.uno-component;type=Python
  • Addons.xcu: application/vnd.sun.star.configuration-data
  • ProtocolHandler.xcu: application/vnd.sun.star.configuration-data
  0
  1
  2
  3
  4
  5
  6
  7
<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest>
 <manifest:file-entry manifest:full-path="handler.py" 
  manifest:media-type="application/vnd.sun.star.uno-component;type=Python"/>
 <manifest:file-entry manifest:full-path="Addons.xcu" 
  manifest:media-type="application/vnd.sun.star.configuration-data"/>
 <manifest:file-entry manifest:full-path="ProtocolHandler.xcu" 
  manifest:media-type="application/vnd.sun.star.configuration-data"/>
</manifest:manifest>

パッケージ化 Edit

拡張機能パッケージは ZIP アーカイブです。ファイルの構成を次のように配置されるように ZIP ファイルを作成します。manifest.xml で指定した位置にファイルを配置します。

- handler.py
- Addons.xcu
- ProtocolHandler.xcu
- description.xml
- META-INF / - manifest.xml

ZIP アーカイブを作成したらファイル名を mytools_ComplexToolbar-0.0.1oxt などに変更します。

インストール Edit

作成したパッケージを OOo の拡張機能マネージャからインストールします。

アンインストールも同様に行います。

ツールバーの利用 Edit

Py-UNO で作成したコンポーネントを正しく動作させるためには OOo を再起動させます。クイックスタートを利用している場合は一度停止させます。

作成したツールバーは Writer でのみ表示されるコントロールで構成されているので、Writer を起動します。正しく表示され、すべてのコントロールが有効であればツールバーの作成およびプロトコルハンドラが正しく実装されていることになります。

ツールバーが表示されないときは Addons.xcu ファイルに不備があるかもしれません。 また、ツールバーが表示されるがコントロールが有効になっていないときは (初期化時に無効化していないとき)、プロトコルハンドラに不具合があるかもしれません。

パッケージ Edit

filemytools_ComoplexToolbar-0.0.1.oxt

コメント Edit

意味分からんといったコメントはここへ。



Attach file: filecomplextoolbar-1.png 267 download [Information] filemytools_ComoplexToolbar-0.0.1.oxt 671 download [Information]

Reload   New Lower page making Edit Freeze Diff Upload Copy Rename   Front page List of pages Search Recent changes Backup   Help   RSS of recent changes