Libre Calc Macro : Python threadを使う (改定版)

Libre Calc

以前、Python Threadについて調査したのですが、もっと簡単に対応できることが分かったので、差し替えます。
少し考えればわかることだったのですが、何分経験がないので、見逃していました。

本文書では、問題部分のみ抜粋して、改善策を記載します。

■ テストプログラム(再掲載)

動作確認を行うためのPythonスクリプトとシートです。
Pythonスクリプト:heartbeat_start()を実行すると、シート上のheartbeat1がカウントアップします。カウントアップ中にthread起動/停止フラグを0にセットするとスレッドを停止します。

〇 テストシート

〇 Pythonコード(対象部分のみ抜粋)

下記コードは、長くなるので、主要部分だけをぬきだしてあります。
削った部分のコードは、以下のセルアクセスIFです。
・prop_ctrl_xxx()は、thread起動/停止フラグの取得、設定を行います。
・prop_heartbeat1_xxx()は、heartbeat1の取得、設定を行います。
・prop_heartbeat2_xxx()は、heartbeat2の取得、設定を行います。
・prop_heartbeat3_xxx()は、heartbeat3の取得、設定を行います。

# coding: utf-8
from __future__ import unicode_literals

import uno
import threading
import time


#
#	purpose	:	entry to count heartbeat.
#
def heartbeat_monitor():
	heartbeat = 0

	while(True):
		if (prop_ctrl_get() == 0):
			break
	
		prop_heartbeat1_set(heartbeat)
		heartbeat = heartbeat + 1
		time.sleep(0.5)

#
#	purpose	:	entry to start heartbeat thread
#
def heartbeat_start(*args):
	prop_ctrl_set(1)
	prop_heartbeat1_set(0)
	prop_heartbeat2_set(0)
	prop_heartbeat3_set(0)

	th = threading.Thread(target=heartbeat_monitor)
	th.setDaemon(True)
	th.start()

■ 実行結果

Pythonスクリプトを実行した後は、普通に動作しています。
しかし、別のLibre Office文書を開く、または、閉じるとthreadが停止してしまいました。
原因は、XSCRIPTCONTEXT.getDocument()でドキュメントを取得してセルにアクセスしていることにありました。
アプリ起動時、終了時には、この参照でExceptionを起こしてしまうみたいです。

XSCRIPTCONTEXT.getDocument()を参照しないようにすればいいと思いますが、Calcのマクロである以上は、なかなか難しいような気がします。
しかも、サンプルでは、Pythonの外部変数の代わりにセルを参照させているので、回避不能です。



この部分の考察が甘かったです。
XSCRIPTCONTEXT.getDocument()を参照しないようにではなくて、SCRIPTCONTEXT.getDocument()が参照できるうちにdocを取得して、使いまわせばいいだけでした。
このことに気づいたのは、別件(後日公開予定)を調べていた時でした。
お勉強中の素人ですので、ご勘弁いただければと思います。

■ Libre文書オープン時のthread停止対策

対策したコードを掲載します。
対策を青文字でマーキングしました。XSCRIPTCONTEXT.getDocument()を取得可能なタイミングでのみ取得しています。
threadにもdocを引き回せばいいと思ったのですが、なぜか、それは動作しなかったので、thread起動直後に取得するようにしました。
少しタイミングが危ないかもしれないですが、今のところ問題は起きてなさそうです。

import uno
import threading
import time


#
#	purpose	:	entry to count heartbeat.
#
def heartbeat_monitor():
	doc = XSCRIPTCONTEXT.getDocument()
	heartbeat = 0

	while(True):
		if (prop_ctrl_get(doc) == 0):
			break
	
		prop_heartbeat1_set(doc, heartbeat)
		heartbeat = heartbeat + 1
		time.sleep(0.5)

#
#	purpose	:	entry to start heartbeat thread
#
def heartbeat_start(*args):
	doc = XSCRIPTCONTEXT.getDocument()
	prop_ctrl_set(doc, 1)
	prop_heartbeat1_set(doc, 0)
	prop_heartbeat2_set(doc, 0)
	prop_heartbeat3_set(doc, 0)

	th = threading.Thread(target=heartbeat_monitor)
	th.setDaemon(True)
	th.start()

あと、これに付随して安全性を高めるために、Basic呼び出し側も改善を施しました。
こちらも青字でマーキングします。
なにをやっているかというと、XSCRIPTCONTEXT.getDocument()が失敗しないようにPython呼び出し直前に青字を実行して、シートをアクティブにして失敗を防止しています。

'
'	purpose	:	モニタースレッドの起動
'
sub heartbeat_start()
	Dim pv as object
	Dim sc as object

	on error goto fatal_error
	pv = ThisComponent.getScriptProvider()
	sc = pv.getScript("vnd.sun.star.script:test.py$heartbeat_start?language=Python&location=user")
	ThisComponent.getCurrentController().getFrame().Activate()
	sc.invoke(array(), array(), array())
fatal_error:
end sub

■ サンプルプログラム

上記の修正したサンプルプログラムを掲載します。
自己責任での御使用をお願いいたします。



タイトルとURLをコピーしました