Write own Toolbars in Python

Shared Libraries
Forum rules
For sharing working examples of macros / scripts. These can be in any script language supported by OpenOffice.org [Basic, Python, Netbean] or as source code files in Java or C# even - but requires the actual source code listing. This section is not for asking questions about writing your own macros.
Post Reply
ms777
Volunteer
Posts: 177
Joined: Mon Oct 08, 2007 1:33 am

Write own Toolbars in Python

Post by ms777 »

Hi all,

the code below allows you do insert custom designed items into the toolbar. As an example, it inserts two selection boxes and one slider into the toolbar.
This is a rework of some beanshell / basic code in the old forum. If you are interested in the old code, KamiKanda attached it to a post on LO https://ask.libreoffice.org/t/toolbar-w ... -etc/70337. With Python, everything goes much easier :-)

I run it in Visual Studio Code for fast design. The other method is to put it e.g. as toolbar.py into your user script folder (C:\Users\ms777\AppData\Roaming\OpenOffice\4\user\Scripts\python on my computer), and Execute Macro from a calc document, select the function doitAll. There is a wrapper around the main code, which detects if the code is called from VS Code or from a living calc document, and sets some globals accordingly.

Have fun,

ms777
AO 4.1.6 in Win10

Code: Select all

import uno
import unohelper
from subprocess import Popen

from com.sun.star.util import XUpdatable # type:ignore

from com.sun.star.frame import XToolbarController # type:ignore
from com.sun.star.frame import XStatusListener # type:ignore

from com.sun.star.lang import XInitialization # type:ignore

from com.sun.star.awt import Point # type:ignore
from com.sun.star.awt import XItemListener # type:ignore
from com.sun.star.awt import XAdjustmentListener # type:ignore

from com.sun.star.beans import PropertyValue # type:ignore


# define ctx, doc, smgr for (i) invocation from VS Code or (ii) invocation from OO execute macro
if __name__ == '__main__': 
    # invocation from VS Code
    localContext = uno.getComponentContext()
    resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext )
    # start OO
    lo_proc = Popen('"C:\Program Files (x86)\OpenOffice 4\program\soffice.exe" -accept=socket,host=localhost,port=2002;urp;', shell=True)
    ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )

    smgr = ctx.ServiceManager
    desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
    doc = desktop.loadComponentFromURL('private:factory/scalc', '_default', 0, ())

elif __name__ == 'ooo_script_framework':
    # invocation from OO execute macro
    doc = XSCRIPTCONTEXT.getDocument() # type: ignore
    ctx = XSCRIPTCONTEXT.getComponentContext() # type: ignore
    smgr = ctx.ServiceManager
else:
    raise Exception('__name__ is unknown: ' + __name__) 

def xray(target):
    global ctx
    mspf = ctx.ServiceManager.createInstanceWithContext("com.sun.star.script.provider.MasterScriptProviderFactory", ctx)
    script_provider = mspf.createScriptProvider("")
    script = script_provider.getScript("vnd.sun.star.script:XrayTool._Main.Xray?language=Basic&location=application")
    script.invoke((target,), (), ())

def logMessage(mes):
    global logSheet
    global logRow
    logSheet.getCellByPosition(0,logRow).String = mes
    logRow += 1


# wParent: uno.com.sun.star.awt.XWindow
# x, y, width, height: int
# wServiceName: str
# wAttribute: long
# returns -> uno.com.sun.star.awt..XWindow
def makeAwtWindow(wParent, x, y, width, height, wServiceName, wAttribute):
    global smgr
    global ctx
    oToolkit = smgr.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)

    # create WindowDescriptor
    wd = uno.createUnoStruct("com.sun.star.awt.WindowDescriptor")
    wd.Type = uno.getConstantByName("com.sun.star.awt.WindowClass.SIMPLE")
    wd.Parent = wParent
    wd.Bounds = uno.createUnoStruct("com.sun.star.awt.Rectangle", x, y, width, height)
    wd.ParentIndex = -1
    wd.WindowAttributes = wAttribute
    wd.WindowServiceName = wServiceName

    return oToolkit.createWindow(wd)

def TestAwt():
    global doc
    parentWindow = doc.CurrentController.Frame.ContainerWindow
    wAttribute = uno.getConstantByName("com.sun.star.awt.WindowAttribute.SHOW") | uno.getConstantByName("com.sun.star.awt.WindowClass.TOP")
    oWindow = makeAwtWindow(parentWindow, 100, 100, 300, 200, "combobox", wAttribute)
    oWindow.setBackground(0XFF00FF)

