LibreOffice5(136)Map AppFont (ma)とピクセルと1/100mmの変換

2018-01-28

旧ブログ

t f B! P L
LibreOffice5(62)Map AppFont (ma)とピクセルで書いたようにMap AppFont (ma)ピクセルに変換するメソッドをみつけたのでそれぞれに変換するマクロを作成しました。Calcのセルの位置や大きさは1/100mmという単位になっているのでその変換もできるようにしました。

前の関連記事:LibreOffice5(135)スクロールバーコントロールのサービスとインターフェイスの一覧


Map AppFont (ma)とピクセルと1/100mmの変換をするマクロ

import unohelper  # オートメーションには必須(必須なのはuno)。
from com.sun.star.style.VerticalAlignment import MIDDLE
from com.sun.star.awt import XActionListener
from com.sun.star.awt import Point  # Struct
from com.sun.star.util import MeasureUnit
def macro(documentevent=None):  # 引数は文書のイベント駆動用。
 ctx = XSCRIPTCONTEXT.getComponentContext()  # コンポーネントコンテクストの取得。
 smgr = ctx.getServiceManager()  # サービスマネージャーの取得。
 doc = XSCRIPTCONTEXT.getDocument()  # マクロを起動した時のドキュメントのモデルを取得。   
 docframe = doc.getCurrentController().getFrame()  # モデル→コントローラ→フレーム、でドキュメントのフレームを取得。
 docwindow = docframe.getContainerWindow()  # ドキュメントのウィンドウ(コンテナウィンドウ=ピア)を取得。
 toolkit = docwindow.getToolkit()  # ピアからツールキットを取得。  
 m = 6  # コントロール間の間隔
 nameX = {"PositionX": m, "Width": 10, "Height": 12, "Label": "X: ", "NoLabel": True, "Align": 2, "VerticalAlign": MIDDLE}  # 名前Xの共通プロパティ。
 numX = {"PositionX": nameX["PositionX"]+nameX["Width"], "Width": 40, "Height": nameX["Height"], "VerticalAlign": MIDDLE}  # X値入力欄の共通プロパティ。
 unitX = {"PositionX": numX["PositionX"]+numX["Width"], "Width": 32, "Height": nameX["Height"], "NoLabel": True, "VerticalAlign": MIDDLE}  # 単位の共通プロパティ。
 nameY, numY, unitY = nameX.copy(), numX.copy(), unitX.copy()  # コントロールのプロパティの辞書をコピーする。
 nameY["PositionX"] = unitX["PositionX"] + unitX["Width"]  # 左隣のコントロールのPositionXと幅からPositionXを算出。
 nameY["Label"] = "Y: " 
 numY["PositionX"] = nameY["PositionX"] + nameY["Width"]
 unitY["PositionX"] = numY["PositionX"] + numY["Width"]
 controls = nameX, numX, unitX, nameY, numY, unitY  # 1行に表示するコントロールのタプル。
 button = {"Height": nameX["Height"]+2, "PushButtonType": 0}  # ボタンの共通プロパティ。PushButtonTypeの値はEnumではエラーになる。
 controldialog =  {"PositionX": 100, "PositionY": 40, "Width": unitY["PositionX"]+unitY["Width"]+m, "Title": "Unit Converter", "Name": "ConvertUnits", "Step": 0, "Moveable": True}  # コントロールダイアログのプロパティ。幅は右端のコントロールから取得。高さは最後に設定する。
 dialog, addControl = dialogCreator(ctx, smgr, controldialog)
 # 1行目
 for c in controls:
  c["PositionY"] = m 
 nameX, numX, unitX, nameY, numY, unitY = [c.copy() for c in controls]  # addControlに渡した辞書は変更されるのでコピーを渡す。
 unitX["Label"] = unitY["Label"] = "px"
 addControl("FixedText", nameX)
 addControl("Edit", numX)  
 addControl("FixedText", unitX) 
 addControl("FixedText", nameY)
 addControl("Edit", numY)  
 addControl("FixedText", unitY)  
 # 2行目
 y = unitY["PositionY"] + unitY["Height"] + m  
 for c in controls:
  c["PositionY"] = y
 nameX, numX, unitX, nameY, numY, unitY = [c.copy() for c in controls]  # addControlに渡した辞書は変更されるのでコピーを渡す。
 unitX["Label"] = unitY["Label"] = "ma"
 addControl("FixedText", nameX)
 addControl("Edit", numX)  
 addControl("FixedText", unitX) 
 addControl("FixedText", nameY)
 addControl("Edit", numY)  
 addControl("FixedText", unitY) 
 # 3行目
 y = unitY["PositionY"] + unitY["Height"] + m  
 for c in controls:
  c["PositionY"] = y
 nameX, numX, unitX, nameY, numY, unitY = [c.copy() for c in controls]  # addControlに渡した辞書は変更されるのでコピーを渡す。
 unitX["Label"] = unitY["Label"] = "1/100mm"
 addControl("FixedText", nameX)
 addControl("Edit", numX)  
 addControl("FixedText", unitX) 
 addControl("FixedText", nameY)
 addControl("Edit", numY)  
 addControl("FixedText", unitY) 
 # 4行目
 y = unitY["PositionY"] + unitY["Height"] + m  
 message = {"Name": "Message", "PositionX": m, "PositionY": y, "Width": controldialog["Width"]-m*2, "Height": 12, "Label": "Pass any one of units and push Convert", "NoLabel": True}
 addControl("FixedText", message)
 # 5行目
 button1, button2 = button.copy(), button.copy()
 button1["PositionY"] = button2["PositionY"] = message["PositionY"] + message["Height"] + m  
 button1["Width"] = 40
 button1["Label"] = "Con~vert"
 button2["Width"] = 30
 button2["Label"] = "~Clear" 
 button2["PositionX"] = unitY["PositionX"] + unitY["Width"] - button2["Width"]
 button1["PositionX"] = button2["PositionX"] - m - button1["Width"]
 actionlistener = ActionListener()
 addControl("Button", button1, {"setActionCommand": "convert" ,"addActionListener": actionlistener})
 addControl("Button", button2, {"setActionCommand": "clear" ,"addActionListener": actionlistener})
 dialog.getModel().setPropertyValue("Height", button1["PositionY"]+button1["Height"]+m)
 dialog.createPeer(toolkit, docwindow)  # ダイアログを描画。親ウィンドウを渡す。ノンモダルダイアログのときはNone(デスクトップ)ではフリーズする。Stepを使うときはRoadmap以外のコントロールが追加された後にピアを作成しないとStepが重なって表示される。
 # モダルダイアログにする。フレームに追加するとエラーになる。
 dialog.execute()  
 dialog.dispose() 
