前の関連記事:Calc(74)クリックした位置に出現するノンモダルダイアログ2
カラーパレットの色の名前と値の一覧を取得するマクロ
import unohelper # オートメーションには必須(必須なのはuno)。
import os
import glob
from itertools import zip_longest
from xml.etree import ElementTree
from com.sun.star.sheet import CellFlags as cf # 定数
from com.sun.star.table.CellHoriJustify import CENTER # enum
def macro(documentevent=None): # 引数は文書のイベント駆動用。import pydevd; pydevd.settrace(stdoutToServer=True, stderrToServer=True)
doc = XSCRIPTCONTEXT.getDocument() # 現在開いているドキュメントを取得。
ctx = XSCRIPTCONTEXT.getComponentContext() # コンポーネントコンテクストの取得。
pathsettingssingleton = ctx.getByName('/singletons/com.sun.star.util.thePathSettings') # thePathSettings
fileurls = pathsettingssingleton.getPropertyValue("Palette").split(";") # Paletteへのパスを取得。セミコロン区切りで複数返ってくるのでリストにする。
lst_socs = ["standard.soc", "chart-palettes.soc"] # 出力順を決まっているファイル名。
for fileurl in reversed(fileurls): # ユーザーフォルダにある方を先に取得するため逆順にする。
sheet = getNewSheet(doc, "Palette") # レイヤー毎にシートを作成する。
c = 0 # 出力列インデックス。
palettepath = os.path.normpath(unohelper.fileUrlToSystemPath(fileurl)) # システムパスに変換。
os.chdir(palettepath) # socファイルのあるフォルダに移動。
set_socs = set(glob.glob("*.soc")) # socファイルのリストを集合にして取得。
socs = lst_socs.copy()
socs.extend(set_socs.difference(lst_socs)) # ファイルの順番を変更。
xpath = './/draw:color'
namespaces1 = {"draw": "{http://openoffice.org/2000/drawing}"} # 名前空間の辞書。replace()で置換するのに使う。
replaceWithValue1, replaceWithKey1 = createReplaceFunc(namespaces1)
namespaces2 = {"draw": "{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}"} # drawはもうひとつの名前空間が割り当てられている。
replaceWithValue2, replaceWithKey2 = createReplaceFunc(namespaces2)
for socname in socs: # socファイルを取得。
if os.path.exists(socname):
tree = ElementTree.parse(socname) # xmlの木を取得。
xpath1 = replaceWithValue1(xpath) # 名前空間1の辞書のキーを値に変換。
nodes = tree.findall(xpath1) # xpahのノードを取得。
replaceWithKey = replaceWithKey1 # 名前空間1を戻す関数。
if not nodes: # ノードが取得出来なかった時。
xpath2 = replaceWithValue2(xpath) # 名前空間を2に変える。
nodes = tree.findall(xpath2) # xpahのノードを取得。
replaceWithKey = replaceWithKey2 # 名前空間2を戻す関数。
if nodes: # ノードが取得出来た時。
outputs = getAttrib(nodes, replaceWithKey)
rowsToSheet(sheet[2, c], outputs) # シートに書き込む。
sheet[1, c].setString(socname) # socファイル名を出力。
sheet[1, c:c+5].merge(True)
sheet[1, c:c+5].setPropertyValue("HoriJustify", CENTER)
rows = sheet[2:2+len(outputs), c+3].getDataArray() # 色の10進数を取得。
for i, row in enumerate(rows):
if row[0]!="": # 0の時もあるので空文字かどうかで判断する。
sheet[2+i, c].setPropertyValue("CellBackColor", int(row[0])) # floatで返ってくるのでintにしないといけない。律速。
c += 5
sheet["A1"].setString(palettepath)
sheet["A1:H1"].merge(True)
def getAttrib(nodes, replaceWithKey):
outputs = []
c = 0 # 行カウンタ。
for node in nodes: # 取得した各ノードについて。
name, color = "", ""
for key, val in node.items(): # ノードの各属性について。
attrib = replaceWithKey(key) # 名前空間の辞書の値をキーに変換。
if attrib=="draw:name":
name = val
elif attrib=="draw:color":
color = val.upper().replace("#", "0x") # Pythonの16進数にする。#を0xに変換する。
if name: # 色名が取得出来ている時。
if c==12: # 12行ずつ空行を挿入。
outputs.append(("",))
c = 0
outputs.append(("", name, color, int(color, 16))) # 出力行に追加。
c += 1
return outputs
関数createReplaceFunc()(Python(26)XPathでxmlの子要素からルートまで遡る方法)、getNewSheet()とrowsToSheet()(Calc(44)ファイルフィルターのオプションダイアログ)の表示は略しています。14行目でPaletteフォルダへのパスを取得しています。
セミコロンで区切ってsharedレイヤーとuserレイヤーのパスが返ってきます(LibreOffice5(75)thePathSettingsシングルトンで既定のパスを取得)。
色一覧はこのフォルダにあるsocファイルで定義してあり、そのxmlから色の英語名とその値を取得しています。
socファイルはxml形式で書いてあるので、ElementTreeのparse()でtreeにしています(29行目)。
socファイルの名前空間のdrawが2種類あるので、ノードを取得できなかたったときは名前空間を変えて再取得しています。
treeからgetroot()メソッドでルートノードを取得していましたが、そこから名前空間を取得する方法はわかりませんでした。
コンフィギュレーションと同様に層構造になっておりsharedレイヤーはuserレイヤーの設定に上書きされるはず(LibreOffice5(30)xcsファイルとxcuファイルとxcdファイル:その1)ですが、linuxBean14.04のLibreOffice5.4.1.2ではuserレイヤーに続いて同名のsharedレイヤーのパレットがまたでてきます。
同じLibreOffice5.4.1.2でもWindows10では同じ名前のカラーパレットはでてきませんが、userレイヤーにあるsocファイルはstandard.socだけです。
マクロを埋め込んだCalcドキュメント
ColorPalette3.ods
このマクロを埋め込んだCalcドキュメントです。
マクロセレクターからドキュメント内のColorPalette3内のmacroを実行するとsocファイルから色情報を抜き出してシートに出力します。
シート名Paletteにuserレイヤー、シート名Palette1にsharedレイヤーのsocファイルの内容が出力されるはずです。
セル一つずつに背景色を設定しているので、すごく時間がかかります。
マクロセレクターから呼び出したときは、すべての結果が出力されるまでマクロセレクターが表示されたままになります。
この埋め込みマクロにはオートメーションのためのコードも入れたままにしてあるので、マイマクロフォルダにマクロファイルを移動させて、オートメーションで実行するとセルの背景色が順に元に戻っていく様子が観察できます。
ただオートメーションではいつ変換が完了したのかがわかりません。
ColorPalette3.odsの実行結果
linuxBeanでの実行結果
Windows10での実行結果
カラーパレットの表示と同様に12色ごとに空行で区切りを入れています。
Blue 1とSky blue 1など違う色名なのに同じ色のものがあったりします。
odsファイルをGoogleスプレッドシートに変換すると色が変わる
odsファイルをGoogleスプレッドシートに変換したらセルの背景色に設定した色は変化してしまいました。
なので上記の実行結果ではGoogleスプレッドシートのマクロで元の色に戻してあります。
function myFunction() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets(); // シートコレクションを取得。
var firstrow = 3; // 行番号は1から始まる。
var firstcolumn = 1; // 列番号は1から始まる。
for (var s=0;s<sheets.length;s++) { // 各シートについて。
var sheet = sheets[s] // シートを取得。
var r = firstrow; // 最初の色のある行番号。
var c = firstcolumn + 2; // 最初の色のある列番号。
while (sheet.getRange(firstrow, c).getValue()) { // 色がなくなるまで右ブロックに探しに行く。
while (sheet.getRange(r, c).getValue()) { // 色列について色がなくなるまで次のブロックに探しに行く。
r += 13;
}
var lastrow = r - 2; // 最終ブロックの12行目を取得。
var values = sheet.getRange(firstrow, c, lastrow-firstrow+1, 1).getValues(); // 16進数の色を取得。
for (var i=0;i<values.length;i++) { // 取得した各行インデックスについて。
var colorhex = values[i][0].replace("0x", "#"); // #から始まる16進数を取得。
if (colorhex!="") { // 空文字でない時。0の時はtrueである必要がある。
sheet.getRange(firstrow+i, c-2).setBackground(colorhex); // 背景色を設定。
}
}
c += 5; // 右の色の列番号。
}
}
}
色の変化の規則をみつけられなかったので(RGB→BGRではない)、セルの16進数の値を取得して再設定しているだけです。
Calcのマクロで背景色を設定するより速く設定されているようでした。
GoogleスプレッドシートではCalcと違って、セルのアクセスには1から始まる行番号や列番号を使っているので配列と一緒に使うときは気をつけないといけません。
最終行や最終列を取得したかったのですが、空セルが間に挟まっているせいかシートのgetLastRow()メソッドやgetLastColumn()メソッドでは正しく最終行や最終列を取得できませんでした(getDataRange()メソッドでも同じ結果)。
例えばColorPalette3linuxBean.odsのPaletteシートではgetLastRow()では181、getLastColumn()では4が返ってきました。
getLastColumn()の4は最初の空列で終わりと判断したと推測できますが、getLastRow()の181は何を根拠にしているのかがわかりませんでした。
Calcのようにデータが入ったセル範囲コレクションを取得するといった方法(Calc(24)セルカーサーでセル範囲を変更する)もみつけられなかったので、while文でデータがどこまで入っているのかいちいち探しています。
セル範囲のgetValues()メソッドの戻り値はCalcと同様に行の配列が返ってきました。
シートからシートコレクションを取得しているのは理解しにくいです。
LibreOfficeと違ってgetSheets()の戻り値はfor sheet in sheetsでシートは取得できませんでした。
Calcのマクロで背景色を設定するより速く設定されているようでした。
GoogleスプレッドシートではCalcと違って、セルのアクセスには1から始まる行番号や列番号を使っているので配列と一緒に使うときは気をつけないといけません。
最終行や最終列を取得したかったのですが、空セルが間に挟まっているせいかシートのgetLastRow()メソッドやgetLastColumn()メソッドでは正しく最終行や最終列を取得できませんでした(getDataRange()メソッドでも同じ結果)。
例えばColorPalette3linuxBean.odsのPaletteシートではgetLastRow()では181、getLastColumn()では4が返ってきました。
getLastColumn()の4は最初の空列で終わりと判断したと推測できますが、getLastRow()の181は何を根拠にしているのかがわかりませんでした。
Calcのようにデータが入ったセル範囲コレクションを取得するといった方法(Calc(24)セルカーサーでセル範囲を変更する)もみつけられなかったので、while文でデータがどこまで入っているのかいちいち探しています。
セル範囲のgetValues()メソッドの戻り値はCalcと同様に行の配列が返ってきました。
シートからシートコレクションを取得しているのは理解しにくいです。
LibreOfficeと違ってgetSheets()の戻り値はfor sheet in sheetsでシートは取得できませんでした。
参考にしたサイト
Extensions/Configuration - ...?
コンフィギュレーションファイルの層構造の解説。
GASでGoogleスプレッドシートのセルの値、行数や列数を取得したり、セルに値を入力したりする基本:Excel VBAプログラマーのためのGoogle Apps Script入門(2) - @IT
Googleスプレッドシートのセル範囲、値の取得方法。
Google Apps Scriptの初回実行に必要となる承認手順
Googleスプレッドシートのマクロはスプレッドシートごとに管理されており初回実行時はこの手順通りにして「許可」ボタンをクリックする必要がありました。


0 件のコメント:
コメントを投稿