слушать выделение текста

Автор Ципихович Эндрю, 13 февраля 2026, 14:17

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

Ципихович Эндрю

здравствуйте, как скриптом питона либры сделать, чтобы скрипт следил за выделением текста в документе, если выделен текст с первым и последним знаком $ скрипт должен запуститься, вставить текст на место выделения?
пробовал код:

from com.sun.star.beans.PropertyState import DIRECT_VALUE
from com.sun.star.document.EventObject import EventObject
from com.sun.star.lang.EventObject import EventObject as LangEventObject
from com.sun.star.text.TextMarkupType import HIDDEN_PARAGRAPH
from com.sun.star.util.SearchFlags import SEARCH_FORWARD
from com.sun.star.util.SearchOptions import SearchOptions
from com.sun.star.frame.DispatchDescriptor import DispatchDescriptor


# Обработчик изменения выделения текста
def OnTextSelected(event):
    doc = XSCRIPTCONTEXT.getDocument()
    controller = doc.CurrentController
    view_cursor = controller.ViewCursor

    if not view_cursor.isCollapsed():  # Если текст выделен
        sel_text = view_cursor.String.strip()

        if sel_text.startswith('$') and sel_text.endswith('$'):  # Проверка на $...$
            new_text = "ЗАМЕНЁННЫЙ_ТЕКСТ"  # Заменяемый текст
            cursor = doc.Text.createTextCursorByRange(view_cursor.Start)
            cursor.gotoRange(view_cursor.End, False)
            cursor.String = new_text

# Регистрация обработчика события выделения текста
def register_listener():
    doc = XSCRIPTCONTEXT.getDocument()
    controller = doc.CurrentController
    listener = SelectionListener()
    controller.addSelectionChangeListener(listener)

# Класс-обработчик события выделения текста
class SelectionListener(unohelper.Base, XSelectionChangeListener):
    def __init__(self):
        pass

    def selectionChanged(self, event):
        OnTextSelected(event)

    def disposing(self, event):
        pass

# Основная функция, запускаемая при открытии документа
def onOpenDoc(event):
    register_listener()  # Регистрируем слушатель события выделения

# Регистрация макроса для события открытия документа
g_exportedScripts = (onOpenDoc,)
получаю ошибку:
Ошибка сценария при выполнении сценария Python vnd.sun.star.script:$-$.py$onOpenDoc?language=Python&location=share.

Сообщение: <class 'AttributeError'>: 'NoneType' object has no attribute 'readBytes'
  File "C:\Program Files\LibreOffice\program\pythonscript.py", line 1096, in getScript
    mod = self.provCtx.getModuleByUrl(fileUri)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\LibreOffice\program\pythonscript.py", line 490, in getModuleByUrl
    src = readTextFromStream(self.sfa.openFileRead(url))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\LibreOffice\program\pythonscript.py", line 154, in readTextFromStream
    read, out = inputStream.readBytes(None, BLOCK_SIZE)
                ^^^^^^^^^^^^^^^^^^^^^
ЧЯДНТ? спасибо

sokol92

Скорее всего, Вы используете в названии модуля Python или функции Python специальные символы.
Допустимы только латинские буквы, цифры, знак подчеркивания.
Владимир.

sokol92

Попробуйте макрос в такой редакции (см. также сообщение об имени модуля Python).
Для добавления слушателя можно также вызывать register_listener.
import uno, unohelper
from com.sun.star.view import XSelectionChangeListener


# Обработчик изменения выделения текста
def OnTextSelected(event):
    doc = XSCRIPTCONTEXT.getDocument()
    controller = doc.CurrentController
    view_cursor = controller.ViewCursor

    if not view_cursor.isCollapsed():  # Если текст выделен
        sel_text = view_cursor.String.strip()

        if sel_text.startswith('$') and sel_text.endswith('$'):  # Проверка на $...$
            new_text = "ЗАМЕНЁННЫЙ_ТЕКСТ"  # Заменяемый текст
            cursor = doc.Text.createTextCursorByRange(view_cursor.Start)
            cursor.gotoRange(view_cursor.End, True)
            cursor.String = new_text


# Регистрация обработчика события выделения текста
def register_listener():
    doc = XSCRIPTCONTEXT.getDocument()
    controller = doc.CurrentController
    listener = SelectionListener()
    controller.addSelectionChangeListener(listener)


# Класс-обработчик события выделения текста
class SelectionListener(unohelper.Base, XSelectionChangeListener):
    def __init__(self):
        pass

    def selectionChanged(self, event):
        OnTextSelected(event)

    def disposing(self, event):
        pass


# Основная функция, запускаемая при открытии документа
def onOpenDoc(event):
    register_listener()  # Регистрируем слушатель события выделения


g_exportedScripts = (onOpenDoc,register_listener,)
Владимир.

Ципихович Эндрю

