LibreOffice5(137)グリッドコントロールを使うマクロの例

公開日: 2018年01月29日 更新日: 2019年05月11日

旧ブログ

t f B! P L
グリッドコントロールはまずDefaultGridColumnModelで列を作成してグリッドコントロールに挿入し、その列にDefaultGridDataModelで行を一連のデータ単位として挿入するという手順になります。

前の関連記事:LibreOffice5(136)Map AppFont (ma)とピクセルと1/100mmの変換


グリッドコントロールをダイアログコントロールに表示するマクロ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import unohelper  # オートメーションには必須(必須なのはuno)。
from datetime import datetime
from com.sun.star.style.VerticalAlignment import MIDDLE
from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMouseListener
from com.sun.star.accessibility import AccessibleRole  # 定数
from com.sun.star.awt import ScrollBarOrientation  # 定数
from com.sun.star.awt import MouseButton  # 定数
from com.sun.star.awt import XEnhancedMouseClickHandler
from com.sun.star.util import XCloseListener
def macro(documentevent=None):  # 引数は文書のイベント駆動用。import pydevd; pydevd.settrace(stdoutToServer=True, stderrToServer=True)
    ctx = XSCRIPTCONTEXT.getComponentContext()  # コンポーネントコンテクストの取得。
    smgr = ctx.getServiceManager()  # サービスマネージャーの取得。
    doc = XSCRIPTCONTEXT.getDocument()  # マクロを起動した時のドキュメントのモデルを取得。  
    controller = doc.getCurrentController()  # コントローラの取得。
    controller.addEnhancedMouseClickHandler(EnhancedMouseClickHandler(controller, ctx, smgr, doc))  # EnhancedMouseClickHandler
