Как я могу вызвать макрос Python в формуле ячейки в OpenOffice.Org Calc? - PullRequest
7 голосов
/ 29 сентября 2011

Чтобы расширить возможности OpenOffice, я определил некоторые макросы Python в файле в каталоге пользовательских сценариев (~ / Библиотека / Поддержка приложений / OpenOffice.org / 3 / user / Scripts / python /, в моем случае). Макросы видны в макросе Python. Однако использование функций в формуле ячейки приводит к «#NAME?» ( Ошибка OO.org 525).

Предположим, я определил следующую функцию:

def pytype(val):
    return str(type(val))

Как мне позвонить pytype в формуле ячейки (например, =PYTYPE("string"))?

Фон

Я импортирую некоторые данные из Authorize.net в базу данных MySQL для анализа. MySQL не может анализировать формат даты и времени, используемый Authorize.net, в поле DATETIME или TIMESTAMP, поэтому я пытаюсь преобразовать данные в формат, который MySQL может обработать перед импортом. OpenOffice также не распознает данные как дату и время, и, насколько мне удалось определить, OO.Org не имеет универсальной функции анализа даты. Таким образом, я расширяю возможности OO.org.

Существуют и другие подходы к более широкой проблеме. Например, я также мог бы попытаться исправить данные в пост-импорте MySQL, используя дополнительные столбцы. На самом деле, это то, что я сделал в первый раз; тем не менее, в таблице уже есть данные, с которыми нужно бороться. Из-за этого, а также из-за того, что в будущем появятся другие задачи, которые я надеюсь решить, используя макросы в формулах, сейчас я в основном заинтересован в вызове макросов Python в формулах.

Ответы [ 2 ]

5 голосов
/ 29 сентября 2011

На старых форумах OO.org (супер) пользователь Villeroy разместил иллюстрацию того, как вызывать функции Python из OO.org Basic , которые затем можно использовать в формулах.Ключом является использование службы com.sun.star.script.provider.MasterScriptProviderFactory в качестве моста.Вот адаптация его решения, обобщенного для вызова произвольных функций в произвольных модулях:

REM Keep a global reference to the ScriptProvider, since this stuff may be called many times: 
Global g_MasterScriptProvider as Object
REM Specify location of Python script, providing cell functions: 
Const URL_Main as String = "vnd.sun.star.script:" 
Const URL_Args as String = "?language=Python&location=user" 

Function invokePyFunc(file AS String, func As String, args As Array, outIdxs As Array, outArgs As Array)
   sURL = URL_Main & file & ".py$" & func & URL_Args
   oMSP = getMasterScriptProvider()
   On Local Error GoTo ErrorHandler
      oScript = oMSP.getScript(sURL)
      invokePyFunc = oScript.invoke(args, outIdxs, outArgs)
      Exit Function
   ErrorHandler:
      Dim msg As String, toFix As String
      msg = Error$
      toFix = ""
      If 1 = Err AND InStr(Error$, "an error occurred during file opening") Then
         msg = "Couldn' open the script file."
         toFix = "Make sure the 'python' folder exists in the user's Scripts folder, and that the former contains " & file & ".py."
      End If
      MsgBox msg & chr(13) & toFix, 16, "Error " & Err & " calling " & func
end Function

Function getMasterScriptProvider() 
   if isNull(g_MasterScriptProvider) then 
      oMasterScriptProviderFactory = createUnoService("com.sun.star.script.provider.MasterScriptProviderFactory") 
      g_MasterScriptProvider = oMasterScriptProviderFactory.createScriptProvider("") 
   endif 
   getMasterScriptProvider = g_MasterScriptProvider
End Function

Затем его можно использовать для создания базовой функции OO.org, вызываемой в формуле.Используя пример pytype:

Const libfile as String = "util"    REM functions live in util.py

Function pytype(value)
    pytype = invokePyFunc(libfile, "pytype", Array(value), Array(), Array())
End Function

Другой потенциальной реализацией является создание надстройки Python .Однако это гораздо более тяжелый вариант, поскольку он требует установки OpenOffice SDK, и для меня не очевидно, будет ли этот подход работать для свободных функций или только для классов.

3 голосов
/ 24 января 2012

outis - спасибо за ваш потрясающий ответ. Если бы не ты, я бы сейчас сошел с ума, все еще писать базовые макросы!

У меня есть несколько замечаний:

Последние 2 аргумента invokePyFunc всегда пусты - просто используйте это:

const filename = "your_file"

Function pyFunc(func as String, args as Array)
    pyFunc = invokePyFunc(filename, func, args, Array(), Array())
End Function

Многомерные массивы сложно вернуть. Если вы вернете ((1,2,3), (4,5,6)), calc будет считать 2 ячейки подряд, содержащие неизвестные объекты.

Это потому, что basic и python по-разному относятся к многомерным массивам.