class ActionListener(unohelper.Base, XActionListener):
 def actionPerformed(self, actionevent):
  cmd = actionevent.ActionCommand
  source = actionevent.Source  # ボタンコントロールが返る。
  context = source.getContext()  # コントロールダイアログが返ってくる。
  message = context.getControl("Message")
  if cmd == "convert":
   edits = [context.getControl("Edit{}".format(i)) for i in range(1, 7)]
   edittxts = [e.getText() for e in edits]
   if ["".join((edittxts[i], edittxts[i+1])) for i in range(0, 6, 2)].count("")==2:  # 2行だけテキストボックスが空文字の時。
    pxToma, pxTomm, maTopx, maTomm, mmTopx, mmToma = createConverters(context)
    edit11, edit12, edit21, edit22, edit31, edit32 = edits
    for i in range(0, 6, 2):  # 各行について。
     x, y = edittxts[i], edittxts[i+1]
     if x or y:  # いずれかは空文字でない時。
      x = int(x) if x.isdigit() else 0  # テキストボックスの文字が数字の時は整数に変換。数字でなければ0にする。
      y = int(y) if y.isdigit() else 0
      if i==0:  # 1行目に数字が入力されている時。
       edit11.setText(x)
       edit12.setText(y)       
       maX, maY = pxToma(x, y)
       edit21.setText(maX)
       edit22.setText(maY)
       mmX, mmY = pxTomm(x, y)
       edit31.setText(mmX)
       edit32.setText(mmY)
       u = "px"
      elif i==2:  # 2行目に数字が入力されている時。
       pxX, pxY = maTopx(x, y)
       edit11.setText(pxX)
       edit12.setText(pxY)
       edit21.setText(x)
       edit22.setText(y)        
       mmX, mmY = maTomm(x, y)
       edit31.setText(mmX)
       edit32.setText(mmY)  
       u = "ma"
      elif i==4:  # 3行目に数字が入力されている時。
       pxX, pxY = mmTopx(x, y)
       edit11.setText(pxX)          
       edit12.setText(pxY)
       maX, maY = mmToma(x, y)
       edit21.setText(maX)
       edit22.setText(maY) 
       edit31.setText(x)
       edit32.setText(y)        
       u = "mm" 
      message.setText("{} converted to other unit".format(u))  
      message.getModel().setPropertyValue("TextColor", None)
      return  
   else:  # パラメーターが1つだけでない時。
    message.setText("Too many inputs or no input")
    message.getModel().setPropertyValue("TextColor", 0xFF0000)
  elif cmd == "clear":
   [context.getControl("Edit{}".format(i)).setText("") for i in range(1, 7)]
   message.setText("Pass any one of units and push Convert")
   message.getModel().setPropertyValue("TextColor", None)
 def disposing(self, eventobject):
  eventobject.Source.removeActionListener(self)