class ToolbarItem(unohelper.Base, XInitialization, XUpdatable, XToolbarController, XStatusListener):
    def __init__( self, ctx, propModuleIdentifier, propFrame, propServiceManager, propParentWindow, propIdentifier, propCommandUrl ):
        self.sCommandUrl = propCommandUrl.Value
        print('in ToolbarItem constructor: ' + self.sCommandUrl)
        return
# com.sun.star.lang.XInitialization
    def initialize(self, args):
        logMessage("initialize " + self.sCommandUrl)
# com.sun.star.util.XUpdatable
    def update(self):
        logMessage("update " + self.sCommandUrl)
# com.sun.star.frame.XStatusListener
    def statusChanged(self, state):
        logMessage("statusChanged " + self.sCommandUrl)

# com.sun.star.frame.XToolbarController
    def execute(self, KeyModifier):
        logMessage("execute " + self.sCommandUrl)

    def click(self):
        logMessage("click " + self.sCommandUrl)

    def doubleClick(self):
        logMessage("doubleclick " + self.sCommandUrl)

    def createPopupWindow(self): 
        logMessage("createPopupWindow " + self.sCommandUrl)
        return None

    def createItemWindow(self, wParent):
        pass


class ToolbarItemComboBox(ToolbarItem, XItemListener):
    asOptions = ()
    sText = ""

    def createItemWindow(self, wParent):
        logMessage("createItemWindow start " + self.sCommandUrl)
        wAttribute = uno.getConstantByName("com.sun.star.awt.WindowAttribute.SHOW") | uno.getConstantByName("com.sun.star.awt.VclWindowPeerAttribute.DROPDOWN")

        oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "combobox", wAttribute)
        oWindow.addItemListener(self)
        oWindow.setText(self.sText)
        oWindow.addItems(self.asOptions, 0)
        oWindow.setDropDownLineCount(6)
        logMessage("createItemWindow end " + self.sCommandUrl)
        return oWindow

    def itemStateChanged(self, event):
        self.lSelected = event.Selected
        s = "selection change to item # " + str(self.lSelected)
        if self.lSelected >= 0:
            s = s + ": " + event.Source.getItem(self.lSelected)
        s = s + ', ' + self.sCommandUrl
        logMessage(s)

class ToolbarItemComboBox2(ToolbarItemComboBox):
    pass

class ToolbarItemSchrollbar(ToolbarItem, XAdjustmentListener):

    def createItemWindow(self, wParent):
        logMessage("createItemWindow start " + self.sCommandUrl)
        wAttribute = uno.getConstantByName("com.sun.star.awt.WindowAttribute.SHOW")
        oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "scrollbar", wAttribute)
        oWindow.addAdjustmentListener(self)
        oWindow.setOrientation(uno.getConstantByName("com.sun.star.awt.ScrollBarOrientation.HORIZONTAL"))
        oWindow.setValues(50, 10, 100)
        logMessage("createItemWindow end " + self.sCommandUrl)
        return oWindow

    def adjustmentValueChanged(self, event):
        s = "schrollbar changed to " + str(event.Value) + ', ' + self.sCommandUrl
        logMessage(s)


def createToolbar(toolbarResourceUrl, toolbarUIName):

# delete toolbar if existing
    oLM = doc.CurrentController.Frame.LayoutManager
    if oLM.getElement(toolbarResourceUrl) is not None:
        oLM.destroyElement(toolbarResourceUrl)
# create toolbar
    oLM.createElement(toolbarResourceUrl)
    oEL = oLM.getElement(toolbarResourceUrl)
    oToolbarSettings = oEL.getSettings(True)        
    oToolbarSettings.UIName = toolbarUIName 
    oEL.setSettings(oToolbarSettings) 
# dock the toolbar in the docking area. Not sure if necessary.
    oLM.dockWindow(toolbarResourceUrl, uno.getConstantByName('com.sun.star.ui.DockingArea.DOCKINGAREA_TOP'), Point(0, 0)) 

def insertToolbarItems(toolbarResourceUrl, aItem):
# register the itemService at the Toolbarfactory
    oo = smgr.createInstanceWithContext("com.sun.star.frame.ToolbarControllerFactory", ctx)
    oLM = doc.CurrentController.Frame.LayoutManager
    oEL = oLM.getElement(toolbarResourceUrl)
    oToolbarSettings = oEL.getSettings(True)

    for Item in aItem:
        if oo.hasController( Item.itemCommandUrl, ""):
            oo.deregisterController( Item.itemCommandUrl, "")
        oo.registerController( Item.itemCommandUrl, "", Item.itemService)

        prop1 = PropertyValue()
        prop1.Name = "CommandURL" 
        prop1.Value = Item.itemCommandUrl
        prop2 = PropertyValue()
        prop2.Name = "Label" 
        prop2.Value = Item.itemLabel

        uno.invoke( oToolbarSettings, "insertByIndex", (oToolbarSettings.Count, uno.Any("[]com.sun.star.beans.PropertyValue", (prop1, prop2)))) 

    oEL.setSettings(oToolbarSettings) 


