Calc(85)データを保持するグリッドコントロール

2018-02-25

旧ブログ

t f B! P L
グリッドコントロールの表示しているダイアログを閉じるときにグリッドコントロールの内容を非表示のシートに保存してデータを保持するようにしました。

前の関連記事:Calc(84)LabelRangesのサービスとインターフェイス一覧


データを保持するグリッドコントロールのマクロ


GridControl/gridcontrol3.py at develop · p--q/GridControl

Calc(78)グリッドコントロールの行をマウスでコピペするをいじくって、グリッドコントロールの行を保存するだけでなく、並び替え機能も追加したり、モダルダイアログでも閉じたときにポップアップメニューにつけたMenuListenerを削除しています(119-121行目)。

また並び替え機能はグリッドコントロールの第一列をキーにしているのでTimeとDateを入れ替えました。

LibreOffice5(72)Javaの例:GUIをPythonにする その5のmenuCreator()、LibreOffice5(69)Javaの例:GUIをPythonにする その2のshowModelessly()とdialogCreator()、Calc(78)グリッドコントロールの行をマウスでコピペするのinsertRows()、XWidth()、YHeigh()はそのまま使っています。
def saveGridRows(doc, gridcontrol, rangename):  # グリッドコントロールの行をhistoryシートのragenameに保存する。  
 griddatamodel = gridcontrol.getModel().getPropertyValue("GridDataModel")  # GridDataModel
 datarows = [griddatamodel.getRowData(i) for i in range(griddatamodel.RowCount)]  # グリッドコントロールの行のリストを取得。
 namedranges = doc.getPropertyValue("NamedRanges")  # ドキュメントのNamedRangesを取得。
 if not rangename in namedranges:  # 名前がない時。名前は重複しているとエラーになる。
  sheets = doc.getSheets()  # シートコレクションを取得。
  sheetname = "history"  # 履歴シート名。
  if not sheetname in sheets:  # 履歴シートがない時。
   sheets.insertNewByName(sheetname, len(sheets))   # 履歴シートを挿入。同名のシートがあるとRuntimeExceptionがでる。
  sheet = sheets[sheetname]  # 履歴シートを取得。
  sheet.setPropertyValue("IsVisible", False)  # 非表示シートにする。
  emptyranges = sheet[:, :2].queryEmptyCells()  # 2列目までの最初の空セル範囲コレクションを取得。
  if len(emptyranges):  # セル範囲コレクションが取得出来た時。
   emptyrange = emptyranges[0]  # 最初のセル範囲を取得。
   emptyrange[0, 0].setString(rangename)
   namedranges.addNewByName(rangename, emptyrange[0, 1].getPropertyValue("AbsoluteName"), emptyrange[0, 1].getCellAddress(), 0)  # 2列目のセルに名前を付ける。名前、式(相対アドレス)、原点となるセル、NamedRangeFlag
 namedranges[rangename].getReferredCells().setString(json.dumps(datarows,  ensure_ascii=False))  # Grid1という名前のセルに文字列でリストを出力する。
def getSavedGridRows(doc, rangename):  # グリッドコントロールの行をhistoryシートのragenameから取得する。 
 namedranges = doc.getPropertyValue("NamedRanges")  # ドキュメントのNamedRangesを取得。 
 if rangename in namedranges:  # 名前がある時。
  txt = namedranges[rangename].getReferredCells().getString()  # 名前が参照しているセルから文字列を取得。
  if txt:
   try:
    return json.loads(txt)
   except json.JSONDecodeError:
    pass
 return None # 保存された行が取得できない時はNoneを返す。
関数saveGridRows()でドキュメントとグリッドコントロール、行データを保存するセルの参照名を引数にして、historyという名前のシートに、グリッドコントロールの行のリストを保存しています。

シートにPythonのオブジェクトはそのまま保存できないので、JSON形式にしてセルに書き込んでいます(164行目)。

historyシート上で直接保存データをみるときも考えて、json.dumps()でensure_ascii=Falseにして日本語文字もそのまま表示するようにしています。

