LibreOffice5(84)Javaの例:GUIをPythonにする その9

2017-09-16

旧ブログ

t f B! P L
LibreOffice5(82)Javaの例:GUIをPythonにする その8の続きです。UnoDialogSample.javaはダイアログにさまざまなコントロールを載せる例です。

前の関連記事:LibreOffice5(83)linuxBeanでは動くのにWindows10では動かないマクロ


unodialogsample.py: いろいろなコントロールを使ってみる


GUI/unodialogsample.py at 150c6e97138595715ae03efdf400f67791ab8c95 · p--q/GUI

(2018.1.2追記。LibreOffice5.4からsetPropertyValue()の引数にタプルが使えなくなったのでsetattr()を使うように変更しました。unodialogsample.py
unodialogsample.odsはこのマクロを埋め込んだCalcドキュメントです。)
(2018.1.28追記。このマクロでは各コントロールの座標を指定していますが、この方法はかなり面倒なのでLibreOffice5(136)Map AppFont (ma)とピクセルと1/100mmの変換のマクロでは隣のコントロールの位置と大きさから算出するようにしています。)


UnoControlDialog上にたくさんのコントロールを載せた例です。

UnoControlFixedText  ラベルフィールド

UnoControlCurrencyField 通貨フィールド

UnoControlFixedLine 線

UnoControlEdit テキストボックス

UnoControlTimeField 時刻フィールド

UnoControlDateField 日付フィールド

UnoControlGroupBox グループボックス

UnoControlPatternField パターンフィールド

UnoControlNumericField 番号フィールド

UnoControlCheckBox チェックボックス

UnoControlRadioButton オプションボタン

UnoControlListBox リストボックス

UnoControlComboBox コンボボックス

UnoControlFormattedField 書式指定されたフィールド

UnoControlScrollBar スクロールバー

UnoControlFileControl ファイルコントロール

UnoControlButton ボタン

UnoControlFixedHyperlink  ハイパーリンク

UnoControlRoadmap ロードマップコントロール

全部で19種類のコントロールを使っています。

Javaの例で載っているUnoControlProgressBarはPythonでは背景の枠がうまく表示できなかったので使っていません(95173 – Progress Bar Dialog not working correctly)。

ダイアログ上のプログレスバーに代わりEclipse: PyDevメモ: LibreOfficeのPythonマクロのデバッグのenableRemoteDebuggingではステータスバーのプログレスバーを使っています(OOoBasic/Generic/StatusIndicator - ...?)。

それぞれのコントロールのサービス名に対してその右側にダイアログエディタにでてくる名前を書きました。

ラベルフィールドとか番号フィールドとかオプションボタンという名前はコントロールのサービス名からは連想しにくいですね。

下線をつけたファイルコントロールと、ハイパーリンク、ロードマップコントロールはダイアログエディタのツールバーにはありませんでした。

コントロール設定メモ


それぞれのコントロールの設定についてはLibreOffice: Main Pageで上記のコントールのサービス名にModelとつけてコントロールモデルのサービスを検索して、そのプロパティをみればだいたいわかります。

UnoControlFixedText  ラベルフィールド

UnoControlFixedTextModelのNoLabelプロパティをTrueにしない場合は、ラベルの横(または~に続く文字)にアクセスキーが表示されます。

となるはずですが、なぜか最初に使っているFixedTextのThis code-sample demonstrates how to create various controls in a dialogだけはNoLabelプロパティをFalseにしたとしてもアクセスキーは表示されませんでした。

いろいろいじってみましたがその理由はわかりませんでした。

UnoControlEdit テキストボックス

UnoControlEditModelのEchoCharプロパティに文字を設定するとそのテキストボックスの文字が伏せ文字にできます。

問題はその伏せ文字にする文字の指定方法で、short型で指定しないといけません。

これはPythonの組み込み関数のord()を使うことで解決しました。

UnoControlPatternField パターンフィールド

UnoControlPatternFieldModelのEditMaskプロパティにPattern Field - Apache OpenOffice WikiOOoBasic/Dialog/PatternField - ...?で解説されているパターンで、入力する文字の種類を限定できます。

NNLLLLLLLL

Nは数字、Lは編集不可文字です。

LiteralMaskプロパティに予め表示する文字を設定します。

__.05.2007

入力可能文字のところはアンダースコアにしています。

何が入力できるのかわからないかもしれないので、HelpTextプロパティを設定してツールチップがでるようにしています。

UnoControlListBox リストボックス

UnoControlListBoxModelのSelectedItemsプロパティに指定したリストボックスの項目がデフォルトで選択されているはずなのですが、選択されていないときもあってあまり信用できません。

UnoControlComboBox コンボボックス