def doItAll():
    global logSheet
    global logRow

    print('Hallo')

    #for the logging ...
    logRow = 0
    logSheet = doc.Sheets.getByIndex(0)
    logSheet.getCellRangeByPosition(0,0,0,1000).clearContents(uno.getConstantByName('com.sun.star.sheet.CellFlags.STRING'))

    #
    logMessage('Hallo')

    TestAwt()

    xSM = ctx.getValueByName("/singletons/com.sun.star.lang.theServiceManager")

    toolbarResourceUrl = "private:resource/toolbar/custom_ms777"
    toolbarUIName = "toolbarUIName ms777"

    ToolbarItemComboBox.asOptions = ( "test", "foo", "bar", "test2", "foo2", "bar2" )
    ToolbarItemComboBox.sText = "Combo Box 1"
    ToolbarItemComboBox.itemLabel = "LabelCombobox 1"
    ToolbarItemComboBox.itemCommandUrl = ".uno:ms777Combobox"
    ToolbarItemComboBox.itemService = "ms777.ToolbarCombobox"
    ToolbarItemComboBox.itemServiceImpl = "ms777.impl.ToolbarCombobox"

    ToolbarItemComboBox2.asOptions = ( "option A", "Karl Heinz")
    ToolbarItemComboBox2.sText = "Combo Box 2"
    ToolbarItemComboBox2.itemLabel = "LabelCombobox 2"
    ToolbarItemComboBox2.itemCommandUrl = ".uno:ms777Combobox2"
    ToolbarItemComboBox2.itemService = "ms777.ToolbarCombobox2"
    ToolbarItemComboBox2.itemServiceImpl = "ms777.impl.ToolbarCombobox2"

    ToolbarItemSchrollbar.itemLabel = "Label Scrollbar"
    ToolbarItemSchrollbar.itemCommandUrl = ".uno:ms777Scrollbar"
    ToolbarItemSchrollbar.itemService = "ms777.ToolbarScrollbar"
    ToolbarItemSchrollbar.itemServiceImpl = "ms777.impl.ToolbarScrollbar"


#    prop1 = PropertyValue()
#    prop1.Name = "CommandURL" 
#    prop1.Value = itemComboBoxCommandUrl 
#    xt = ssf.createInstanceWithArgumentsAndContext((1, 2, 3, 4, 5, prop1), ctx)
#    xray(xt)

#    xray(xSM)

    createToolbar(toolbarResourceUrl, toolbarUIName)

    for Item in (ToolbarItemComboBox, ToolbarItemComboBox2, ToolbarItemSchrollbar):
        ssf  = unohelper.createSingleServiceFactory( Item,  Item.itemServiceImpl,  (Item.itemService,) )
        xSM.insert(ssf)

    insertToolbarItems(toolbarResourceUrl, (ToolbarItemComboBox, ToolbarItemComboBox2, ToolbarItemSchrollbar))

if __name__ == '__main__':
    doItAll()
    lo_proc.wait() # when run from VS code, better wait until AO is finished. Otherwise, some calls goto nowhere ...
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

Re: Write own Toolbars in Python

Post by JeJe »

Its possible to do it simply even in Basic, in an off-beat way, by adding a normal blank button to the toolbar of the required width and making it disabled (Edit: if I remember rightly). Then adding the awt window with the toolbar as parent at the same position. Things get messed up if the toolbar is hidden/shown etc though.
Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
ms777
Volunteer
Posts: 177
Joined: Mon Oct 08, 2007 1:33 am

Re: Write own Toolbars in Python

Post by ms777 »

... the above code is for AO only. LO uses a more up to date Python version. Python seems not to be fully compatible.
The below code runs on LO 7.4.0.3 on Win 10 / x64.
Some issues: When run from VS Code, an LO process remains alive, which you have to kill manually. Sometimes, you see some black areas. Minify / maxify solves that, the code needs probably some redraw functions where AO does not. Use with care.
ms777

Code: Select all

import uno
import unohelper
from subprocess import Popen

from com.sun.star.util import XUpdatable # type:ignore

from com.sun.star.frame import XToolbarController # type:ignore
from com.sun.star.frame import XStatusListener # type:ignore

