Для реализации переноса среды программирования с Basic на Python можно использовать следующие модули.
Используемые модули
import uno
import unohelper
Контекст сценария
В макросе Python для AlterOffice вы можете использовать переменную XSCRIPTCONTEXT из модуля com.sun.star.script.provider.XScriptContext, которая предоставляет следующие интерфейсы:
- com.sun.star.frame.XModel: возвращает текущий объект документа.
- com.sun.star.document.XScriptInvocationContext: возвращает зависимый от вызова объект.
- com.sun.star.frame.XDesktop: возвращает экземпляр службы com.sun.star.frame.Desktop.
- com.sun.star.uno.XComponentContext: возвращает текущий контекст компонента, который используется для указания контекста.
Обратите внимание, что переменная XSCRIPTCONTEXT определена на уровне модуля и не доступна для модулей, импортированных из вашего скрипта.
Контекст компонента
В Basic вы можете получить контекст компонента следующим образом:
ctx = GetDefaultContext()
В Python вы можете получить доступ к контексту компонента через контекст скрипта.
ctx = XSCRIPTCONTEXT.getComponentContext()
Это значение необходимо для создания экземпляров служб.
Менеджер сервисов
В Basic вы можете получить менеджер сервисов следующим образом:
smgr = GetProcessServiceManager()
В Python этот экземпляр является наиболее важным значением для создания экземпляров служб для работы с офисными API.
ctx = XSCRIPTCONTEXT.getComponentContext()
smgr = ctx.getServiceManager()
Для создания экземпляров служб вам нужен как контекст компонента, так и диспетчер служб.
Экземпляр службы
В Basic вы можете создавать службы с помощью встроенной функции следующим образом:
sfa = CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
Или с инициализацией аргументов:
arg = com.sun.star.ui.dialogs.TemplateDescription.FILESAVE_SIMPLE
file_picker = CreateUnoServiceWithArguments("com.sun.star.ui.dialogs.FilePicker", Array(arg))
В Python вы можете работать с интерфейсом com.sun.star.lang.XMultiComponentFactory следующим образом:
ctx = XSCRIPTCONTEXT.getComponentContext()
smgr = ctx.getServiceManager()
sfa = smgr.createInstanceWithContext("com.sun.star.ucb.SimpleFileAccess", ctx)
Если вам нужно инициализировать экземпляр, используйте метод
createInstanceWithArgumentsAndContext
:from com.sun.star.ui.dialogs.TemplateDescription import FILESAVE_SIMPLE
file_picker = smgr.createInstanceWithArgumentsAndContext("com.sun.star.ui.dialogs.FilePicker", (FILESAVE_SIMPLE,) ctx)
или инициализировать после создания экземпляра:
file_picker = smgr.createInstanceWithContext("com.sun.star.ui.dialogs.FilePicker", ctx)
file_picker.initialize((FILESAVE_SIMPLE,))
Сервис с конструктором
В Basic вы можете вызывать сервисную конструкцию из ее модуля:
shell_execute = com.sun.star.system.SystemShellExecute.create()
В Python вы должны создать его экземпляр с помощью метода
XMultiComponentFactory::createInstanceWithArgumentsAndContext
с начальными аргументами или создать экземпляр после создания.Вызов конструктора выполняет проверку типов перед передачей аргументов в метод
createInstanceWithArgumentsAndContext
.Текущий документ
В Basic функция ThisComponent во время выполнения обеспечивает доступ к текущему документу:
doc = ThisComponent
В Python вы можете получить доступ к текущему документу через контекст скрипта:
doc = XSCRIPTCONTEXT.getDocument()
Если вы создали макрос внутри конкретного документа, то объект документа, к которому относится макрос, будет соответствовать этому документу. Если же макрос находится в пользовательской библиотеке или общей библиотеке макросов, то объект документа, к которому обращается макрос, будет находиться в активном окне.
Рабочий стол
В Basic, StarDesktop предоставляют функцию выполнения во время работы:
desktop = StarDesktop()
В Python вы можете получить доступ к рабочему столу через контекст скрипта:
desktop = XSCRIPTCONTEXT.getDesktop()
Экземпляр структуры
В Basic экземпляр структуры может быть создан двумя способами:
Dim a As New com.sun.star.awt.Point
a = CreateUnoStruct("com.sun.star.awt.Point")
В Python вы можете использовать следующие способы создания экземпляра структуры. Импортируйте класс структуры и вызовите его.
from com.sun.star.awt import Point
a = Point() # создать экземпляр со значениями по умолчанию, X=0, Y=0
b = Point(100, 200) # инициализировать с начальными значениями, X=100, Y=200
Вызывая класс для создания нового экземпляра структуры, вы можете очистить его аргументы или передать значения для всех полей. Другими словами, вы не можете передать недостаточное количество аргументов для инициализации. И его порядок должен совпадать с определением структуры в его IDL. Например, экземпляр структуры b, имеющий X=100 и Y=200 в приведенном выше фрагменте кода.
Вы можете инициализировать без импорта класса вашей целевой структуры с помощью функции
uno.createUnoStruct
следующим образом:import uno
a = uno.createUnoStruct("com.sun.star.awt.Point")
b = uno.createUnoStruct("com.sun.star.awt.Point", 100, 200)
Это дает тот же результат, что и в приведенном выше примере. Первый параметр метода
createUnoStruct
— это имя инициализируемой структуры. Следующие аргументы являются начальными значениями для нового экземпляра.Enum
В Basic вы можете получить доступ к модулю enum следующим образом:
ITALIC = com.sun.star.awt.FontSlant.ITALIC
В Python можно использовать следующие способы:
import uno
from com.sun.star.awt.FontSlant import ITALIC
ITALIC = uno.getConstantByName("com.sun.star.awt.FontSlant.ITALIC")
ITALIC = uno.Enum("com.sun.star.awt.FontSlant", "ITALIC")
Данный код позволяет получить доступ к модулю enum в Python при работе с библиотекой UNO и определить перечисляемый тип ITALIC с помощью методов getConstantByName и Enum.
Константы
В Basic вы можете получить доступ к константам через модуль:
BOLD = com.sun.star.awt.FontWeight.BOLD
В Python предусмотрены следующие способы:
import uno
from com.sun.star.awt.FontWeight import BOLD
BOLD = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD")
В этом примере импортируется и устанавливается константа BOLD в модуле com.sun.star.awt.FontWeight с помощью функции getConstantByName из модуля uno.
Последовательность
Последовательность - это набор значений одного типа, который можно обойти в цикле. В языке Basic для этого используется массив, а в Python - кортеж. Список в Python не является подходящим для передачи в качестве значения последовательности.
Строка
Строка Python может содержать более 64K байт.
Если вам нужно написать в сценарии 7-битные символы, отличные от Ascii, напишите комментарий в начале файла. Это стандартные инструкции Python.
# -*- coding: utf_8 -*-
Пожалуйста, прочтите документацию Python для более подробной информации.
Char
В Basic нет специального значения для типа char. В Python класс uno.Char определен для типа char.
import uno
c = uno.Char("a")
Тип
"Type" это метатип UNO, представляющий тип UNO. Начиная с версии 3.2, если вы передаете методу строковое значение в качестве аргумента, который должен быть типом, мост Basic считывает его как тип.
oMap = com.sun.star.container.EnumerableMap.create("string", "string")
В Python для создания нового значения типа можно использовать следующие способы.
import uno
t = uno.getTypeByName("string")
t = uno.Type("string", uno.Enum("com.sun.star.uno.TypeClass", "STRING"))
Последовательность байтов
Последовательность байтов - это набор чисел, каждое из которых занимает один байт. В Basic для хранения такой последовательности можно использовать массив байтов.
В Python такую последовательность можно представить в виде строки str, которая обернута в класс uno.ByteSequence. Если вы получаете из UNO какую-то последовательность байтов, то она будет представлена в виде объекта типа uno.ByteSequence. Чтобы получить реальное значение этой последовательности, нужно обратиться к значению переменной объекта.
Исключение
В Basic вы получаете исключение как некоторую ошибку. И для ее перехвата используется оператор On Error.
Sub ErrorExample
On Error GoTo Handler
' ... error
Exit Sub
Handler:
End Sub
В Python исключение, созданное в UNO, можно рассматривать как обычное исключение Python. Вот пример:
from com.sun.star.container import IndexOutOfBoundsException
try:
obj.getByIndex(100) # raises IndexOutOfBoundsException
except IndexOutOfBoundsException as e:
print(e)
Если метод
getByIndex
вызывает IndexOutOfBoundsException
, его можно перехватить в операторе exclude, поскольку все исключения наследуют класс Python Exception.А также вы можете выдать исключение UNO из своего кода Python следующим образом:
from com.sun.star.uno import RuntimeException
raise RuntimeException("Some message", None)
Пустое значение
В Basic возможна ситуация, когда переменная не имеет значения, то есть она может быть равна нулю или пустой. Это нежелательно, так как такие переменные могут привести к ошибкам в работе программы.
None - это специальный объект-сентинель в Python, который используется для обозначения отсутствия значения или недействительности. В UNO None используется, чтобы указать на недопустимый или отсутствующий объект, а также в качестве возвращаемого значения методов типа void. Это может быть полезно при передаче аргументов в методы, которые могут принимать нулевые значения. Однако, результат такой передачи зависит от реализации метода.
Контейнеры
Объект контейнера в Basic представляет собой некий список или массив, который можно обходить поочередно для доступа к его элементам.
В Python такой ярлык, как в Basic, не используется. Вместо этого для доступа к элементам индексируемого контейнера в Python используются циклы, в которых генерируются последовательные индексы с помощью функции
range()
. Таким образом, можно последовательно обойти все элементы контейнера и выполнить необходимые операции с каждым из них.for i in range(container.getCount()):
obj = container.getByIndex(i)
URL-адрес и системный путь
import uno
path = "/home/foo/Documents/file.odt"
url = uno.systemPathToFileUrl(path)
path = uno.fileUrlToSystemPath(url)
Аргументы и возвращаемое значение
В Basic режим первого аргумента метода parseStrict - "in out" в следующем коде:
aURL = CreateUnoStruct("com.sun.star.util.URL")
aURL.Complete = ".uno:Paste"
CreateUnoService("com.sun.star.util.URLTransformer").parseStrict(aURL)
Содержимое переменной aURL обновляется после вызова метода.
В Python параметр out mode возвращается как часть возвращаемого значения.
from com.sun.star.util import URL
aURL = URL()
aURL.Complete = ".uno:Paste"
dummy, aURL = smgr.createInstanceWithContext("com.sun.star.util.URLTransformer", ctx).parseStrict(aURL)
# Definition of com.sun.star.util.XURLTransformer::parseStrict method:
# void parseStrict([inout] com.sun.star.util.URL aURL);
Если метод имеет режим out в своих параметрах, его возвращаемым значением всегда является кортеж, содержащий исходное возвращаемое значение и значения для параметров out. Вот потенциальный пример:
# boolean getSomeValue([in] string aName, [out] short aNum, [inout] long aNum2);
result, num, num2 = obj.getSomeValue("foo", 100, 200)
В данном примере исходное возвращаемое значение передается в переменную результата, а значения второго и третьего параметров передаются в переменные num и num2 соответственно. Хотя второй параметр метода принимает значение 100, оно не используется в методе в качестве входного значения. Также стоит отметить, что возвращаемый кортеж не содержит записи для параметра in mode.
Функции для выполнения
В Basic вы не можете выбирать подпрограммы, которые будут выполняться пользователями.
Определите переменную g_exportedScripts, которая содержит кортеж callable в вашем файле макроса.
def func_a(): pass
def func_b(): pass
def func_hidden(): pass # not shown in the UI
g_exportedScripts = func_a, func_b
В приведенном выше коде func_hidden не отображается в диалоговом окне выполнения макросов.
Импорт модулей
В Basic вы можете использовать подпрограммы из других модулей или библиотек, которые были загружены.
BasicLibraries.loadLibrary("Library1")
Library1.Foo()
В языке Python вы можете импортировать модули, которые находятся в списке sys.path. Если вы хотите использовать свой собственный модуль, который находится в каталоге Scripts/python, поместите его в каталог pythonpath рядом со своим скриптом.
- Scripts/
- python/
- macro.py
- pythonpath/ # этот каталог добавляется автоматически перед выполнением вашего макроса
- your_module.py # этот модуль можно найти
Когда вы запускаете макрос из своего скрипта, внутренний исполнитель автоматически добавляет каталог pythonpath/ в список sys.path, чтобы вы могли использовать его для поиска модулей.
Обратите внимание, что имена модулей могут конфликтовать друг с другом, если у вас есть модуль с одинаковым именем в каком-то месте. Это стандартный механизм импорта модулей в Python. Чтобы избежать конфликта имен, поместите каждый модуль в библиотеку с уникальным именем.
Диалоговое окно
В Basic существует функция CreateUnoDialog runtime для создания экземпляра диалогового окна.
DialogLibraries.loadLibrary("Standard")
dialog = CreateUnoDialog(DialogLibraries.Standard.Dialog1)
dialog.execute()
dialog.dispose()
В Python такая функция быстрого доступа не предусмотрена, но вы можете легко создать диалоговое окно с помощью сервиса.
def dialog_example():
ctx = XSCRIPTCONTEXT.getComponentContext()
smgr = ctx.getServiceManager()
dp = smgr.createInstanceWithContext("com.sun.star.awt.DialogProvider", ctx)
dialog = dp.createDialog("vnd.sun.star.script:Standard.Dialog1?location=user")
dialog.execute()
dialog.dispose()
Окно сообщения
В Basic вы можете использовать функцию MsgBox runtime для отображения сообщения пользователям.
Msgbox "Hello."
В Python функция быстрого доступа не предусмотрена, но вы можете использовать интерфейс через com.sun.star.awt.XMessageBoxFactory.
def messagebox(ctx, parent, message, title, message_type, buttons):
""" Show message in message box. """
toolkit = parent.getToolkit()
older_imple = check_method_parameter(
ctx, "com.sun.star.awt.XMessageBoxFactory", "createMessageBox",
1, "com.sun.star.awt.Rectangle")
if older_imple:
msgbox = toolkit.createMessageBox(
parent, Rectangle(), message_type, buttons, title, message)
else:
message_type = {"messbox": 0, "infobox": 1, "warningbox": 2, "errorbox": 3, "querybox": 4}[message_type]
msgbox = toolkit.createMessageBox(
parent, message_type, buttons, title, message)
n = msgbox.execute()
msgbox.dispose()
return n
def check_method_parameter(ctx, interface_name, method_name, param_index, param_type):
""" Check the method has specific type parameter at the specific position. """
cr = create_service(ctx, "com.sun.star.reflection.CoreReflection")
try:
idl = cr.forName(interface_name)
m = idl.getMethod(method_name)
if m:
info = m.getParameterInfos()[param_index]
return info.aType.getName() == param_type
except:
pass
return False
Поле ввода
Для Python не предусмотрена функция, вы можете создать самостоятельно. Вот пример:
def inputbox(message, title="", default="", x=None, y=None):
""" Shows dialog with input box.
@param message message to show on the dialog
@param title window title
@param default default value
@param x dialog positio in twips, pass y also
@param y dialog position in twips, pass y also
@return string if OK button pushed, otherwise zero length string
"""
WIDTH = 600
HORI_MARGIN = VERT_MARGIN = 8
BUTTON_WIDTH = 100
BUTTON_HEIGHT = 26
HORI_SEP = VERT_SEP = 8
LABEL_HEIGHT = BUTTON_HEIGHT * 2 + 5
EDIT_HEIGHT = 24
HEIGHT = VERT_MARGIN * 2 + LABEL_HEIGHT + VERT_SEP + EDIT_HEIGHT
import uno
from com.sun.star.awt.PosSize import POS, SIZE, POSSIZE
from com.sun.star.awt.PushButtonType import OK, CANCEL
from com.sun.star.util.MeasureUnit import TWIP
ctx = uno.getComponentContext()
def create(name):
return ctx.getServiceManager().createInstanceWithContext(name, ctx)
dialog = create("com.sun.star.awt.UnoControlDialog")
dialog_model = create("com.sun.star.awt.UnoControlDialogModel")
dialog.setModel(dialog_model)
dialog.setVisible(False)
dialog.setTitle(title)
dialog.setPosSize(0, 0, WIDTH, HEIGHT, SIZE)
def add(name, type, x_, y_, width_, height_, props):
model = dialog_model.createInstance("com.sun.star.awt.UnoControl" + type + "Model")
dialog_model.insertByName(name, model)
control = dialog.getControl(name)
control.setPosSize(x_, y_, width_, height_, POSSIZE)
for key, value in props.items():
setattr(model, key, value)
label_width = WIDTH - BUTTON_WIDTH - HORI_SEP - HORI_MARGIN * 2
add("label", "FixedText", HORI_MARGIN, VERT_MARGIN, label_width, LABEL_HEIGHT,
{"Label": str(message), "NoLabel": True})
add("btn_ok", "Button", HORI_MARGIN + label_width + HORI_SEP, VERT_MARGIN,
BUTTON_WIDTH, BUTTON_HEIGHT, {"PushButtonType": OK, "DefaultButton": True})
add("btn_cancel", "Button", HORI_MARGIN + label_width + HORI_SEP, VERT_MARGIN + BUTTON_HEIGHT + 5,
BUTTON_WIDTH, BUTTON_HEIGHT, {"PushButtonType": CANCEL})
add("edit", "Edit", HORI_MARGIN, LABEL_HEIGHT + VERT_MARGIN + VERT_SEP,
WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(default)})
frame = create("com.sun.star.frame.Desktop").getCurrentFrame()
window = frame.getContainerWindow() if frame else None
dialog.createPeer(create("com.sun.star.awt.Toolkit"), window)
if not x is None and not y is None:
ps = dialog.convertSizeToPixel(uno.createUnoStruct("com.sun.star.awt.Size", x, y), TWIP)
_x, _y = ps.Width, ps.Height
elif window:
ps = window.getPosSize()
_x = ps.Width / 2 - WIDTH / 2
_y = ps.Height / 2 - HEIGHT / 2
dialog.setPosSize(_x, _y, 0, 0, POS)
edit = dialog.getControl("edit")
edit.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(default))))
edit.setFocus()
ret = edit.getModel().Text if dialog.execute() else ""
dialog.dispose()
return ret
Эта функция может быть вызвана следующим образом:
pnputbox("Please input some value", "Input", "Default value")
Выполнение макросов с помощью кнопок панели инструментов
В Basic вы можете вызывать любые подпрограммы или функции с помощью кнопок на панели инструментов.
Sub WriteHello()
ThisComponent.getText().getEnd().setString("Hello!")
End Sub
Для того чтобы выполнить функцию с помощью кнопки панели инструментов в Python, необходимо определить свою функцию с аргументом или аргументами переменной длины, чтобы можно было передать аргумент, когда функция будет вызываться с помощью назначенной кнопки на панели инструментов.
def writeHello(*args):
# writeHello(arg): тоже нормально, но если вы хотите вызвать эту функцию
# каким-то образом определите, что ваша функция принимает переменные аргументы.
XSCRIPTCONTEXT.getDocument().getText().getEnd().setString("Hello!")