def createConverters(window):
 def maTopx(x, y):  # maをpxに変換する。
  point = window.convertPointToPixel(Point(X=x, Y=y), MeasureUnit.APPFONT)
  return point.X, point.Y
 def mmTopx(x, y):  # 1/100mmをpxに変換する。
  point = window.convertPointToPixel(Point(X=x, Y=y), MeasureUnit.MM_100TH)
  return point.X, point.Y
 def pxToma(x, y):  # pxをmaに変換する。
  point = window.convertPointToLogic(Point(X=x, Y=y), MeasureUnit.APPFONT)
  return point.X, point.Y
 def pxTomm(x, y):  # pxをmmに変換する。
  point = window.convertPointToLogic(Point(X=x, Y=y), MeasureUnit.MM_100TH)
  return point.X, point.Y
 def mmToma(x, y):  # 1/100mmをmaに変換する。
  return pxToma(*mmTopx(x, y))
 def maTomm(x, y):  # maをmmに変換する。
  return pxTomm(*maTopx(x, y))
 return pxToma, pxTomm, maTopx, maTomm, mmTopx, mmToma
LibreOffice5(69)Javaの例:GUIをPythonにする その2のdialogCreator()は省略しています。

ダイアログコントロールやコントロールの位置や大きさはすべてma単位で設定しています。

コントロールの位置は隣にあるコントロールの位置と大きさから取得するようにし、ダイアログコントロールの大きさは表示するコントロールから算出しています。

unitconverter.ods

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

unitconverter.odsで単位を変換する


unitconverter.odsを開くとモーダルダイアログが出現します。


上からピクセル、Map AppFont、1/100mmの単位の数値の入力欄になっています。

いずれか一つの単位の枠のみ入力して「Convert」ボタンをクリックすると空欄になっている、他の単位の欄に変換後の値が出力されます。

VirtualVoxのゲストOSのlinuxBean14.04で100maを変換してみると上図のようになりました。

X方向では100maは208pxに、Y方向では100maは150pxに変換されています。

システムフォントの高さの1/8、幅の1/4が1maなので、変換時のシステムフォントの縦横比は約3:2とわかります(LibreOffice5(58)モードレスダイアログの例をPythonに翻訳する:その4)。

LibreOffice5(62)Map AppFont (ma)とピクセルでxdlファイルで設定した値でmaとpxを比較した値とは少し結果が異なりますが、その原因はよくわかりませんでした。

オプションダイアログだから違うのか、このときのシステムフォントの値が違うのかもしれません。

とりあえず実行中のダイアログを実測してみるとmaとpxの変換は値通りでした。

1/100mmでは実測よりも少し大きい結果となりましたが、どうしてLibreOfficeが画面の実寸を知るのか不思議です。
(2018.2.2追記。1/100mmをpxに変換するときも結構誤差がありました。Calc(65)フレームとコントローラの位置参照。)


Windows10で実行するとX方向もY方向も同じ値になりました。

つまりWindows10のシステムフォントの縦横比は2:1ということになります。

このマクロのダイアログ自身maで設定しているので、Windows10ではシステムフォントの縦横比を反映して、linuxBean14.04に比べてWindows10では幅が狭くなって高さは高くなっていることがわかります。

Windows10でシステムフォントを変更する方法はわかりませんでした。

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