from com.sun.star.lang import XInitialization # type:ignore

from com.sun.star.awt import Point # type:ignore
from com.sun.star.awt import XItemListener # type:ignore
from com.sun.star.awt import XAdjustmentListener # type:ignore

from com.sun.star.beans import PropertyValue # type:ignore


# define ctx, doc, smgr for (i) invocation from VS Code or (ii) invocation from OO execute macro
if __name__ == '__main__': 
    # invocation from VS Code
    localContext = uno.getComponentContext()
    resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext )
    # start OO
    lo_proc = Popen('"C:\Program Files\LibreOffice\program\soffice.exe" -accept=socket,host=localhost,port=2002;urp;', shell=True)
    ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )

    smgr = ctx.ServiceManager
    desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
    doc = desktop.loadComponentFromURL('private:factory/scalc', '_default', 0, ())

elif __name__ == 'ooo_script_framework':
    # invocation from OO execute macro
    doc = XSCRIPTCONTEXT.getDocument() # type: ignore
    ctx = XSCRIPTCONTEXT.getComponentContext() # type: ignore
    smgr = ctx.ServiceManager
else:
    raise Exception('__name__ is unknown: ' + __name__) 

def xray(target):
    global ctx
    mspf = ctx.ServiceManager.createInstanceWithContext("com.sun.star.script.provider.MasterScriptProviderFactory", ctx)
    script_provider = mspf.createScriptProvider("")
    script = script_provider.getScript("vnd.sun.star.script:XrayTool._Main.Xray?language=Basic&location=application")
    script.invoke((target,), (), ())

def logMessage(mes):
    global logSheet
    global logRow
    logSheet.getCellByPosition(0,logRow).String = mes
    logRow += 1


# wParent: uno.com.sun.star.awt.XWindow
# x, y, width, height: int
# wServiceName: str
# wAttribute: long
# returns -> uno.com.sun.star.awt..XWindow
def makeAwtWindow(wParent, x, y, width, height, wServiceName, wAttribute):
    global smgr
    global ctx
    from com.sun.star.awt.WindowClass import SIMPLE # type:ignore 

    oToolkit = smgr.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)

    # create WindowDescriptor
    wd = uno.createUnoStruct("com.sun.star.awt.WindowDescriptor")
    wd.Type = SIMPLE
    wd.Parent = wParent
    wd.Bounds = uno.createUnoStruct("com.sun.star.awt.Rectangle", x, y, width, height)
    wd.ParentIndex = -1
    wd.WindowAttributes = wAttribute
    wd.WindowServiceName = wServiceName

    return oToolkit.createWindow(wd)

def TestAwt():
    global doc
    from com.sun.star.awt.WindowAttribute import SHOW # type:ignore 

    parentWindow = doc.CurrentController.Frame.ContainerWindow
    wAttribute = SHOW
    oWindow = makeAwtWindow(parentWindow, 100, 100, 300, 200, "combobox", wAttribute)
    oWindow.setBackground(0XFF00FF)

class ToolbarItem(unohelper.Base, XInitialization, XUpdatable, XToolbarController, XStatusListener):
    def __init__( self, ctx, propModuleIdentifier, propFrame, propServiceManager, propParentWindow, propIdentifier, propCommandUrl, propValue ):
        self.sCommandUrl = propCommandUrl.Value
        print('in ToolbarItem constructor: ' + self.sCommandUrl)
        return
# com.sun.star.lang.XInitialization
    def initialize(self, args):
        logMessage("initialize " + self.sCommandUrl)
# com.sun.star.util.XUpdatable
    def update(self):
        logMessage("update " + self.sCommandUrl)
# com.sun.star.frame.XStatusListener
    def statusChanged(self, state):
        logMessage("statusChanged " + self.sCommandUrl)

# com.sun.star.frame.XToolbarController
    def execute(self, KeyModifier):
        logMessage("execute " + self.sCommandUrl)

    def click(self):
        logMessage("click " + self.sCommandUrl)

    def doubleClick(self):
        logMessage("doubleclick " + self.sCommandUrl)

    def createPopupWindow(self): 
        logMessage("createPopupWindow " + self.sCommandUrl)
        return None

    def createItemWindow(self, wParent):
        pass


