"Собрать" функцию из переменных.

Автор Tigrik, 7 июня 2020, 16:46

0 Пользователи и 1 гость просматривают эту тему.

Tigrik

Здравствуйте!

Если я ничего не путаю, так как видел это краем глазом, да и достаточно давно, то в Calc можно было "собрать" саму функцию (и её параметры) с помощью переменных (символьных, строковых, любых).
Если это действительно так, то можно такое же сделать и в Basic?
По поиску ничего не нашёл - если пропустил подобные вопросы и ответы, то, пожалуйста, "направьте".

Спасибо.

sokol92

Если Вы имеете в виду функции Basic, которые могут указываться в формулах ячеек листа, то да. Об этом написано в разделе 15.7 книги А.Питоньяка OOME_4_0.odt
Функция должна быть в библиотеке Standard документа или приложения.
Владимир.

Tigrik

Цитата: sokol92 от  7 июня 2020, 17:57
Если Вы имеете в виду функции Basic, которые могут указываться в формулах ячеек листа, то да. Об этом написано в разделе 15.7 книги А.Питоньяка OOME_4_0.odt
Функция должна быть в библиотеке Standard документа или приложения.
Спасибо за ссылку, но у меня есть этот текст. Я не до такой степени владею английским, что бы понимать такого рода текст, но у меня есть переводной Питоньяк, но более ранний.
Там я не нашёл то, о чём спрашиваю.
Поясню на примере.
Допустим, есть две функции, имена которых получаются из переменных. Есть ли возможность эти функции запустить?

Sub my_Proba01()
'  Код
End Sub

Sub my_Proba02()
'  Код
End Sub

Sub Start_Proba()
Dim str$, i%
For i = 1 To 2
str = "my_Proba0" & i
' Запуск функций по циклу: сначала - my_Proba01; затем - my_Proba02
Next
End Sub

sokol92

#3
В LO есть аналог метода Excel Application.Run. Посмотрите ответ  Rami здесь
Владимир.

bigor

#4
Цитата: Tigrik от  7 июня 2020, 18:28Есть ли возможность эти функции запустить?
Да. Третьей процедурой :)
sub run_2_macr
For i=1 to 2
Test_ i
next
End sub


Sub Test_(param)
If param = 1 then test_1
If param = 2 then test_2
End Sub


Sub test_1
Print "Macros1"
End sub


Sub test_2
Print "Macros2"
End sub


Ну или в основной процедуре условие прописать, можно case использовать.
Поддержать разработчиков LibreOffice можно можно тут, а наш форум вот тут

Tigrik

Некоторые бытовые проблемы отвлекли на самом интересном месте.
---
Спасибо большое за ответы.
Прошу извинить, если я что-то "не догоняю".
Цитата: sokol92 от  7 июня 2020, 18:39
В LO есть аналог метода Excel Application.Run. Посмотрите ответ Rami здесь
Правильно ли я понимаю, что это процедура используется для вызова, в данной случае, для макроса "my_Proba01" и в ней достаточно вставить путь до макроса?
Или ещё что-то нужно изменить в ней?

Sub Sub_App
Dim scriptUri$, scriptProvider, script
scriptUri="vnd.sun.star.script:Standard.Module2.my_Proba01?language=Basic&location=document"
scriptProvider=ThisComponent.scriptProvider
script=scriptProvider.getScript(scriptUri)
script.invoke(array(),array(),array())
End Sub

Выдает сообщение:
Ошибка времени выполнения Basic.
Вызвано исключение
Type: com.sun.star.script.provider.ScriptFrameworkErrorException
Message: The following Basic script could not be found:
library: 'Standard'
module: 'Module2'
method: 'my_Proba01'
location: 'document'
.
=======
Цитата: Bigor от  7 июня 2020, 19:00
Да. Третьей процедурой :)

Ну или в основной процедуре условие прописать, можно case использовать.
Возможно, я, опять, не совсем внятно объяснил.
Хотя, предложенное решения, для меня, также интересно и будет ещё "испытываться", но я спрашивал немного о другом.
В этом примере всего 2 запускаемых макроса и их спокойно можно запускать и из первого макроса без всякого цикла: по условию или через Case.
Если, предположим, нужно запустить 100 подобных макросов, то этот вариант как-то, скажем прямо, "не камильфо".
Есть ли возможность запускать макрос, имя которого "собрано", допустим в цикле, прямо из этого же макроса (где этот цикл) без "посредника"-процедуры?

Возможно, что метод, предложенный Rami (через скрипт) и можно реализовать (строка в пути легко "собирается" в цикле), но я, пока, не смог с этим разобраться.