макрос из сообщения № 2 находится в папке:
C:\Users\start\AppData\Roaming\LibreOffice\4\user\Scripts\python\$-$.py
при открытии документа у которого указано, что при открытии нужно выполнить макрос:
$-$.py$onOpenDoc
получаю ошибку:Ошибка сценария при выполнении сценария Python vnd.sun.star.script:−.py$onOpenDoc?language=Python&location=user.
Сообщение: <class 'AttributeError'>: 'NoneType' object has no attribute 'readBytes'  File "C:\Program Files\LibreOffice\program\pythonscript.py", line 1096, in getScript    mod = self.provCtx.getModuleByUrl(fileUri)          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File "C:\Program Files\LibreOffice\program\pythonscript.py", line 490, in getModuleByUrl    src = readTextFromStream(self.sfa.openFileRead(url))          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File "C:\Program Files\LibreOffice\program\pythonscript.py", line 154, in readTextFromStream    read, out = inputStream.readBytes(None, BLOCK_SIZE)                ^^^^^^^^^^^^^^^^^^^^^
что не так?

sokol92

#4
Цитата: Ципихович Эндрю от 16 февраля 2026, 13:18макрос из сообщения № 2 находится в папке:
C:\Users\start\AppData\Roaming\LibreOffice\4\user\Scripts\python\$-$.py
Скажите пожалуйста, Вы читали сообщение #1 из этой темы?
Имя модуля (у Вас $-$.py) должно состоять из латинских букв, цифр, знака подчеркивания.
Владимир.

Ципихович Эндрю

спасибо, всё осознал, каюсь, работает, спасибо

Ципихович Эндрю

и опять НЕ всё гут, замена происходит НО после неё Ctrl+Z не отменит замену и стрелочка "Отменить" не среагировала - осталась НЕ активной, как будто ничего и НЕ было, как это устранить?

economist

Макрос - билет в один конец. Но можно доработать макрос, чтобы он сохранял состояние до запуска. И тогда Файл Восстановить или просто Открыть - отменит действие макроса. А вот сохранить в истории отмен - тут сложнее. В MSO, с их 25 млрд$ выручки в год, проблема так же не решена, с 1995 г.
Пить не буду коньяка - читану Питоньяка!

sokol92

Цитата: Ципихович Эндрю от 16 февраля 2026, 13:34как это устранить?
Сначала нужно всё обдумать.
Допустим, у Вас будет работать отмена действия (Undo). Вы нажмете на Ctrl+z, после этого восстановится выделение текста $...$, слушатель обработает это выделение и опять заменит текст.

Цитата: economist от 16 февраля 2026, 18:25Макрос - билет в один конец. Но можно доработать макрос, чтобы он сохранял состояние до запуска. И тогда Файл Восстановить или просто Открыть - отменит действие макроса. А вот сохранить в истории отмен - тут сложнее. В MSO, с их 25 млрд$ выручки в год, проблема так же не решена, с 1995 г.
В Excel (или Word) макрос VBA сбрасывает контекст отмены. Microsoft вряд ли будет это менять по соображениям совместимости версий.

В LibreOffice в этом плане (отмена действия макроса) всё несопоставимо лучше.

Владимир.

sokol92

Пример измененного макроса в файле.
Замена текста, заключенного в знаки доллара, должна работать с возможностью отмены.
Пробуйте.
Владимир.

Ципихович Эндрю

Цитата: sokol92 от 16 февраля 2026, 20:47Пробуйте
проблема осталась
Цитата: Ципихович Эндрю от 16 февраля 2026, 13:34замена происходит НО после неё Ctrl+Z не отменит замену и стрелочка "Отменить" не среагировала - осталась НЕ активной, как будто ничего и НЕ было

Ципихович Эндрю

sokol92, посмотрите пожалуйста, Ваш макрос из поста № 2 как смог подправил для вставки изображения:
        if sel_text.startswith('$') and sel_text.endswith('$'):  # Проверка на $...$
            # Путь к файлу изображения
            image_path = r"C:\Users\start\AppData\Roaming\LibreOffice\4\user\Scripts\python\output.svg"
            # Создаём графический объект
            graphic_obj = doc.createInstance("com.sun.star.text.GraphicObject")
            # Устанавливаем URL файла (преобразуем путь в формат LibreOffice)
            graphic_obj.GraphicURL = uno.systemPathToFileUrl(image_path)
            # Очищаем выделенный текст ($...$)
            text_range = view_cursor.getText()
            text_range.setString("")  # Удаляем содержимое выделения
            # Создаём курсор на месте удаления
            cursor = text_range.createTextCursorByRange(view_cursor.Start)
            # Вставляем изображение
            text_range.insertTextContent(cursor, graphic_obj, False)
картинка вставляется но как-то странно)) и со временем документ падает, как подправить?

sokol92

Цитата: Ципихович Эндрю от 18 февраля 2026, 13:25как подправить?
У меня тоже не получается стабилизировать поведение макроса.

Исходная идея со слушателем изменений выделения мне не нравится. Лучше выделить фрагмент текста и явно запустить макрос (можно через кнопку на инструментальной линейке или сочетанием клавиш.
Владимир.

Ципихович Эндрю

спасибо, понятно......
а вообще в макросе есть:
# Создаём графический объект
graphic_obj = doc.createInstance("com.sun.star.text.GraphicObject")
возможно ли не создавать файл с формулы
import matplotlib.pyplot as plt     # Модуль для перевода формулы LaTeX в изображениеа сразу помещать в графический объект либры?

sokol92

Не совсем понял вопрос.
Свойство GraphicURL отмечено как устаревшее, лучше присваивать свойство Graphic. Для получения объекта Graphic из файла можно использовать метод queryGraphic основного интерфейса для работы с графикой GraphicProvider
Владимир.