Запуск макроса для либре врайтера на ЯП Питон по клику ПКМ

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

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

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

что-то опять у меня картинка НЕ складывается, код, который имею:
import uno
from com.sun.star.awt import XMouseClickHandler
from com.sun.star.view.SelectionType import SINGLE
import unohelper
import traceback
import re
import winsound  # Модуль для воспроизведения звукового сигнала

ctx = uno.getComponentContext()
smgr = ctx.ServiceManager
handler = None
controller = None  # Глобальное объявление контроллера

def Message_box(title, message):
    """Выводит окно сообщения"""
    try:
        toolkit = smgr.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)
        desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
        frame = desktop.getCurrentFrame()
        if frame:
            window = frame.getContainerWindow()
            msgbox = toolkit.createMessageBox(window, 'infobox', 1, title, message)
            msgbox.execute()
            msgbox.dispose()
        else:
            Message_box("Ошибка окна", f"{title}: Окно недоступно.")
    except Exception as e:
        Message_box("Ошибка показа сообщения", f"Ошибка: {e}")

class MouseHandler(unohelper.Base, XMouseClickHandler):
    """Класс-обработчик событий мыши"""
    def __init__(self):
        self.click_count = 0
 
    def mouseReleased(self, ev):
        global controller  # Работа с глобальной переменной
        try:
            # Определение места клика
            view_cursor = controller.ViewCursor
            pos_view_cursor = view_cursor.getPosition()  # Запоминаем позицию ViewCursor
           
            # Создание отдельного TextCursor для манипуляции текстом
            text_cursor = view_cursor.Text.createTextCursor()
           
            # Переходим в начало текущего абзаца
            text_cursor.gotoStartOfParagraph(True)
            para_start = text_cursor.getPosition()
           
            # Переходим в конец текущего абзаца
            text_cursor.gotoEndOfParagraph(True)
            para_end = text_cursor.getPosition()
           
            # Копируем текст текущего абзаца
            text_cursor.setSelection(text_cursor)
            paragraph_text = text_cursor.String.strip()
           
            # Индексация позиций формул
            formulas_positions = [(m.start(), m.end()) for m in re.finditer(r'\$.+?\$', paragraph_text)]
           
            # Индекс текущего положения курсора в пределах абзаца
            current_pos = pos_view_cursor - para_start
           
            for start_pos, end_pos in formulas_positions:
                # Проверяем, попала ли позиция курсора в пределы найденной формулы
                if start_pos <= current_pos < end_pos:
                    # Выделяем всю формулу
                    text_cursor.gotoStart(False)
                    text_cursor.goRight(para_start + start_pos, False)
                    text_cursor.goRight((para_start + end_pos)-start_pos, True)
                   
                    # Воспроизводим звуковой сигнал
                    winsound.MessageBeep(winsound.MB_OK)
                   
                    # Сообщение о событии
                    Message_box('Событие ЛКМ внутри формулы вида "$...$"', str(ev))
                    return False
        except Exception as ex:
            tb = traceback.format_exc()
            Message_box('Ошибка:', f'{ex}\n\nОтладочная информация:\n{tb}')
        return False     
     
    def disposing(self, ev):
        pass
       

def start_mouse_handler(_=None):
    """Запуск обработчика событий мыши"""
    global handler, controller
    try:
        desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
        doc = desktop.getCurrentComponent()
        controller = doc.CurrentController
        if handler == None:
            handler = MouseHandler()
        controller.addMouseClickHandler(handler)

    except Exception as e:
        Message_box('Критическая ошибка', str(e))

g_exportedScripts = (start_mouse_handler,)  # Экспорт скрипта
   
получаю ошибку:
getPosition

Отладочная информация:
Traceback (most recent call last):
  File "C:\Users\start\AppData\Roaming\LibreOffice\4\user\Scripts\python\Mouse.py", line 48, in mouseReleased
    para_start = text_cursor.getPosition()
                 ^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: getPosition. Did you mean: 'RubyPosition'?
просьба поправить работу макроса
везде где ни гуглил был намёк, на то что у меня в коде: Переходим в начало\конец текущего абзаца - не очень нравиться, как-бы учесть, что речь про строку, а не про абзац, а то в абзаце если его понимать дословно формула может и быть, но её может не быть в месте клика ЛКМ, спасибо

sokol92

