前の関連記事:Calc(56)追加できるリスナー一覧: その8
lockControllers()メソッドとaddActionLock()メソッド
XModelのlockControllers()メソッドは
(2018.2.12追記。lockControllers()でロックされるのは画面の更新の部分だけのようで、コントローラでシートの操作はlockControllers()したままでできました。Calc(76)画面の更新をせずにコントローラでシートを操作する参照。)
unlockControllers()で更新の通知を再開します。
ドキュメントはlockControllers()をもっています(Calc(5)Calcドキュメントのサービスとインターフェイス一覧)。
XActionLockableのaddActionLock()メソッドはそのメソッドをもつオブジェクトの更新を保留にします。
フレーム、ドキュメント、セルがaddActionLock()メソッドをもっています(Calc(25)フレームのサービスとインターフェイス一覧、Calc(5)Calcドキュメントのサービスとインターフェイス一覧、Calc(7)セルのサービスとインターフェイス一覧)
ドキュメントはlockControllers()メソッドとaddActionLock()メソッドをもっているのでこれらの速度を比較してみました。
(2018.2.13追記。lockControllers()メソッドとaddActionLock()メソッドの違いを書いたものをようやく見つけました。
[Solved] How to disable screen update, change calculate mode (View topic) • Apache OpenOffice Community Forum
lockControllers()メソッド 画面を更新しない。
addActionLock()メソッド 再計算をしない。
lockControllers()メソッドが「画面を更新しない」ということはデベロッパーガイドにも書いてありました(Models - Apache OpenOffice Wiki)。
addActionLock()メソッドは、オブジェクト内部の更新を止める、ので「再計算をしない」に限らないのでCalc(76)画面の更新をせずにコントローラでシートを操作するでlockControllers()メソッドと同じ結果になったようです。)
lockControllers()やaddActionLock()での速度比較するマクロ
[OpenOffice Basic] シートへの高速なアクセス
このBasicの例をPythonにして比較しました。
#!/opt/libreoffice5.4/program/python # -*- coding: utf-8 -*- import unohelper # オートメーションには必須(必須なのはuno)。 import time, math from com.sun.star.awt import XEnhancedMouseClickHandler from com.sun.star.awt import MouseButton # 定数 from com.sun.star.sheet import CellFlags as cf # 定数 def macro(documentevent=None): # 引数は文書のイベント駆動用。OnStartAppでもDocumentEventが入るがSourceはNoneになる。# import pydevd; pydevd.settrace(stdoutToServer=True, stderrToServer=True) # デバッグサーバーを起動していた場合はここでブレークされる。import pydevdは時間がかかる。 doc = XSCRIPTCONTEXT.getDocument() if documentevent is None else documentevent.Source # ドキュメントのモデルを取得。 controller = doc.getCurrentController() # コントローラーの取得。 sheet = controller.getActiveSheet() sheet.clearContents(cf.VALUE+cf.DATETIME+cf.STRING+cf.ANNOTATION+cf.FORMULA+cf.HARDATTR+cf.STYLES) # セルの内容を削除。 sheet["A1:D1"].setDataArray((("EachCell", "addActionLock", "lockControllers", "setDataArray"),)) sheet["A1:D1"].getColumns().setPropertyValue("OptimalWidth", True) # セルカーサーのセル範囲の列幅を最適化する。 controller.addEnhancedMouseClickHandler(EnhancedMouseClickHandler(doc, sheet)) # EnhancedMouseClickHandler class EnhancedMouseClickHandler(unohelper.Base, XEnhancedMouseClickHandler): def __init__(self, doc, sheet): self.args = doc, sheet def mousePressed(self, enhancedmouseevent): target = enhancedmouseevent.Target # ターゲットを取得。 if enhancedmouseevent.Buttons==MouseButton.LEFT: # 左ボタンのとき if enhancedmouseevent.ClickCount==2: # ダブルクリックの時 if target.supportsService("com.sun.star.sheet.SheetCell"): # ターゲットがセルの時。 txt = target.getString() doc, sheet = self.args sheet.clearContents(cf.VALUE+cf.DATETIME+cf.STRING+cf.ANNOTATION+cf.FORMULA+cf.HARDATTR+cf.STYLES) # セルの内容を削除。 sheet["A1:D1"].setDataArray((("EachCell", "addActionLock", "lockControllers", "setDataArray"),)) x = -3.14 y = 3.14 n = 30000 d = (y-x)/n if txt=="EachCell": start = time.perf_counter() for i in range(1, n+1): k = x + d*i sheet[i, 0].setValue(k) sheet[i, 1].setValue(math.sin(k)) sheet[1, 2].setValue(i) end = time.perf_counter() elif txt=="addActionLock": start = time.perf_counter() doc.addActionLock() for i in range(1, n+1): k = x + d*i sheet[i, 0].setValue(k) sheet[i, 1].setValue(math.sin(k)) sheet[1, 2].setValue(i) doc.removeActionLock() end = time.perf_counter() elif txt=="lockControllers": start = time.perf_counter() doc.lockControllers() for i in range(1, n+1): k = x + d*i sheet[i, 0].setValue(k) sheet[i, 1].setValue(math.sin(k)) sheet[1, 2].setValue(i) doc.unlockControllers() end = time.perf_counter() elif txt=="setDataArray": start = time.perf_counter() rows = [(x+d*i, math.sin(x+d*i)) for i in range(1, n+1)] sheet[1:len(rows)+1, 0:len(rows[0])].setDataArray(rows) sheet[1, 2].setValue(len(rows)) end = time.perf_counter() sheet[2, 2].setString(txt) sheet[3, 2].setString("Finished: {}s".format(end-start)) return False # セル編集モードにしない。 return True # Falseを返すと右クリックメニューがでてこなくなる。 def mouseReleased(self, enhancedmouseevent): return True def disposing(self, eventobject): pass g_exportedScripts = macro, #マクロセレクターに限定表示させる関数をタプルで指定。算出した値を3万x2個とカウンタ(C2)セル、合計9回セルに書き込む時間を計測するEachCellモード、ドキュメントのaddActionLock()メソッドでドキュメントの更新を保留するaddActionLockモード、ドキュメントのlockControllers()メソッドでコントローラーへの更新の通知を保留にするlockControllersモード、XCellRangeDataのsetDataArray()で一括に書き込むsetDataArrayモードの4種類の実行速度が計測できます。
setDataArrayモードは最後にカウンタを書き込むだけなので、実質6万個のセルに書き込むだけになります。
マクロを起動するとこれら4つのモード名が入ったセルが表示されるので各セルをダブルクリックするとそれぞれのモードで計測を開始します。
comparetimes.ods
このマクロを埋め込んだCalcドキュメントです。
ドキュメントを開くと上記のマクロが起動するようになっていますので、4つのモード名が入ったセルが出現します。
各モードの測定結果
VPCJ2にWindows10をクリーンインストールのこのハードで実行しました。
まずVirtualBox5.1.30のゲストOSのlinuxBean14.04で起動したLibreOffice5.4.1.2での結果です。
C2セルに書き出した行数、C3セルに実行したモード、C4セルに実行秒を表示しています。
EachCellモードはかなりの時間がかかるので実行するには覚悟が必要です。
C2セルに逐一カウンタが出力されるはずなのですが、そうはならず最後に一括して結果が表示されました。
それは他のモードでもすべて同じ結果でした。
マクロセレクターから計測するように実行したときは結果が表示されるまではマクロセレクターウィンドウが表示されたままでした。
結局結果がすべてでてから一括して書き込まれるのであれば、このモードを使う意味は全くないことになります。
AddActionLockモードではEachCellよりはだいぶ速くなりました。
lockControllersモードはAddActionLockモードでもほとんど同じ結果になります。
グラフ表示などしているときはすべての更新を保留にするlockControllersモードの方がより速くなるのかもしれません。
setDataArrayモードでは瞬時に結果が表示されます。
setDataArrayモードが圧倒的に速いというのは想定通りの結果ですが、EachCellモード、AddActionLockモード、lockControllersモードが同じ表示結果なのにこれほど速度が異なるのは想定外でした。
今度はホストOSのWindows10 Home 64bitでの結果です。
いずれにせよ多数のセルに書き込む必要があるときはsetDataArray()メソッドを使うのがよいことになります。
参考にしたサイト
OOoBasic/Generic/lockControllers - ...?
lockControllers()の解説。
OOobbs2/56 - ...?
addActionLock()の使用例。
[OpenOffice Basic] シートへの高速なアクセス
addActionLock()の書き込み速度比較のBasicの例。
[Solved] How to disable screen update, change calculate mode (View topic) • Apache OpenOffice Community Forum
lockControllers()メソッドとaddActionLock()メソッドがでてくるQ&A。
0 件のコメント:
コメントを投稿