コンボボックスは選択した項目がテキストボックスに代入されないので、実質使用不可能(LibreOffice5.2、5.3、5.4.0で確認)です。

unodialogsample.pyではXItemListenerのitemStateChanged()メソッドの引数のItemEvent StructのItemIdの値を代入するようにしていますが、どの項目を選択しても同じ値が返ってきてしまいます。

SelectedやHighlightedの値も常に0ですので使えません。

Javaの例でもコンボボックスの選択した値がテキストボックスに入りませんでしたので、ランタイムダイアログに使う実装に問題があるのだと思います。

ちなみ、ダイアログエディタで作成したコンボボックスはちゃんと動きましたので、どうしても使いたいときはダイアログエディタで作成したダイアログを使うしかなさそうです。

UnoControlFormattedField 書式指定されたフィールド

UnoControlFormattedFieldModelのEffectiveValueの値を書式指定して表示するものです。
 numberformatssupplier = smgr.createInstanceWithContext("com.sun.star.util.NumberFormatsSupplier", ctx)  # フォーマットサプライヤーをインスタンス化。
 numberformats = numberformatssupplier.getNumberFormats()  # フォーマットサプライヤーからフォーマット一覧を取得。
 formatstring = "NNNNMMMM DD, YYYY"  # フォーマット。デフォルトのフォーマット一覧はCalc→書式→セル→数値でみれる。
 locale = Locale(Language="en", Country="US")  # フォーマット一覧をくくる言語と国を設定。
 formatkey = numberformats.queryKey(formatstring, locale, True)  # formatstringが既存のフォーマット一覧にあるか調べる。第3引数のブーリアンは意味はないはず。
 if formatkey == -1:  # デフォルトのフォーマットにformatstringがないとき。
  formatkey = numberformats.addNew(formatstring, locale)  # フォーマット一覧に追加する。保存はドキュメントごと?
EffectiveValue以外に、FormatsSupplierとFormatKeyも渡さないといけないので、それをこの7行で作成しています。

まずNumberFormatsSupplierサービスをインスタンス化しています。

getNumberFormats()でフォーマット一覧を取得しています。

今回はEffectiveValueをNNNNMMMM DD, YYYYという書式にします。

書式の指定方法は数の書式コード - LibreOffice Helpにあります。

これらは言語指定によって異なります。

今回は76行目で言語en-USを指定しているのでNumber Format Codes - LibreOffice Helpの書式コードを使っています。

フォーマット一覧のqueryKey()メソッドで書式がフォーマット一覧に既にあるのかを確認して、-1が返ってきたときは新しい書式なので、addNew()メソッドでフォーマット一覧に新しい書式を追加して戻り値のフォーマットキーを取得します。

書式は言語ごとに異なるのでqueryKey()やaddNew()では言語もLocale Structで指定しています。

フォーマット一覧はドキュメント単位で定義されています(Number Formats - Apache OpenOffice Wiki)。

unodialogsample.pyではEffectiveValueの値12348を言語en-USのNNNNMMMM DD, YYYY書式で表示しています。

書式指定されたフィールドが結果的にどのように表示されるかはCalcで簡単に確認できます。

CalcのセルにEffectiveValueにする値12348を入力します。

セルを右クリック→セルの書式設定。


このパネルで言語を「日本語」からen-US、つまり「英語(米国)」にします。

書式コードに「NNNNMMMM DD, YYYY」を入力します。


書式コード「NNNNMMMM DD, YYYY」は言語「英語(米国)」にすでにあるのでそれが自動的に選択されます。


OKするとセルの12348がSaturday, October 21, 1933に変化しています。

書式を解除するには、セルを右クリニック→直接設定した書式の解除、とします。


言語「日本語」には書式コード「NNNNMMMM DD, YYYY」がないので、カテゴリーは「ユーザー定義」になります。

UnoControlFileControl ファイルコントロール

「検索」ボタンがついてくるのでそれをクリックすると、すでに入力されたパスをデフォルトフォルダにしてファイル選択ダイアログが表示されます。


LibreOffice5(77)FilePickerのTemplateDescriptionを実行してみるで使ったFilePickerサービスによるファイル選択と違って、フィルターの設定などオプションは全く選べません。

Windows10でも同じ結果です。

UnoControlFixedHyperlink ハイパーリンク

ハイパーリンクはJavaの例にはなかったものです。

クリックするとURLプロパティに設定したページがデフォルトブラウザで開きます。

LibreOffice5.4.1.2と5.3.3.2でやってみると同じページが二つ開いてしまいます。

LibreOffice5.2.6.2ではちゃんと1つのページしか開きませんので、きっとバグですね。