bigor

#6
Цитата: Tigrik от  7 июня 2020, 22:24Выдает сообщение:
Ошибка времени выполнения Basic.
Вызвано исключение
Type: com.sun.star.script.provider.ScriptFrameworkErrorException
Message: The following Basic script could not be found:
library: 'Standard'
module: 'Module2'
method: 'my_Proba01'
а Ваш макрос my_Proba01 во 2-м модуле?

ps еще варианты
Поддержать разработчиков LibreOffice можно можно тут, а наш форум вот тут

sokol92

А Вы попробуйте сделать так, как советует сообщение об ошибке. Добавьте в библиотеку Standard документа модуль Module2, в нем создайте процедуру:

Sub my_Proba01()
 Msgbox "Вызвана  my_Proba01"
End Sub

Владимир.

Tigrik

Цитата: Bigor от  8 июня 2020, 11:45
а Ваш макрос my_Proba01 во 2-м модуле?
Цитата: sokol92 от  8 июня 2020, 11:48
А Вы попробуйте сделать так, как советует сообщение об ошибке. Добавьте в библиотеку Standard документа модуль Module2, в нем создайте процедуру:
Конечно, эта процедура (my_Proba01) находится в нужном модуле и библиотеке - иначе, в ошибке будет пустая строка напротив метода - я уже это проверял.

Но, в данном случае, всё оказалось намного проще - для location было определено document, а нужно было application - подумал же, что здесь "собака порылась" - много вариантов перепробовал, а application не догадался.
Спасибо ссылкам от sokol92 и, особенно, от Bigor, где и было решение:
еще варианты

Sub Sub_App
Dim scriptUri$, scriptProvider, script
scriptUri="vnd.sun.star.script:Standard.Module2.my_Proba01?language=Basic&location=application"
scriptProvider=ThisComponent.scriptProvider
script=scriptProvider.getScript(scriptUri)
script.invoke(array(),array(),array())
End Sub

Всё работает.

Осталось только разобраться и понять, что это за параметры в последней команде:
s.invoke(array(), array(), array())

Tigrik

Цитата: Tigrik от  8 июня 2020, 14:01
Осталось только разобраться и понять, что это за параметры в последней команде:
s.invoke(array(), array(), array())
Нашёл в
последней версии руководства:
Executing Python Scripts
The LibreOffice Application Programming Interface (API) Scripting Framework supports inter-language script execution between Python and Basic, or other supported programming languages for that matter. Arguments can be passed back and fourth across calls, providing they represent primitives data types that both languages recognize, and assuming that the Scripting Framework converts them appropriately.

Syntax
workstation_name = script.invoke(Array(), Array(), Array())
opSysName = script.invoke(Array(), in_outs, Array()) ' in_out is an Array
file_len = script.invoke(Array(systemFilePath), Array(), Array())
normalizedPath = script.invoke(Array(systemFilePath), Array(), Array())

sokol92

#10
Описание здесь (на английском). Если у Вас нет выходных параметров, то в первом массиве Вы указываете (входные) параметры макроса, второй и третий параметры оставляете как есть (пустые массивы). Например (вызываемый макрос имеет 3 параметра):

s.invoke(array("Text", 2, True), array(), array())
Владимир.

Tigrik

Цитата: sokol92 от  8 июня 2020, 15:33
Описание здесь (на английском). Если у Вас нет выходных параметров, то в первом массиве Вы указываете (входные) параметры макроса, второй и третий параметры оставляете как есть (пустые массивы). Например (вызываемый макрос имеет 3 параметра):

s.invoke(array("Text", 2, True), array(), array())
Благодарю, sokol92.
Поэксперементировал и, вроде бы, нормально получается:

Sub Sub_App_03
Dim scriptUri$, sPathIF$
scriptUri = "Standard.Module2.my_Proba03"
sPathIF = "vnd.sun.star.script:" '  РАБОТАЕТ!
' sPathIF = "com.sun.star.script.provider.XScript:" '  НЕ РАБОТАЕТ?
' sPathIF = "com.sun.star.script:" '  НЕ РАБОТАЕТ?
scriptUri = sPathIF & scriptUri & "?language=Basic&location=application"
ThisComponent.scriptProvider.getScript(scriptUri).invoke(array("Text", 2, True), aOutParamIndex, aOutParam)
End Sub

Sub my_Proba03( i, j, k) : Msgbox (i &chr(10)& j &chr(10)& k) : End Sub

Второй и третий массивы - это возвращаемая информация от вызываемой процедуры (индексы параметров и сами значения).

