Calc(69)XMouseClickHandlerで取得できる位置

2018-02-03

旧ブログ

t f B! P L
XEnhancedMouseClickHandlerと違ってコントロールに追加したXMouseClickHandlerのメソッドの引数のMouseEvent StructのSourceアトリビュートはNoneではないのでそれを利用します。

前の関連記事:Calc(68)XEnhancedMouseClickHandlerで取得できる位置


コントロールに追加したXMouseClickHandlerのメソッドの引数のSource


MouseEvent StructのSourceにはコンポーネントウィンドウと同じサービスとインターフェイスをもったオブジェクトが入っていました(LibreOffice5(118)コンポーネントウィンドウのサービスとインターフェイスの一覧)。

そのAccessibleContextのAccessibleRoleはPANEL=40になっていました。

AccessibleChildCountは0でした。

AccessibleParentにはSCROLL_PANE=51の要素が入っており、さらにそのAccessibleParentにはPANEL=40の要素が入っていました。


数式バーまで含んだ部分がコンポーネントウィンドウで、コンポーネントウィンドウの中でスクロールする部分がSCROLL_PANE=51(図の赤枠)ということになります。

行ヘッダと列ヘッダを除いたセルだけの部分(図の青枠)がMouseEvent StructのSourceに入っている部分です。

MouseEvent StructからSCROLL_PANE=51の左上角の画面に対する座標を求めるには次のようになります。

mouseevent.Source.getAccessibleContext().getAccessibleParent().getAccessibleContext().getLocationOnScreen()

mouseeventがMouseEvent Structです。

Calc(67)AccessibleWindowの位置と大きさを取得するマクロのときはこの部分はDOCUMENT_SPREADSHEET=84になっていました。

このときのサービスとインターフェイスはCalc(66)AccessibleSpreadsheetDocumentViewのサービスとインターフェイスの一覧になっていたのでオブジェクトとしては違うものです。

XMouseClickHandlerで取得できる位置を表示するマクロ

import unohelper  # オートメーションには必須(必須なのはuno)。
from com.sun.star.awt import XMouseClickHandler
from com.sun.star.awt import MouseButton  # 定数
from com.sun.star.frame.FrameAction import FRAME_UI_DEACTIVATING  # enum
from com.sun.star.frame import XFrameActionListener
from com.sun.star.util import MeasureUnit
from com.sun.star.document import XDocumentEventListener
from com.sun.star.style.VerticalAlignment import MIDDLE
from com.sun.star.awt import XActionListener
from com.sun.star.awt import Point  # Struct
def macro(documentevent=None):  # 引数は文書のイベント駆動用。import pydevd; pydevd.settrace(stdoutToServer=True, stderrToServer=True)
 doc = XSCRIPTCONTEXT.getDocument()  # 現在開いているドキュメントを取得。
 ctx = XSCRIPTCONTEXT.getComponentContext()  # コンポーネントコンテクストの取得。
 smgr = ctx.getServiceManager()  # サービスマネージャーの取得。
 controller = doc.getCurrentController()  # コントローラの取得。
 mouseclickhandler = MouseClickHandler(controller, ctx, smgr, doc)
 controller.addMouseClickHandler(mouseclickhandler)  # EnhancedMouseClickHandler
 doc.addDocumentEventListener(DocumentEventListener(mouseclickhandler))  # DocumentEventListener 
