これは LibreOffice Technology Advent Calendar 2024 の10日目の記事です。
セル内の数式で自作マクロの関数を使いたくなることがたまにあります。 たまにやろうとすると「えーっと、どうやるんだっけ……」となって手間取ってしまうので、サッとできるようにメモしておきます。
それぞれの方法にメリット・デメリットがありますから、「Basic マクロを使うことで逆に大変になってしまった」とならないようにうまく使っていきたいものですね。
準備: セキュリティレベルの設定
デフォルトではセキュリティレベルが「高」になっていてマクロが実行できません。 まずはセキュリティレベルを「中」に変更します。
(セキュリティレベルを「高」のままにして署名を使う方法もあるようですがこの記事では扱いません)
- メニュー>ツール>オプション
- LibreOffice>セキュリティ>マクロセキュリティ
- 「セキュリティレベル」タブ
- 「中」に変更する
設定変更したら
- メニュー>ファイル>再読み込み
でマクロを有効化します。
Basicマクロで関数を書く
- メニュー>ツール>マクロ>マクロの管理>Basic...
または
- メニュー>ツール>マクロ>マクロの編集
からマクロのエディタウィンドウを開きます。
参考: 【LibreOfficeマクロ】Editor起動 | 新人・若手SEスキルアップNavi
例として身長(cm)と体重(kg)を受け取って BMI を算出し、さらに BMI を元に適当にクラス分けする関数を書いてみました。
(注意: 以降のコード例では変数名などに snake case を使っていますが、単に私の好みでそうしているだけです。LibreOffice Basic では変数名などは camel case にするのが慣例のようです。念の為)
option explicit function bmi(weight_kg, height_cm) ' 身長を cm から m に変換 dim height_m : height_m = height_cm / 100 bmi = weight_kg / (height_m * height_m) end function function bmi_class(weight_kg, height_cm) dim rv ' return value dim bmi_val : bmi_val = bmi(weight_kg, height_cm) ' このクラス分けは適当なもの if bmi_val < 20 then rv = "A" elseif bmi_val < 30 then rv = "B" elseif bmi_val < 40 then rv = "C" else rv = "?" end if bmi_class = rv end function
マクロとして書いた関数は Calc のセルの数式内でも関数として使えるようになります。
引数としてセル参照を指定すると、関数にはセルの値が渡されます。このあたりは Calc の数式で使える普通の関数と同じ挙動ですね。
うまく動かないときの対処:
- 再計算されない(数式の処理結果が変わらない)
- メニュー>データ>計算>再計算(F9)
- 新しく追加した関数、リネームした関数が認識されていない場合など?
- メニュー>ファイル>再読み込み
マクロから Calc の関数を呼ぶ
例として FLOOR 関数(床関数)を扱います。
Calc のセル内の数式であれば
= FLOOR( 4.9 )
と書けば計算してくれますが、この FLOOR は Calc の関数であって Basic マクロの組み込み関数ではなく、Basic マクロでは使えません。
たとえば Basic マクロで以下のように書いても(関数が未定義なため)エラーになってしまいます。
dim a : a = FLOOR( 4.9 )
そこでどうするかというと、以下のヘルプページに情報があります。
ヘルプページに書かれている通りですが、 Calc の FLOOR 関数を呼び出す場合は以下のようになります。
function my_floor(value) as integer dim oService as object oService = CreateUnoService("com.sun.star.sheet.FunctionAccess") my_floor = oService.callFunction("FLOOR", Array(value)) end function
テンプレート
option explicit function my_func() my_func = ... ' {関数名} = ... で返り値を設定する end function ' エラーハンドリング function my_func2() on local error goto err__my_func2 ' ... 処理 ... err__my_func2: ' エラー発生時の処理 end function ' 途中で return する場合 function my_func3() ' ... my_func3 = ... exit function ' ... end function ' Calc の関数を使うための共通処理 function call_calc_func(func_name as string, values as array) dim service as object service = CreateUnoService("com.sun.star.sheet.FunctionAccess") call_calc_func = service.callFunction(func_name, values) end function
よく使うもの
自分がよく使いそうなもの。
整数を入力して時刻に変換
時刻の入力を何度も繰り返す場合、いちいちコロン :
を打つのが面倒なんですよね……。
1234
(整数または文字列)と入力したら自動的に 12:34
(時刻)に変換してほしい。
option explicit function hm_to_time(arg) on local error goto err__hm_to_time dim rv as double dim n as double dim h, m as integer select case typename(arg) case "String" : n = cint(arg) case else : n = arg end select h = calc_floor(n / 100) m = n mod 100 rv = ((h * 60) + m) / (60 * 24) hm_to_time = rv exit function err__hm_to_time: hm_to_time = "ERROR!" end function function calc_floor(value) as integer calc_floor = call_calc_func("FLOOR", Array(value)) end function function call_calc_func(func_name as string, values as array) dim service as object service = CreateUnoService("com.sun.star.sheet.FunctionAccess") call_calc_func = service.callFunction(func_name, values) end function
まあこれは
=( FLOOR(A1 / 100) * 60 + MOD(A1, 100) ) / (60 * 24)
でもできるので例としてはちょっと微妙かもですね。