У меня не получилось использовать интерфейс, в котором определен этот метод invoke.
Опять, наверное, что-то не то делаю?

sokol92

#12
Муторное это дело. :) Объясню на примерах, как я это понимаю (здесь есть кому исправить).

Составим процедуру my_Proba04, которая складывает первый и второй параметры и помещает в третий. Вызываем ее и показываем результирующие массивы (через точку с запятой):

' Складывает два первых параметра и помещает в третий
Sub  my_Proba04(i,  j, k)
 k=i+j
End Sub  

Sub Sub_App_04
Dim oScript, aOutParamIndex, aOutParam
   oScript=ThisComponent.scriptProvider.getScript("vnd.sun.star.script:Standard.Module2.my_Proba04?language=Basic&location=application")
   oScript.invoke(array(5, 10, 0), aOutParamIndex, aOutParam)

   Msgbox "aOutParamIndex: " & Join(aOutParamIndex, ";") &  chr(10) &  _
             "aOutParam: " &  Join(aOutParam, ";")   ' выводит содержание массивов, элементы разделяются точкой с запятой
End Sub


Отметим, что в первом параметре метода invoke мы указали три элемента массива: два первых для сложения (5 и 10, соответствуют параметрам i и j процедуры my_Proba04) и третий - "пустышку" (0, аргумент k), так как третий параметр my_Proba04 используется только для формирования результата. Второй и третий аргументы метода invoke (aOutParamIndex, aOutParam) заполняются самим методом invoke.

Поскольку мы описали параметры процедуры my_Proba04 без модификатора Byval, то считается, что мы передаем эти параметры по ссылке, а не по значению. В этом случае они интерпретируются как входные-выходные (inout). Метод invoke нам возвращает массив aOutParamIndex из трех элементов 0,1,2 - номера выходных параметров, а также массив aOutParam из 3 элементов 5,10,15 (15 - это результат работы my_Proba04).

Теперь модифицируем процедуру так:
' Складывает два первых параметра (in) и помещает в третий (out)
Sub  my_Proba04(Byval i,  Byval j, k)
 k=i+j
End Sub  

Мы два первых параметра указали с модификатором Byval, что означает передачу по значению. Они будут интерпретироваться как входные (in). Третий параметр мы должны указать по ссылке (без указания Byval), иначе мы не сможем получить его значение по завершению работы процедуры.
Метод invoke нам возвращает массив aOutParamIndex из одного элемента (у нас в процедуре my_Proba04 только один inout параметр: k) и массив aOutParam из одного элемента (естественно, 15).
Уфф :)
Владимир.

Tigrik

Спасибо sokol92, очень интересно!

Я, немного, потестил различные варианты и выяснилось, что, естественно, можно возвращать сколько угодно параметров, но обязательно объявлять это в вызываемой функции и задавать место в первом массиве invoke для всех параметров (входных и выходных).
Причём, в этом массиве и возвращаемым параметрам можно присваивать значения и их использовать в вызываемой функции, и окончательные значения параметров будут занесены в массив aOutParam.

Это в основной процедуре:

dValue = oScript.invoke(array(5, 10, 6.5, 0, "Text", 0), aOutParamIndex, aOutParam)



Sub  my_Proba04(Byval i,  Byval j, k!, z, strTx$, x#) As Double
k=i + j + k : z=i * j : strTx = "k=i + j + k + " & strTx : x = z / k
my_Proba04 = x
End Sub

Единственное, что не получилось - это с параметрами типа Double.
invoke с ним работает, но значение этого параметра не вносится в aOutParam.
Конечно, можно передать это значение через саму вызываемую функцию, но если таких параметров несколько, то тогда придется возвращать массив значений.

Может быть, что-то не доглядел.

sokol92

#14
Спасибо за исследование!

Вы заметили интересную особенность. Если мы в условиях ответа #12 (оба варианта my_Proba04) вызовем процедуру так:

oScript.invoke(array(5.1, 10, 0), aOutParamIndex, aOutParam)

то (неожиданно, по крайней мере, для меня) получим в возвращаемом значении 15 (а не 15.1).
Не спасает и явное описание параметра k as Double, а также передача третьего элемента массива как Cdbl(0). Помогает только такой трюк (любое дробное значение в "пустышке"):

oScript.invoke(array(5.1, 10, 0.1), aOutParamIndex, aOutParam)


Я могу это интерпретировать (тексты не смотрел, нет свободных пары дней на поиски :)) только таким образом: тип параметра k "угадывается" по значению "пустышки", при этом исходный тип значения где-то теряется по дороге...

Владимир.