Если вы вернете такую ​​структуру к базовой, вы должны получить к ней доступ, как data(row)(col), и calc ожидает data(row, col) для многомерных массивов.

Из-за этого вам нужно использовать функцию преобразователя для возвращаемых значений:

' Converts python multidimensional arrays to basic arrays.
function convPy2Basic(pyvalue)
    if isarray(pyvalue) then
        dim lastRow as integer 
        lastRow = ubound(pyvalue)
        if lastRow = -1 then
            ' empty array
            convPy2Basic = ""
        else
            if isarray(pyvalue(0)) then
                ' Multi-dimensional array
                dim maxCols as integer, lastCol as integer
                maxCols = ubound(pyvalue(0))

                dim res(lastRow, maxCols)
                for rowIndex = 0 to lastRow
                    lastCol = ubound(pyvalue(rowIndex))

                    ' Expand array if needed.
                    if lastCol > maxCols then
                        maxCols = lastCol
                        redim preserve res(lastRow, maxCols)
                    end if

                    for colIndex = 0 to lastCol
                        res(rowIndex, colIndex) = pyvalue(rowIndex)(colIndex)
                    next colIndex
                next rowIndex

                convPy2Basic = res
            else
                ' Single-dimensional array - this is supported by libreoffice
                convPy2Basic = pyvalue
            end if
        end if
    else
        convPy2Basic = pyvalue
    end if
end function

Function invokeScriptFunc(file AS String, lang, ext, func As String, args As Array, outIdxs As Array, outArgs As Array)
   sURL = URL_Main & file & "." & ext & "$" & func & "?language=" & lang & "&location=user" 
   oMSP = getMasterScriptProvider()
   oScript = oMSP.getScript(sURL)
   invokeScriptFunc = oScript.invoke(args, outIdxs, outArgs)
end Function

Function invokePyFunc(file AS String, func As String, args As Array, outIdxs As Array, outArgs As Array)
   res = invokeScriptFunc(file, "Python", "py", func, args, outIdxs, outArgs)
   invokePyFunc = convPy2Basic(res)
end Function

Итак, мой макро-мост Python-Basic выглядит так:

' Keep a global reference to the ScriptProvider, since this stuff may be called many times: 
Global g_MasterScriptProvider as Object
' Specify location of Python script, providing cell functions: 
Const URL_Main as String = "vnd.sun.star.script:"

' Converts python multidimensional arrays to basic arrays.
function convPy2Basic(pyvalue)
    if isarray(pyvalue) then
        dim lastRow as integer 
        lastRow = ubound(pyvalue)
        if lastRow = -1 then
            ' empty array
            convPy2Basic = ""
        else
            if isarray(pyvalue(0)) then
                ' Multi-dimensional array
                dim maxCols as integer, lastCol as integer
                maxCols = ubound(pyvalue(0))

                dim res(lastRow, maxCols)
                for rowIndex = 0 to lastRow
                    lastCol = ubound(pyvalue(rowIndex))

                    ' Expand array if needed.
                    if lastCol > maxCols then
                        maxCols = lastCol
                        redim preserve res(lastRow, maxCols)
                    end if

                    for colIndex = 0 to lastCol
                        res(rowIndex, colIndex) = pyvalue(rowIndex)(colIndex)
                    next colIndex
                next rowIndex

                convPy2Basic = res
            else
                ' Single-dimensional array - this is supported by libreoffice
                convPy2Basic = pyvalue
            end if
        end if
    else
        convPy2Basic = pyvalue
    end if
end function

Function invokeScriptFunc(file AS String, lang, ext, func As String, args As Array, outIdxs As Array, outArgs As Array)
   sURL = URL_Main & file & "." & ext & "$" & func & "?language=" & lang & "&location=user" 
   oMSP = getMasterScriptProvider()
   oScript = oMSP.getScript(sURL)
   invokeScriptFunc = oScript.invoke(args, outIdxs, outArgs)
end Function

Function invokePyFunc(file AS String, func As String, args As Array, outIdxs As Array, outArgs As Array)
   res = invokeScriptFunc(file, "Python", "py", func, args, outIdxs, outArgs)
   invokePyFunc = convPy2Basic(res)
end Function

Function getMasterScriptProvider() 
   if isNull(g_MasterScriptProvider) then 
      oMasterScriptProviderFactory = createUnoService("com.sun.star.script.provider.MasterScriptProviderFactory") 
      g_MasterScriptProvider = oMasterScriptProviderFactory.createScriptProvider("") 
   endif 
   getMasterScriptProvider = g_MasterScriptProvider
End Function

const filename = "skaiciuokle"

Function pyFunc(func as String, args as Array)
    pyFunc = invokePyFunc(filename, func, args, Array(), Array())
End Function

И используется так:

function DamageToArmor(data, damageType as String, armorType as String, dmgPerGun as Integer, guns as Integer)
    DamageToArmor = pyFunc("dmg2armor", Array(data, damageType, armorType, dmgPerGun, guns))
end function
...