class ToolbarItemComboBox(ToolbarItem, XItemListener):
    asOptions = ()
    sText = ""

    def createItemWindow(self, wParent):
        logMessage("createItemWindow start " + self.sCommandUrl)
        wAttribute = uno.getConstantByName("com.sun.star.awt.WindowAttribute.SHOW") | uno.getConstantByName("com.sun.star.awt.VclWindowPeerAttribute.DROPDOWN")

        oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "combobox", wAttribute)
        oWindow.addItemListener(self)
        oWindow.setText(self.sText)
        oWindow.addItems(self.asOptions, 0)
        oWindow.setDropDownLineCount(6)
        logMessage("createItemWindow end " + self.sCommandUrl)
        return oWindow

    def itemStateChanged(self, event):
        self.lSelected = event.Selected
        s = "selection change to item # " + str(self.lSelected)
        if self.lSelected >= 0:
            s = s + ": " + event.Source.getItem(self.lSelected)
        s = s + ', ' + self.sCommandUrl
        logMessage(s)

class ToolbarItemComboBox2(ToolbarItemComboBox):
    pass

class ToolbarItemSchrollbar(ToolbarItem, XAdjustmentListener):

    def createItemWindow(self, wParent):
        logMessage("createItemWindow start " + self.sCommandUrl)
        wAttribute = uno.getConstantByName("com.sun.star.awt.WindowAttribute.SHOW")
        oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "scrollbar", wAttribute)
        oWindow.addAdjustmentListener(self)
        oWindow.setOrientation(uno.getConstantByName("com.sun.star.awt.ScrollBarOrientation.HORIZONTAL"))
        oWindow.setValues(50, 10, 100)
        logMessage("createItemWindow end " + self.sCommandUrl)
        return oWindow

    def adjustmentValueChanged(self, event):
        s = "schrollbar changed to " + str(event.Value) + ', ' + self.sCommandUrl
        logMessage(s)


def createToolbar(toolbarResourceUrl, toolbarUIName):
    from com.sun.star.ui.DockingArea import DOCKINGAREA_TOP # type:ignore 

# delete toolbar if existing
    oLM = doc.CurrentController.Frame.LayoutManager
    if oLM.getElement(toolbarResourceUrl) is None:
        oLM.destroyElement(toolbarResourceUrl)
# create toolbar
    oLM.createElement(toolbarResourceUrl)
    oEL = oLM.getElement(toolbarResourceUrl)
    oToolbarSettings = oEL.getSettings(True)        
    oToolbarSettings.UIName = toolbarUIName 
    oEL.setSettings(oToolbarSettings) 
# dock the toolbar in the docking area. Not sure if necessary.
    oLM.dockWindow(toolbarResourceUrl, DOCKINGAREA_TOP, Point(0, 60)) 

def insertToolbarItems(toolbarResourceUrl, aItem):
# register the itemService at the Toolbarfactory
    oo = smgr.createInstanceWithContext("com.sun.star.frame.ToolbarControllerFactory", ctx)
    oLM = doc.CurrentController.Frame.LayoutManager
    oEL = oLM.getElement(toolbarResourceUrl)
    oToolbarSettings = oEL.getSettings(True)

    for Item in aItem:
        if oo.hasController( Item.itemCommandUrl, ""):
            oo.deregisterController( Item.itemCommandUrl, "")
        oo.registerController( Item.itemCommandUrl, "", Item.itemService)

        prop1 = PropertyValue()
        prop1.Name = "CommandURL" 
        prop1.Value = Item.itemCommandUrl
        prop2 = PropertyValue()
        prop2.Name = "Label" 
        prop2.Value = Item.itemLabel

        uno.invoke( oToolbarSettings, "insertByIndex", (oToolbarSettings.Count, uno.Any("[]com.sun.star.beans.PropertyValue", (prop1, prop2)))) 

    oEL.setSettings(oToolbarSettings) 


def doItAll():
    global logSheet
    global logRow

    print('Hallo')

    #for the logging ...
    logRow = 0
    logSheet = doc.Sheets.getByIndex(0)
    logSheet.getCellRangeByPosition(0,0,0,1000).clearContents(uno.getConstantByName('com.sun.star.sheet.CellFlags.STRING'))

    #
    logMessage('Hallo')