class MouseClickHandler(unohelper.Base, XMouseClickHandler):
 def __init__(self, subj, ctx, smgr, doc):
  self.subj = subj  # disposing()用。コントローラは取得し直さないと最新の画面の状態が反映されない。
  self.args = ctx, smgr, doc
 def mousePressed(self, mouseevent):
  ctx, smgr, doc = self.args
  target = doc.getCurrentSelection()  # ターゲットのセルを取得。
  if mouseevent.Buttons==MouseButton.LEFT:  # 左ボタンのとき
   if target.supportsService("com.sun.star.sheet.SheetCell"):  # ターゲットがセルの時。
    if mouseevent.ClickCount==2:  # ダブルクリックの時
     controller = doc.getCurrentController()  # 現在のコントローラを取得。
     frame = controller.getFrame()  # フレームを取得。
     componentwindow = frame.getComponentWindow()  # コンポーネントウィンドウを取得。
     source = mouseevent.Source  # クリックした枠のコンポーネントウィンドウが返る。
     point = componentwindow.convertPointToLogic(Point(X=mouseevent.X, Y=mouseevent.Y), MeasureUnit.APPFONT)  # EnhancedMouseClickHandlerの座標をmaに変換。
     # コントロールダイアログの左上の座標を設定。
     dialogX = point.X
     dialogY = point.Y
     m = 6  # コントロール間の間隔
     nameX = {"PositionX": m, "Width": 105, "Height": 12, "NoLabel": True, "Align": 2, "VerticalAlign": MIDDLE}  # 名前Xの共通プロパティ。
     numX = {"PositionX": nameX["PositionX"]+nameX["Width"], "Width": 20, "Height": nameX["Height"], "VerticalAlign": MIDDLE}  # X値入力欄の共通プロパティ。
     unitX = {"PositionX": numX["PositionX"]+numX["Width"], "Width": 10, "Height": nameX["Height"], "Label": "px", "NoLabel": True, "VerticalAlign": MIDDLE}  # 単位の共通プロパティ。
     nameY, numY, unitY = nameX.copy(), numX.copy(), unitX.copy()  # コントロールのプロパティの辞書をコピーする。
     nameY["PositionX"] = unitX["PositionX"] + unitX["Width"]  # 左隣のコントロールのPositionXと幅からPositionXを算出。
     nameY["Width"] = 10
     numY["PositionX"] = nameY["PositionX"] + nameY["Width"]
     unitY["PositionX"] = numY["PositionX"] + numY["Width"]
     controls = nameX, numX, unitX, nameY, numY, unitY  # 1行に表示するコントロールのタプル。
     controldialog =  {"PositionX": dialogX, "PositionY": dialogY, "Width": unitY["PositionX"]+unitY["Width"]+m, "Title": "Position", "Name": "Position", "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に渡した辞書は変更されるのでコピーを渡す。
     nameX["Label"] = "mouseevent.X: "
     numX["Text"] = str(mouseevent.X)  # プロパティに代入するときは文字列に変更必要。
     nameY["Label"] = ".Y: " 
     numY["Text"] = str(mouseevent.Y)
     addControl("FixedText", nameX)
     addControl("Edit", numX)  
     addControl("FixedText", unitX) 
     addControl("FixedText", nameY)
     addControl("Edit", numY)  
     addControl("FixedText", unitY)  
     # 2行目
     y = nameX["PositionY"] + nameX["Height"] + m  
     for c in controls:
      c["PositionY"] = y
     nameX, numX, unitX, nameY, numY, unitY = [c.copy() for c in controls]  # addControlに渡した辞書は変更されるのでコピーを渡す。
     nameX["Label"] = "Target X: "
     point = componentwindow.convertPointToPixel(target.getPropertyValue("Position"), MeasureUnit.MM_100TH)  # クリックしたセルの左上角の座標。1/100mmをpxに変換。
     numX["Text"] = str(point.X)
     nameY["Label"] = "Y: " 
     numY["Text"] = str(point.Y)    
     addControl("FixedText", nameX)
     addControl("Edit", numX)  
     addControl("FixedText", unitX) 
     addControl("FixedText", nameY)
     addControl("Edit", numY)  
     addControl("FixedText", unitY) 
     # 3行目
     y = nameX["PositionY"] + nameX["Height"] + m  
     for c in controls:
      c["PositionY"] = y
     nameX, numX, unitX, nameY, numY, unitY = [c.copy() for c in controls]  # addControlに渡した辞書は変更されるのでコピーを渡す。
     nameX["Label"] = "Source.getPosSize().X: "
     possize = source.getPosSize()  # サブウィンドウのPosSize。
     numX["Text"] = str(possize.X)
     nameY["Label"] = ".Y: " 
     numY["Text"] = str(possize.Y)    
     addControl("FixedText", nameX)
     addControl("Edit", numX)  
     addControl("FixedText", unitX) 
     addControl("FixedText", nameY)
     addControl("Edit", numY)  
     addControl("FixedText", unitY)  
     # 4行目
     y = nameX["PositionY"] + nameX["Height"] + m  
     for c in controls:
      c["PositionY"] = y
     nameX, numX, unitX, nameY, numY, unitY = [c.copy() for c in controls]  # addControlに渡した辞書は変更されるのでコピーを渡す。
     nameX["Label"] = "AccessibleContext.getLocation().X: "
     accessiblecontext = source.getAccessibleContext()  # ウィンドウのAccessibleContextを取得。
     point = accessiblecontext.getLocation()  # 位置を取得。
     numX["Text"] = str(point.X)
     nameY["Label"] = ".Y: " 
     numY["Text"] = str(point.Y)    
     addControl("FixedText", nameX)
     addControl("Edit", numX)  
     addControl("FixedText", unitX) 
     addControl("FixedText", nameY)
     addControl("Edit", numY)  
     addControl("FixedText", unitY)          
     # 5行目
     button = {"PositionY": nameX["PositionY"]+nameX["Height"]+m, "Height": nameX["Height"]+2, "Width": 30, "Label": "~Close", "PushButtonType": 2}  # ボタン。PushButtonTypeの値はEnumではエラーになる。
     button["PositionX"] = unitY["PositionX"] + unitY["Width"] - button["Width"]
     addControl("Button", button)
     dialog.getModel().setPropertyValue("Height", button["PositionY"]+button["Height"]+m)
     toolkit = componentwindow.getToolkit()  # ピアからツールキットを取得。コンテナウィンドウでもコンポーネントウィンドウでも結果は同じ。
     dialog.createPeer(toolkit, componentwindow)  # ダイアログを描画。親ウィンドウを渡す。ノンモダルダイアログのときはNone(デスクトップ)ではフリーズする。Stepを使うときはRoadmap以外のコントロールが追加された後にピアを作成しないとStepが重なって表示される。
     showModelessly(ctx, smgr, frame, dialog)  # ノンモダルダイアログとして表示。ダイアログのフレームを取得。
     return True  # セル編集モードにしない。
  return False  # セル編集モードにする。
 def mouseReleased(self, mouseevent):
  return False  # シングルクリックでFalseを返すとセル選択範囲の決定の状態になってどうしようもなくなる。
 def disposing(self, eventobject):
  self.subj.removeMouseClickHandler(self)
class DocumentEventListener(unohelper.Base, XDocumentEventListener):
 def __init__(self, mouseclickhandler):
  self.args = mouseclickhandler
 def documentEventOccured(self, documentevent):
  mouseclickhandler = self.args
  if documentevent.EventName=="OnUnload":  
   source = documentevent.Source
   source.removeMouseClickHandler(mouseclickhandler)
   source.removeDocumentEventListener(self)
 def disposing(self, eventobject):
  eventobject.Source.removeDocumentEventListener(self)
関数showModelessly()とdialogCreator()はLibreOffice5(69)Javaの例:GUIをPythonにする その2で作成したものを使っています。

DocumentEventListenerはドキュメントを閉じるときのリスナーの除去のためだけの目的です。

XEnhancedMouseClickHandlerXMouseClickHandlerではメソッドの戻り値のブーリアンの働きが逆になっています(Calc(27)ドキュメント内のすべてのシートでクリックを補足する)。

MouseClickHandler.ods

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

XMouseClickHandlerで取得できる位置



シート上のA1セルをダブルクリックするとこのようなダイアログが出現します。

1行目はMouseEvent StructのXYを出力しています。

この原点は見えている左上セルの左上角になっていました。

つまりCalc(65)フレームとコントローラの位置のcontroller.getPropertyValue("VisibleAreaOnScreen")が原点です。

しかしウィンドウを分割したり、行や列の固定をすると原点が変化しました。


ウィンドウの分割をして右下の枠の左上のセルをダブルクリックしたときの結果です。

分割する前のときと違って原点が、左上のセルの左上角になっていることがわかります。

2行目はドキュメントモデルのgetCurrentSelection()で取得した選択しているセルのPositionプロパティの1/100mmをピクセルに変換して出力しています。

Calc(65)フレームとコントローラの位置でもやったようにセルの位置から取得したピクセル単位は誤差が多くて使いにくいものです。

3行目はMouseEvent StructのSourceのgetPosSize()から取得したXYです。

この点の原点は常にコンポーネントウィンドウのSCROLL_PANE=51の子ウィンドウの左上角になっていました(上記)。

この原点の画面に対する座標はコンポーネントウィンドウからSCROLL_PANE=51の子ウィンドウを探して取得するか、
MouseEvent Structからmouseevent.Source.getAccessibleContext().getAccessibleParent().getAccessibleContext().getLocationOnScreen()から取得するか、
controller.getPropertyValue("VisibleAreaOnScreen")からcontroller.getBorder()を引いて求めることができます。

4行目はAccessibleContextから取得したXYはMouseEvent StructのSourceのgetPosSize()から取得したXYと同じでした。

次の関連記事:Calc(70)クリックした位置に出現するモダルダイアログ

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