URLにはhttps://をつけておかないといけません。

幅は調整しなくてもクリックできる幅は広がりませんでしたが、高さは調整しないとコントロールのHeightの部分だけクリックできてしまいます(OOoBasic/Dialog/Example16 - ...?))。

ActionListenerを付けるとクリックしてもデフォルトブラウザを起動できなくなりました。


UnoControlRoadmap ロードマップコントロール

LibreOffice5(70)Javaの例:GUIをPythonにする その3と違ってUnoControlRoadmapModelのCompleteプロパティをFalseにしているので、設定した項目の最後にドットの項目が追加されています。

unodialogsample.pyで使っているリスナー


unodialogsample.pyのコントロールにはいくつかリスナーをつけています。


XMouseListenerのmouseEntered()を使って、My Labelの上にマウスポインターをもっていったときにポインターを変化させています。

mouseEntered()に設定していれば、mouseExited()にポインターを戻すコードを書かなくてもリスナーを設定したコントロールから出るとポインターは元に戻ります。

ということは「Enter」というより「On」の意味のようです。


XTextListenerのtextChanged()で通貨フィールドの変更を感知して、変更後の値をメッセージボックスに表示します。

Eclipse: PyDevメモ: LibreOfficeのPythonマクロのリスナーの問題点で書いたように、textChanged()がなぜか4回も発火してしまうので、前値と比較してそこから変化したときだけメッセージボックスを表示しています。


XFocusListenerのfocusGained()で、タブキーを使って他のコントロールからフォーカスを得たときに、テキストボックスの伏せ文字を解除しています。

伏せ文字の解除にはUnoControlEditModelのEchoCharプロパティに0を設定すればできました。

XMouseListenerのmouseEntered()と違って、フォーカスを失っても自動的に元に戻らないので、focusLost()でフォーカスを失ったときに伏せ文字に戻しています。


XKeyListenerのkeyPressed()で、キーボードの入力を感知して、テキストボックスに文字を入力するとそのキーをその右側に表示します。

keyPressed()の引数のKeyEvent StructのKeyCharでキーボードから入力した文字が取得できます。

しかし文字でないもの、バックスペースとか、スペースとか、矢印ボタンなどのキーが押されたときはKeyCharでは、表示できないものが取得されてしまうので、KeyCodeを取得してそのうち、BACKSPACE, SPACE, DELETE, LEFT, RIGHT, HOME, ENDに相当コードのときだけ、代わりの文字列を表示するようにしています。

これらのキーコードは定数com.sun.star.awt.Keyにあります。

これらのキーコードでない時はKeyCharを取得しています。