#    TestAwt()

    xSM = ctx.getValueByName("/singletons/com.sun.star.lang.theServiceManager")

    toolbarResourceUrl = "private:resource/toolbar/custom_ms777"
    toolbarUIName = "toolbarUIName ms777"

    ToolbarItemComboBox.asOptions = ( "test", "foo", "bar", "test2", "foo2", "bar2" )
    ToolbarItemComboBox.sText = "Combo Box 1"
    ToolbarItemComboBox.itemLabel = "LabelCombobox 1"
    ToolbarItemComboBox.itemCommandUrl = ".uno:ms777Combobox"
    ToolbarItemComboBox.itemService = "ms777.ToolbarCombobox"
    ToolbarItemComboBox.itemServiceImpl = "ms777.impl.ToolbarCombobox"

    ToolbarItemComboBox2.asOptions = ( "option A", "Karl Heinz")
    ToolbarItemComboBox2.sText = "Combo Box 2"
    ToolbarItemComboBox2.itemLabel = "LabelCombobox 2"
    ToolbarItemComboBox2.itemCommandUrl = ".uno:ms777Combobox2"
    ToolbarItemComboBox2.itemService = "ms777.ToolbarCombobox2"
    ToolbarItemComboBox2.itemServiceImpl = "ms777.impl.ToolbarCombobox2"

    ToolbarItemSchrollbar.itemLabel = "Label Scrollbar"
    ToolbarItemSchrollbar.itemCommandUrl = ".uno:ms777Scrollbar"
    ToolbarItemSchrollbar.itemService = "ms777.ToolbarScrollbar"
    ToolbarItemSchrollbar.itemServiceImpl = "ms777.impl.ToolbarScrollbar"


#    prop1 = PropertyValue()
#    prop1.Name = "CommandURL" 
#    prop1.Value = itemComboBoxCommandUrl 
#    xt = ssf.createInstanceWithArgumentsAndContext((1, 2, 3, 4, 5, prop1), ctx)
#    xray(xt)

#    xray(xSM)

    createToolbar(toolbarResourceUrl, toolbarUIName)

    for Item in (ToolbarItemComboBox, ToolbarItemComboBox2, ToolbarItemSchrollbar):
        ssf  = unohelper.createSingleServiceFactory( Item,  Item.itemServiceImpl,  (Item.itemService,) )
        xSM.insert(ssf)

    insertToolbarItems(toolbarResourceUrl, (ToolbarItemComboBox, ToolbarItemComboBox2, ToolbarItemSchrollbar))

if __name__ == '__main__':
    doItAll()
    lo_proc.wait() # when run from VS code, better wait until AO is finished. Otherwise, some calls goto nowhere ...
ms777
Volunteer
Posts: 177
Joined: Mon Oct 08, 2007 1:33 am

Re: Write own Toolbars in Python

Post by ms777 »

... an update: The below code runs both on LO Win10 64 bit and on AO. If you start from outside LO, you have to modify the Popen line
Now the Toolbar can be switched on and off through the Show Toolbars Menu. On LO, there are still some redraw issues

Good luck,

ms777

Code: Select all

import uno
import unohelper
from subprocess import Popen

from com.sun.star.util import XUpdatable # type:ignore

from com.sun.star.frame import XToolbarController # type:ignore
from com.sun.star.frame import XStatusListener # type:ignore

from com.sun.star.lang import XInitialization # type:ignore

from com.sun.star.awt import Point # type:ignore
from com.sun.star.awt import XItemListener # type:ignore
from com.sun.star.awt import XAdjustmentListener # type:ignore

from com.sun.star.beans import PropertyValue # type:ignore
from com.sun.star.beans.PropertyState import DIRECT_VALUE # type:ignore

from com.sun.star.awt.WindowAttribute import SHOW # type:ignore
from com.sun.star.awt.WindowClass import SIMPLE # type:ignore 
from com.sun.star.awt.VclWindowPeerAttribute import DROPDOWN # type:ignore


# define ctx, doc, smgr for (i) invocation from VS Code or (ii) invocation from OO execute macro
if __name__ == '__main__': 
    # invocation from VS Code
    localContext = uno.getComponentContext()
    resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext )
# start OO
# for LibreOffice
    lo_proc = Popen('"C:\Program Files\LibreOffice\program\soffice.exe" -accept=socket,host=localhost,port=2002;urp;', shell=True)
# for Apache OpenOffice
#    lo_proc = Popen('"C:\Program Files (x86)\OpenOffice 4\program\soffice.exe" -accept=socket,host=localhost,port=2002;urp;', shell=True)
    ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )

    smgr = ctx.ServiceManager
    desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
    doc = desktop.loadComponentFromURL('private:factory/scalc', '_default', 0, ())

elif __name__ == 'ooo_script_framework':
    # invocation from OO execute macro
    doc = XSCRIPTCONTEXT.getDocument() # type: ignore
    ctx = XSCRIPTCONTEXT.getComponentContext() # type: ignore
    smgr = ctx.ServiceManager
else:
    raise Exception('__name__ is unknown: ' + __name__) 

def xray(target):
    global ctx
    mspf = ctx.ServiceManager.createInstanceWithContext("com.sun.star.script.provider.MasterScriptProviderFactory", ctx)
    script_provider = mspf.createScriptProvider("")
    script = script_provider.getScript("vnd.sun.star.script:XrayTool._Main.Xray?language=Basic&location=application")
    script.invoke((target,), (), ())