class EnhancedMouseClickHandler(unohelper.Base, XEnhancedMouseClickHandler):
    def __init__(self, subj, ctx, smgr, doc):
        self.subj = subj
        self.args = ctx, smgr, doc
    def mousePressed(self, enhancedmouseevent):
        ctx, smgr, doc = self.args
        target = enhancedmouseevent.Target  # ターゲットのセルを取得。
        if enhancedmouseevent.Buttons==MouseButton.LEFT:  # 左ボタンのとき
            if target.supportsService("com.sun.star.sheet.SheetCell"):  # ターゲットがセルの時。
                if enhancedmouseevent.ClickCount==2# ダブルクリックの時
                    cellbackcolor = target.getPropertyValue("CellBackColor"# セルの背景色を取得。
                    if cellbackcolor==0x8080FF# 背景が青紫色の時。
                        createDialog(ctx, smgr, doc, True# ノンモダルダイアログにする。
                        return False  # セル編集モードにしない。
                    elif cellbackcolor==0xFFFF80# 背景が黄色の時。
                        createDialog(ctx, smgr, doc, False# モダルダイアログにする。 
                        return False  # セル編集モードにしない。
        return True  # セル編集モードにする。
    def mouseReleased(self, enhancedmouseevent):
        return True  # シングルクリックでFalseを返すとセル選択範囲の決定の状態になってどうしようもなくなる。
    def disposing(self, eventobject):  # ドキュメントを閉じる時でも呼ばれない。
        self.subj.removeEnhancedMouseClickHandler(self)
def createDialog(ctx, smgr, doc, flg):
    frame = doc.getCurrentController().getFrame()  # モデル→コントローラ→フレーム、でドキュメントのフレームを取得。
    containerwindow = frame.getContainerWindow()  # ドキュメントのウィンドウ(コンテナウィンドウ=ピア)を取得。
    toolkit = containerwindow.getToolkit()  # ピアからツールキットを取得。 
    m = 6  # コントロール間の間隔
    grid = {"PositionX": m, "PositionY": m, "Width": 145, "Height": 100, "ShowColumnHeader": True, "ShowRowHeader": True# グリッドコントロールの基本プロパティ。
    label = {"PositionX": m, "Width": 45, "Height": 12, "Label": "Date and time: ", "NoLabel": True, "Align": 2, "VerticalAlign": MIDDLE}  # ラベルフィールドコントロールの基本プロパティ。
    x = label["PositionX"]+label["Width"# ラベルフィールドコントロールの右端。
    textbox = {"PositionX": x, "Width": grid["PositionX"]+grid["Width"]-x, "Height": label["Height"], "VerticalAlign": MIDDLE}  # テクストボックスコントロールの基本プロパティ。
    button = {"PositionX": m, "Width": 30, "Height": label["Height"]+2, "PushButtonType": 0# ボタンの基本プロパティ。PushButtonTypeの値はEnumではエラーになる。
    controldialog =  {"PositionX": 100, "PositionY": 40, "Width": grid["PositionX"]+grid["Width"]+m, "Title": "Grid Example", "Name": "controldialog", "Step": 0, "Moveable": True# コントロールダイアログの基本プロパティ。幅は右端のコントロールから取得。高さは最後に設定する。
    dialog, addControl = dialogCreator(ctx, smgr, controldialog)  # コントロールダイアログの作成。
    mouselister = MouseListener(doc)
    actionlistener = ActionListener()
    grid1 = addControl("Grid", grid, {"addMouseListener": mouselister})  # グリッドコントロールの取得。
    gridmodel = grid1.getModel()  # グリッドコントロールモデルの取得。
    gridcolumn = gridmodel.getPropertyValue("ColumnModel"# DefaultGridColumnModel
    column0 = gridcolumn.createColumn()  # 列の作成。
    column0.Title = "Date"  # 列ヘッダー。
    column0.ColumnWidth = 60  # 列幅。
    gridcolumn.addColumn(column0)  # 列を追加。
    column1 = gridcolumn.createColumn()  # 列の作成。
    column1.Title = "Time"  # 列ヘッダー。
    column1.ColumnWidth = grid["Width"] - column0.ColumnWidth  #  列幅。列の合計がグリッドコントロールの幅に一致するようにする。
    gridcolumn.addColumn(column1)  # 列を追加。
    griddata = gridmodel.getPropertyValue("GridDataModel"# GridDataModel
    now = datetime.now()  # 現在の日時を取得。
    griddata.addRow(0, (now.date().isoformat(), now.time().isoformat()))  # グリッドに行を挿入。
    y = grid["PositionY"] + grid["Height"] + # 下隣のコントロールのY座標を取得。
    label["PositionY"] = textbox["PositionY"] = y
    textbox["Text"] = now.isoformat().replace("T", " ")
    addControl("FixedText", label)
    addControl("Edit", textbox) 
    y = label["PositionY"] + label["Height"] + # 下隣のコントロールのY座標を取得。
    button1, button2 = button.copy(), button.copy()
    button1["PositionY"] = button2["PositionY"= y
    button1["Label"] = "~Now"
    button2["Label"] = "~Close"
    button2["PushButtonType"] = 2  # CANCEL 
    button2["PositionX"] = grid["Width"] - button2["Width"]
    button1["PositionX"] = button2["PositionX"] - m - button1["Width"]
    addControl("Button", button1, {"setActionCommand": "now" ,"addActionListener": actionlistener}) 
    addControl("Button", button2) 
    dialog.getModel().setPropertyValue("Height", button1["PositionY"]+button1["Height"]+m)  # コントロールダイアログの高さを設定。
    dialog.createPeer(toolkit, containerwindow)  # ダイアログを描画。親ウィンドウを渡す。ノンモダルダイアログのときはNone(デスクトップ)ではフリーズする。Stepを使うときはRoadmap以外のコントロールが追加された後にピアを作成しないとStepが重なって表示される。
    if flg:  # ノンモダルダイアログにするとき。オートメーションでは動かない。
        dialogframe = showModelessly(ctx, smgr, frame, dialog) 
        dialogframe.addCloseListener(CloseListener(dialog, mouselister, actionlistener))  # CloseListener
    else# モダルダイアログにする。フレームに追加するとエラーになる。
        dialog.execute() 
        dialog.dispose()
class CloseListener(unohelper.Base, XCloseListener):  # ノンモダルダイアログのリスナー削除用。
    def __init__(self, dialog, mouselister, actionlistener):
        self.args = dialog, mouselister, actionlistener
    def queryClosing(self, eventobject, getsownership):
        dialog, mouselister, actionlistener = self.args
        dialog.getControl("Grid1").removeMouseListener(mouselister)
        dialog.getControl("Button1").removeActionListener(actionlistener)
    def notifyClosing(self, eventobject):
        pass
    def disposing(self, eventobject): 
        eventobject.Source.removeCloseListener(self)
class MouseListener(unohelper.Base, XMouseListener): 
    def __init__(self, doc):
        self.args = doc
    def mousePressed(self, mouseevent):
        if mouseevent.Buttons==MouseButton.LEFT and mouseevent.ClickCount==2# ダブルクリックの時。
            doc = self.args
            selection = doc.getCurrentSelection()  # シート上で選択しているオブジェクトを取得。
            if selection.supportsService("com.sun.star.sheet.SheetCell"):  # 選択オブジェクトがセルの時。
                source = mouseevent.Source  # グリッドコントロールを取得。
                griddata = source.getModel().getPropertyValue("GridDataModel"# GridDataModelを取得。
                rowdata = griddata.getRowData(source.getCurrentRow())  # グリッドコントロールで選択している行のすべての列をタプルで取得。
                selection.setString(" ".join(rowdata))  # 選択セルに書き込む。
    def mouseReleased(self, mouseevent):
        pass
    def mouseEntered(self, mouseevent):
        pass
    def mouseExited(self, mouseevent):
        pass
    def disposing(self, eventobject):
        eventobject.Source.removeMouseListener(self)
class ActionListener(unohelper.Base, XActionListener):
    def actionPerformed(self, actionevent):
        cmd = actionevent.ActionCommand
        source = actionevent.Source  # ボタンコントロールが返る。
        context = source.getContext()  # コントロールダイアログが返ってくる。
        if cmd == "now":
            now = datetime.now()  # 現在の日時を取得。
            context.getControl("Edit1").setText(now.isoformat().replace("T", " "))  # テキストボックスコントロールに入力。
            grid1 = context.getControl("Grid1"# グリッドコントロールを取得。
            griddata = grid1.getModel().getPropertyValue("GridDataModel"# グリッドコントロールモデルからDefaultGridDataModelを取得。
            griddata.addRow(griddata.RowCount, (now.date().isoformat(), now.time().isoformat()))  # 新たな行を追加。
            accessiblecontext = grid1.getAccessibleContext()  # グリッドコントロールのAccessibleContextを取得。
            for i in range(accessiblecontext.getAccessibleChildCount()):  # 子要素をのインデックスを走査する。
                child = accessiblecontext.getAccessibleChild(i)  # 子要素を取得。
                if child.getAccessibleContext().getAccessibleRole()==AccessibleRole.SCROLL_BAR:  # スクロールバーの時。
                    if child.getOrientation()==ScrollBarOrientation.VERTICAL:  # 縦スクロールバーの時。
                        child.setValue(child.getMaximum())  # 最大値にスクロールさせる。
                        return
    def disposing(self, eventobject):
        eventobject.Source.removeActionListener(self)
LibreOffice5(69)Javaの例:GUIをPythonにする その2のshowModelessly()とdialogCreator()の表記は略しています。

ダイアログコントロール上のコントロールは隣のコントロールの位置と大きさから、位置を算出しています。

55行目でグリッドコントロールモデルからDefaultGridColumnModelを取得してcreateColumn()メソッドで新規列を作成して、addColumn()メソッドでその列をDefaultGridColumnModelに挿入しています。

列のインデックスはaddColumn()の追加順になっていました。

64行目でグリッドコントロールモデルからDefaultGridDataModelを取得してaddRow()メソッドで行インデックスを指定して、列数と一致した要素のタプルを挿入することでセルに値を代入しています。

gridcontrol.ods

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

gridcontrol.odsの実行例



gridcontrol.odsの青紫色が背景のセルをダブルクリックするとノンモダルダイアログが出現します。


ダイアログ内のテーブルがグリッドコントロールです。

ダイアログが開いた時の日時が1行目に入っています。

Nowボタンをクリックするとボタンをクリックしたときの日時が追加されていきます。


 すべての行が表示できなくなるとグリッドコントロールに縦スクロールバーが出現します。

デフォルトでは行を追加してもその行が見えるようにスクロールはされませんが、このマクロではスクロールバーを取得して常に追加された行が表示されるようにしています(137行目)。

スクロールバーの取得は、グリッドコントロールにそれを取得するメソッドを見つけられず、 getAccessibleContext()メソッドで取得したAccessibleContextの子要素を走査してAccessibleRoleがSCROLL_BARのものを探しだしています。

スクロールバーのAccessibleContextのサービスとインターフェイスはLibreOffice5(118)コンポーネントウィンドウのサービスとインターフェイスの一覧でみた、コンポーネントウィンドウのものと同じでした(AccessibleContextは常にAccessibleWindow?)。


グリッドコントロールのいずれかの行をダブルクリックするとその行のセルの値を結合して選択したセルに書き込みます。

青紫色が背景のセルのときはノンモダルダイアログなのでダイアログを表示させたままセルを変更できます。

それに対して背景が黄色のセルのときはモダルダイアログなのでダイアログ表示後にセルは変更できません。

グリッドコントロールではCalcのシートと違って1つのセルだけ選択するということはできず、行全体の選択しかできませんでした。

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

ブログ検索 by Blogger

Translate

Created by Calendar Gadget

QooQ