Выгрузите, пожалуйста, файл (.odt).
Из общих соображений.
Мы обрабатываем событие mouseReleased. В этот момент объект, по которому мы щелкнули левой кнопкой мыши, уже выделен. Это controller.getSelection(). Никакие курсоры не нужны.   
Владимир.

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

Цитата: sokol92 от 11 марта 2026, 14:45Выгрузите, пожалуйста, файл (.odt)
ок
Цитата: sokol92 от 11 марта 2026, 14:45Мы обрабатываем событие mouseReleased. В этот момент объект, по которому мы щелкнули левой кнопкой мыши, уже выделен. Это controller.getSelection(). Никакие курсоры не нужны.
так речь про клик, клик это не НЕ дабклик, он же не будет выделен-по-моему так))

sokol92

Так это мой файл - в нем нет формул.
Приложите свой файл и опишите подробно свой замысел - что именно Вы собираетесь сделать (что выделить, куда щелкнуть) и что бы Вы хотели от макроса. Что Вы имеете в виду под термином "формула" (в Writer есть свои определения).

Цитата: Ципихович Эндрю от 11 марта 2026, 15:18так речь про клик, клик это не НЕ дабклик, он же не будет выделен-по-моему так))
В примере из #25 можно поэкспериментировать, например:
1. Щелкнуть левой кнопкой мыши по изображению
2. Нажать левую кнопку мыши, выделить фрагмент текста, отпустить левую кнопку мыши.

Владимир.

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

Цитата: sokol92 от 11 марта 2026, 15:45Так это мой файл - в нем нет формул
у Вас же был MouseHandler.odt я в его имени для отличия специально добавил 1 - MouseHandler1.odt
ну да забыл добавьте пожалуйста текст например:
тмдавтмдватлпми392к9$S = \pi r^2$02305гк02оашцфаоошщцфоа5екеуецу
и получается если сделал клик ЛКМ внутри формулы $S = \pi r^2$ - она должна выделиться 

sokol92

Давайте уточним задачу.

1. Под "формулой" мы понимаем фрагмент текста в пределах одного параграфа, ограниченный с двух сторон знаком доллара. Символы внутри этого фрагмента могут быть любыми.
2. Предлагаю использовать для выделения именно двойной щелчок левой кнопки мыши - это больше соответствует привычкам пользователей Writer.

Всё так?
Владимир.

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

sokol92, так как наиболее точный разбор формула\НЕ формула можно добиться
from sympy import symbols, Eq, parse_expr

def check_if_formula(formula_str):
    try:
        x, y = symbols('x y')  # Объявляем символы
        expression = parse_expr(formula_str)  # Пробуем разобрать строку как выражение
        return True
    except Exception as e:
        return False

# Проверяем формулу
my_formula = "x^2 + y^2"
result = check_if_formula(my_formula)
if result:
    print(f"{my_formula}: Это формула")
else:
    print(f"{my_formula}: Это НЕ формула")
а импортировать sympy - это лишний наворот, остальные проверки, например:
    """Проверяет наличие типичных команд и конструкций LaTeX"""
    math_form = ['\\mu', '\\pi', '\\frac', '\\sqrt', '^', '_', 'sin', 'cos', 'tan', '+', '-', '*', '/']
это 50\50, тогда соглашусь с вашими двумя пунктами за исключением, того что формула должна быть расположена на одной строке, а не в одном абзаце


sokol92

#37
Цитата: Ципихович Эндрю от Вчера в 08:19формула должна быть расположена на одной строке
В текстовом документе Writer "единицей измерения" является параграф. На строки полагаться нельзя, поскольку они зависят, например, от размера листа печати, полей, шрифтов и.д.

Можно попробовать в параграфе, к которому относится двойной щелчок мыши, последовательно искать "формулы LaTeX" и выделять ту формулу, которая соответствует выделению. Если такой "формулы" нет, то двойной щелчок будет работать как обычно.

Цитата: Ципихович Эндрю от Вчера в 08:19from sympy import
Я (пока) использую  исключительно Python Standard Library - в противном случае круг пользователей макросов будет ограничен.


Если нет возражений, то сделаю в ближайшее время (если не будет других желающих).
Владимир.

sokol92

Можно посмотреть пример.
Если курсор мыши находится внутри "формулы", то при двойном щелчке мыши должна быть выделена "формула", иначе выполняются стандартные действия LibreOffice Writer.
Владимир.