ensure_ascii=Falseを指定しないときは日本語文字は\\u3042といったコードになってシートに書き込まれてしまいます。

セルを名前で参照するとシートを選択しなくてもドキュメントから名前だけでアクセスできるので便利です。

関数getSavedGridRows()でドキュメントとセルの参照名を引数にして行のリストを取得します。

セルの参照名がないときやセルから取得した文字列がJSONでデコードできなかったときはNoneを返します。

GridControl3.ods

このマクロを埋め込んだCalcドキュメントです。

GridControl3.odsの実行結果



青色のセルのダブルクリックでノンモダルダイアログ、黄色のセルをダブルクリックでモダルダイアログとして開きます(LibreOfficeのカラーパレットの「青色」は青紫色に見えますけど)。


色のついたセルをダブルクリックするとグリッドコントロールを載せたダイアログがでてきます。

一番最初にダイアログを表示させたときは保存しているデータはないのでダイアログを開いたときの時刻と日付が1行目に入っています。

Closeボタンを右クリックしてAddを選択するとテクストボックスの値がグリッドコントロールの行に追加されます。

すでに行が選択されているときはそこに行を挿入します。


右側のテクストボックスをダブルクリックするとポップアップメニューがでてくくるのでNowを選択すると左のテキストボックスの現在時刻、右のテクストボックスに今日の日付が入ります。


テキストボックスには好きな値が入れれますので日本語を入力してAddすればグリッドコントロールに日本語が追加されます。

ダイアログを閉じてまた開くとグリッドコントロールの行には同じ値が入っています。

行のデータは非表示のシートに保存してあるので、ドキュメントを保存せずに閉じるとグリッドコントロールのデータも保存されません。

保存されているグリッドコントロールのデータをみる


保存されているグリッドコントロールのデータをみるには、メニューのシート→シートの表示。


非表示のシートににあるhistoryというシートを選択してOK。


データは2列目の一番上の空きセルにJSON形式で入っています。

このセルには名前がつけてあり、その名前をその左のセルに表示しています。


メニューからシート→名前付き範囲または式→管理で、名前をつけたセルのアドレスがわかります。

グリッドコントロールの行を選択したときに発火するリスナーの問題点


310行目のGridSelectionListener(unohelper.Base, XGridSelectionListener)は、グリッドコントロールの行を選択したときに発火するリスナーです。

グリッドコントロールのaddSelectionListener()メソッド(addGridSelectionListener()メソッドではなく)でグリッドコントロールに追加します。

グリッドコントロールに行を追加すると追加した行を選択することになるのでGridSelectionListenerのselectionChanged()メソッドが発火します。

しかし2回も発火します。

1回目の発火ではgridselectioneventのSelectedRowIndexesアトリビュートはByteSequenceが入っておりFalseになります。

2回目の発火時のSelectedRowIndexesアトリビュートには挿入した行のインデックスが入ったタプルが入っています。

また、selectionChanged()メソッドをEclipse: PyDevメモ: LibreOfficeのPythonマクロのデバッグの方法でブレークするとマウスのクリックが一切使えなくなりました。

そのときはEclipse: PyDevメモ: LibreOfficeのPythonマクロのリスナーの問題点にあるようにショートカットキーですべて操作しないといけません。

行が存在しないグリッドコントロールにGridSelectionListenerを追加し、そのあとに追加した行を選択したところ、SelectedRowIndexesに負の数値が返ってきました。

例えば0行のグリッドコントロールに4行追加したあとに2行目を選択した時SelectedRowIndexesは(-2, -1, 0, 1)が入っていました。

なので、上のマクロでは保存データがないときでも必ず1行は追加するようにしています。

 DefaultGridDataModelのgetRowData()メソッドの引数に負の値を渡すとLibreOfficeがクラッシュしました。

参考にしたサイト


19.2. json — JSON エンコーダおよびデコーダ — Python 3.5.3 ドキュメント
json.dumps()でオブジェクトを文字列に、json.loads()でオブジェクトに戻せました。

次の関連記事:Calc(86)ActionTriggerContainer、ActionTrigger、ActionTriggerSeparatorのサービスとインターフェイスの一覧

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