KeyCharでは表示できない文字も取得されてしまうので、正規表現パターン[!\"#$%&'()=~|`{+*}<>?\-\^\\@[;:\],./\\\w]+でキーボードにある見える文字だけ取得するようにしています。

このパターンは私の持っているキーボードにある文字を順に打ち出して作成したものです。


XItemListenerのitemStateChanged()で、Enable Close dialog Buttonのチェックボックスをチェックを外すと下にあるClose Buttonを使えなくします。

UnoControlCheckBoxModelのTriStateプロパティをTrueにしているので、「チェックされていない」と「チェックされている」、以外に「知らない」という状態があって、コントロールのgetState()メソッドでそれぞれ0、1、2が返ってきます。


XSpinListenerのup()で、書式指定されたフィールドのスピンドルボタンの上をクリックするとそのフィールドの書式化する前の内部値をメッセージボックスに表示します。

これはつまりUnoControlFormattedFieldModelのEffectiveValueの値を表示しています。


XAdjustmentListenerのadjustmentValueChanged()で、スクロールバーを上下させたときに、その値をモードレスダイアログに表示します。

Eclipse: PyDevメモ: LibreOfficeのPythonマクロのリスナーの問題点で書いたようにスクロールバーのボタンをクリックしたときにXAdjustmentListenerのadjustmentValueChanged()メソッドが2回発火してしまうので、どうしても値が2つ表示されてしまいます。

モードレスダイアログのフレームには、XCloseListenerを付けています。

XCloseListenerのnotifyClosing()で、モードレスダイアログを閉じるとき、つまりフレームを閉じるときに、まずモードレスダイアログをdispose()しています。

そうしないと、またスクロールバーを動かしてもモードレスダイアログが再度表示されませんでした。

オブジェクトをdispose()をしてもNoneに変わるようではないようで、dispose()した後に、そのオブジェクトが入っていた変数にわざわざNoneを代入しています。

ちなみに、モードレスダイアログをdispose()せずにsetVisible(False)にしといて、またsetVisible(True)にしても再表示できませんでした(フレームを付け直すと表示できるかも(未確認))。

unodialogsample.pyで使っている汎用関数


showModelessly()、dialogCreator()はLibreOffice5(69)Javaの例:GUIをPythonにする その2ででてきたものです。

eventSource()はLibreOffice5(70)Javaの例:GUIをPythonにする その3ででてきたものです。

toDate(year, month, day)
def toDate(year, month, day):  # 日付のnamedtupleを返す
 struct = Date(Year=year, Month=month, Day=day)  # com.sun.star.util.Date
 class StructDate(namedtuple("StructDate", "Date y m d")):
  __slots__ = ()  # インスタンス辞書の作成抑制。
  def __str__(self):  # 文字列として呼ばれた場合に返す値を設定。
   return "{:0>4}-{}-{}".format(self.y, self.m, self.d)
 return StructDate(struct, year, month, day)  # namedtupleを返す
引数は西暦の年、月、日です。

月と日は1から始まります。

戻り値はDate、y、m、dの属性名をもつnamedtupleです。

属性名のDateはcom.sun.star.util.Date Struct、yは年、mは月、dは日を返します。

引数はPythonのdateオブジェクトと同じにしています。

この関数を作った目的は文字列で出力するときに、2017-9-5といった形式を得たかったことです。

unodialogsample.pyでは、日付フィールドのツールチップで文字列としての出力を使っています。

toTime(hour=0, minute=0, second=0, microsecond=None, tzinfo=None)
def toTime(hour=0, minute=0, second=0, microsecond=None, tzinfo=None):  # 時刻のnamedtupleを返す。tzinfoについては未対応。
 microsecond, flg = (0, False) if microsecond is None else (microsecond, True)  # flgはマイクロ秒の表示のためのフラグ。
 tzinfo = False if tzinfo is None else tzinfo
 struct = Time(Hours=hour, Minutes=minute, Seconds=second, NanoSeconds=microsecond*1000, IsUTC=tzinfo) # com.sun.star.util.Time
 class StructTime(namedtuple("StructTime", "Time h m s ms")):
  __slots__ = ()  # インスタンス辞書の作成抑制。
  def __str__(self):  # 文字列として呼ばれた場合に返す値を設定。
   return "{:>2}:{:0>2}:{:0>2}.{}".format(self.h, self.m, self.s, self.ms) if flg else "{:>2}:{:0>2}:{:0>2}".format(self.h, self.m, self.s)
 return StructTime(struct, hour, minute, second, microsecond)  # namedtupleを返す
引数は時、分、秒、ミリ秒、タイムゾーン、で、それぞれデフォルト値は0, 0, 0, None, Noneです。

戻り値はTime h m s msの属性名をもつnamedtupleです。

属性名のDateはcom.sun.star.util.Time Struct、hは時、mは分、sは秒、msはミリ秒を返します。

com.sun.star.util.Time Structはミリ秒ではなくて、そのさらに1/1000のナノ秒が使われていますが、Pythonのtimeオブジェクトに揃えてミリ秒を使っています。

この関数を作った目的は文字列で出力するときに、 9:08:10.002425といった形式を得たかったことです。

時は2桁ですが、09といったように先頭に0は付きません。

tzinfoは文字列の出力には使っていません。

unodialogsample.pyでは、時刻フィールドのツールチップで文字列としての出力を使っています。

参考にしたサイト


95173 – Progress Bar Dialog not working correctly
ダイアログではプログレスバーはPythonからはうまく使えないようです。

OOoBasic/Generic/StatusIndicator - ...?
ステータスバーのプログレスバーはPythonからも使えました。

OOoBasic/Dialog/Example16 - ...?
ハイパーリンクのBasicの例。

Pattern Field - Apache OpenOffice Wiki
パターンフィールドで使えるパターンの解説。

OOoBasic/Dialog/PatternField - ...?
パターンフィールドで使えるパターンの解説。

数の書式コード - LibreOffice Help
書式指定されたフィールドで使える書式コード。言語指定によって異なります。

Number Format Codes - LibreOffice Help
書式指定されたフィールドで使える書式コード。言語指定が英語の時。

Number Formats - Apache OpenOffice Wiki
書式コードのパターンはドキュメントごとの管理になるそうです。

passwords - How to remove the echoChar (TextField)? - Stack Overflow
UnoControlEditModelのEchoCharプロパティでも0にすることで解除できました。

ダイアログコントロールの詳細 - Apache OpenOffice Wiki
Basic用のダイアログコントロールの解説。

OOoBasic/Dialog - ...?
Javaの例にでてこなかったコントロールもまだたくさんありました。

次の関連記事:LibreOffice5(85)FileURLとSystemPathの変換方法

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