def logMessage(mes):
    global logSheet
    global logRow
    logSheet.getCellByPosition(0,logRow).String = mes
    logRow += 1


# wParent: uno.com.sun.star.awt.XWindow
# x, y, width, height: int
# wServiceName: str
# wAttribute: long
# returns -> uno.com.sun.star.awt..XWindow
def makeAwtWindow(wParent, x, y, width, height, wServiceName, wAttribute):
    global smgr
    global ctx
    oToolkit = smgr.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)

    # create WindowDescriptor
    wd = uno.createUnoStruct("com.sun.star.awt.WindowDescriptor")
    wd.Type = SIMPLE
    wd.Parent = wParent
    wd.Bounds = uno.createUnoStruct("com.sun.star.awt.Rectangle", x, y, width, height)
    wd.ParentIndex = -1
    wd.WindowAttributes = wAttribute
    wd.WindowServiceName = wServiceName
    return oToolkit.createWindow(wd)

def TestAwt():
    global doc

    parentWindow = doc.CurrentController.Frame.ContainerWindow
    oWindow = makeAwtWindow(parentWindow, 100, 100, 300, 200, "combobox", SHOW)
    oWindow.setBackground(0XFF00FF)

class ToolbarItem(unohelper.Base, XInitialization, XUpdatable, XToolbarController, XStatusListener):
    def __init__( self, ctx, *args ):
        self.sCommandUrl = 'unknown.url'
        for arg in args:
            if isinstance(arg, PropertyValue):
                if arg.Name == 'CommandURL':
                    self.sCommandUrl = arg.Value
                    break

        print('ToolbarItem constructor end: ' + self.sCommandUrl)

        return
# com.sun.star.lang.XInitialization
    def initialize(self, args):
        logMessage("initialize " + self.sCommandUrl)
# com.sun.star.util.XUpdatable
    def update(self):
        logMessage("update " + self.sCommandUrl)
# com.sun.star.frame.XStatusListener
    def statusChanged(self, state):
        logMessage("statusChanged " + self.sCommandUrl)

# com.sun.star.frame.XToolbarController
    def execute(self, KeyModifier):
        logMessage("execute " + self.sCommandUrl)

    def click(self):
        logMessage("click " + self.sCommandUrl)

    def doubleClick(self):
        logMessage("doubleclick " + self.sCommandUrl)

    def createPopupWindow(self): 
        logMessage("createPopupWindow " + self.sCommandUrl)
        return None

    def createItemWindow(self, wParent):
        pass

class ToolbarItemComboBox(ToolbarItem, XItemListener):
    asOptions = ()
    sText = ""

    def createItemWindow(self, wParent):
        logMessage("createItemWindow start " + self.sCommandUrl)

        oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "combobox", SHOW | DROPDOWN)
        oWindow.addItemListener(self)
        oWindow.setText(self.sText)
        oWindow.addItems(self.asOptions, 0)
        oWindow.setDropDownLineCount(6)
        logMessage("createItemWindow end " + self.sCommandUrl)
        return oWindow

    def itemStateChanged(self, event):
        self.lSelected = event.Selected
        s = "selection change to item # " + str(self.lSelected)
        if self.lSelected >= 0:
            s = s + ": " + event.Source.getItem(self.lSelected)
        s = s + ', ' + self.sCommandUrl
        logMessage(s)

class ToolbarItemComboBox2(ToolbarItemComboBox):
    pass

class ToolbarItemSchrollbar(ToolbarItem, XAdjustmentListener):

    def createItemWindow(self, wParent):
        logMessage("createItemWindow start " + self.sCommandUrl)
        oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "scrollbar", SHOW)
        oWindow.addAdjustmentListener(self)
        from com.sun.star.awt.ScrollBarOrientation import HORIZONTAL # type: ignore
        oWindow.setOrientation(HORIZONTAL)
        oWindow.setValues(50, 10, 100)
        logMessage("createItemWindow end " + self.sCommandUrl)
        return oWindow

    def adjustmentValueChanged(self, event):
        s = "schrollbar changed to " + str(event.Value) + ', ' + self.sCommandUrl
        logMessage(s)


def createToolbar(toolbarResourceUrl, toolbarUIName, aItem):

    oUIConfigurationManagerSupplier = smgr.createInstanceWithContext("com.sun.star.ui.ModuleUIConfigurationManagerSupplier", ctx)
    oUIConfigurationManager = oUIConfigurationManagerSupplier.getUIConfigurationManager('com.sun.star.sheet.SpreadsheetDocument')
    oToolbarSettings = oUIConfigurationManager.createSettings()
    oToolbarSettings.UIName = toolbarUIName 

    oToolbarControllerFactory = smgr.createInstanceWithContext("com.sun.star.frame.ToolbarControllerFactory", ctx)
# the below only works for LO
#    oToolbarControllerFactory = ctx.getValueByName("/singletons/com.sun.star.frame.theToolbarControllerFactory")
    oTheServiceManager = ctx.getValueByName("/singletons/com.sun.star.lang.theServiceManager")

    for Item in aItem:
# register the itemService at the Toolbarfactory
        if oToolbarControllerFactory.hasController( Item.itemCommandUrl, 'com.sun.star.sheet.SpreadsheetDocument'):
            oToolbarControllerFactory.deregisterController( Item.itemCommandUrl, 'com.sun.star.sheet.SpreadsheetDocument')
        oToolbarControllerFactory.registerController( Item.itemCommandUrl, 'com.sun.star.sheet.SpreadsheetDocument', Item.itemService)

# add the Item to the oToolbarSettings
        p1 = PropertyValue("CommandURL", 0, Item.itemCommandUrl, DIRECT_VALUE)
        p2 = PropertyValue("Label",      0, Item.itemLabel,      DIRECT_VALUE)
        uno.invoke( oToolbarSettings, "insertByIndex", (oToolbarSettings.Count, uno.Any("[]com.sun.star.beans.PropertyValue", (p1, p2)))) 

# create service factories for each toolbar item and register them
        ssf  = unohelper.createSingleServiceFactory( Item, Item.itemServiceImpl,  (Item.itemService,) )
        oTheServiceManager.insert(ssf)

    oUIConfigurationManager.insertSettings(toolbarResourceUrl, oToolbarSettings)


def doItAll():
    global logSheet
    global logRow

    print('Hallo')

    #for the logging ...
    logRow = 0
    logSheet = doc.Sheets.getByIndex(0)
    logSheet.getCellRangeByPosition(0,0,0,1000).clearContents(uno.getConstantByName('com.sun.star.sheet.CellFlags.STRING'))

    #
    logMessage('Hallo')

#    TestAwt()

    toolbarResourceUrl = "private:resource/toolbar/custom_ms777"
    toolbarUIName = "toolbarUIName ms777"

    ToolbarItemComboBox.asOptions = ( "test", "foo", "bar", "test2", "foo2", "bar2" )
    ToolbarItemComboBox.sText = "Combo Box 1"
    ToolbarItemComboBox.itemLabel = "LabelCombobox 1"
    ToolbarItemComboBox.itemCommandUrl = ".uno:ms777Combobox"
    ToolbarItemComboBox.itemService = "ms777.ToolbarCombobox"
    ToolbarItemComboBox.itemServiceImpl = "ms777.impl.ToolbarCombobox"

    ToolbarItemComboBox2.asOptions = ( "option A", "Karl Heinz")
    ToolbarItemComboBox2.sText = "Combo Box 2"
    ToolbarItemComboBox2.itemLabel = "LabelCombobox 2"
    ToolbarItemComboBox2.itemCommandUrl = ".uno:ms777Combobox2"
    ToolbarItemComboBox2.itemService = "ms777.ToolbarCombobox2"
    ToolbarItemComboBox2.itemServiceImpl = "ms777.impl.ToolbarCombobox2"

    ToolbarItemSchrollbar.itemLabel = "Label Scrollbar"
    ToolbarItemSchrollbar.itemCommandUrl = ".uno:ms777Scrollbar"
    ToolbarItemSchrollbar.itemService = "ms777.ToolbarScrollbar"
    ToolbarItemSchrollbar.itemServiceImpl = "ms777.impl.ToolbarScrollbar"


    createToolbar(toolbarResourceUrl, toolbarUIName, (ToolbarItemComboBox, ToolbarItemComboBox2, ToolbarItemSchrollbar))

# when run from VS Code, better wait until AO is finished. Otherwise, some calls goto nowhere ...
# under AO, a simple lo_proc.wait() is sufficient
# under LO (Win 10, 64bit), a simple lo_proc.wait() is sufficient, if you terminate LO by menu 
# under LO (Win 10, 64bit), the process is not terminated, if you terminate the desktop by closing the window. For this case we do the polling 
if __name__ == '__main__':
    doItAll()
    import time
    while True:
        if lo_proc.poll() == 0:
            break
        if desktop.ActiveFrame is None:
            desktop.terminate()
            break
        time.sleep(1)

    lo_proc.wait() 
Post Reply