LibreOffice Writer Элементы управления

Автор Ципихович Эндрю, 16 апреля 2026, 06:34

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

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

sokol92, большое спасибо за ваши ответы, но Вы меня увели от сути=соли топика, в картинке я показал о чём речь, не в курсе
Цитата: Ципихович Эндрю от 16 апреля 2026, 06:34Вопрос: можно ли и как программно макросом на ЯП питон вызвать свою панель предварительно создав её?

sokol92

#16
Цитата: Ципихович Эндрю от 16 апреля 2026, 06:34Вопрос: можно ли и как программно макросом на ЯП питон вызвать свою панель предварительно создав её?
Мы создали свою панель управления (с экзотическим названием "My little custom toolbar=Моя маленькая пользовательская панель инструментов").
Мы не сохранили панель - как это сделать, показано здесь.

P.S. Может быть, Вас смущает текст на кнопке вместо иконки?
Владимир.

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

#17
sokol92
Цитата: sokol92 от 19 апреля 2026, 19:48Мы не сохранили панель - как это сделать, показано здесь.
нет, я сохранил, строка:
oModuleCfgMgr.store    ' для сохранения инструментальной линейки после закрытия LibreOffice имеется, так картинка в #15 актуальна, есть кнопка Test, она работоспособна, панели нет
есть билет на балет, на трамвай билета нет....
почему Вы её называете:
Цитата: sokol92 от 19 апреля 2026, 19:48Мы создали свою панель управления
понять пока не могу))
ну да, теперь в LibreOffice Writer → меню Вид → Панели инструментов имеется пункт с названием "My little custom toolbar=Моя маленькая пользовательская панель инструментов" но это далеко ещё НЕ панель..., а кнопка))

sokol92

Цитата: Ципихович Эндрю от 20 апреля 2026, 06:40но это далеко ещё НЕ панель..., а кнопка
Это панель, в которой есть одна кнопка.
Цитата из комментариев к Вашей программе:
Цитировать' Чтобы добавить второй элемент, увеличьте значение nCount, создайте новый элемент панели инструментов и вставьте его
Владимир.

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

sokol92, ну ок, добавил ещё две кнопки, итого теперь три кнопки, на их место можно поставить лейбл, чекбокс, это понятно...я могу и приложить картинку, но можете и сами представить картинку в сообщении #15, только справа от кнопки Test теперь есть ещё две, и что? до того, что мне нужно (что на картинке #15) бездна... одна тысяча различий, я бы не назвал это панелью это элементы управления, но только НЕ в документе
смысл тот, что нынче 2026 год.... у меня как наверное и у многих монитор, далеко не 17-19 дюймовый, а 28 дюймов, либру врайтер я смотрю в масштабе 130% и справа и слева от документа я имею НЕ задействованное пространство (сейчас замерил по 10 см с каждой стороны, в высоту это пространство 25 см, итого две области 10х25) вот я его и пытаюсь приручить? (и этот метод костыльный - ну хоть что-то...) как его приручить?
пысы в Ворде для этого есть вижуал студио))

sokol92

Давайте разделим вопросы.

1. Создание панелей управления.
Мы разобрали создание пользовательской панели управления с помощью макросов. За скобками остались вопросы отображения иконок (вместо текстов) и назначения горячих клавиш.

2. Отображение ранее созданных панелей управления.
Если Вы готовите программное обеспечение для неопределенного круга лиц, то у них могут быть различные мониторы. Здесь, как минимум, нужно определиться с алгоритмами отображения панелей.
Если предполагается, что ПО будет использоваться определенным кругом лиц, то проще всего вручную однократно настроить панель "под себя". Как зафиксировать эти настройки мы уже обсудили.
Владимир.

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

#21
Цитата: sokol92 от 20 апреля 2026, 18:42Мы разобрали создание пользовательской панели управления с помощью макросов
категорически с этим не согласен
обратимся опять к картинке с сообщения №15, её характеристики:
имеет размер 3.5х13.5 см
имеет "шапку", на которой имеется название панели раскрывающееся поле, знак Х - закрытие панели
за эту шапку можно мышью зацепить и перетащить в нужное место, в любое место, кроме панели задач -это вотчина ОС
можно за край панели зацепить мышью и изменить ширину и высоту панели - усечённо, но можно

что из перечисленного мы разобрали? - ничего
и этот метод я называю костыльный, потому как изменить ширину и высоту панели можно только усечённо, изменить до размеров 10х25 нельзя, а сравните 3.5х13.5 намного меньше 10х25, кратно меньше, я уж не говорю, о том, что пустого пространства 10х25-два!

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

здравствуйте, понял, что нужно писать расширение...
пробую впервые, макрос:
# файл: Module.py

import os
from pathlib import Path
import zipfile
import datetime
import shutil

# Путь к папке с расширением в профиле LO (для тестов можно оставить так)
BASE_PATH = r"C:\Users\start\AppData\Roaming\LibreOffice"

# Читаемое имя расширения и издатель
EXT_NAME = "WriterCustomPanel"
PUBLISHER_NAME = "Ципихович Эндрю"  # желаемое читаемое имя для Extensions Manager

def log(message, log_path=None):
    ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"[{ts}] {message}\n"
    if log_path is None:
        log_path = Path(BASE_PATH) / "writer_custom_panel.log.txt"
    try:
        log_path.parent.mkdir(parents=True, exist_ok=True)
        with open(log_path, "a", encoding="utf-8") as f:
            f.write(line)
    except Exception:
        pass

def ensure_dirs(base_path, folders):
    for f in folders:
        path = Path(base_path) / f
        path.mkdir(parents=True, exist_ok=True)
        log(f"Created: {path}")

def write_file(path, content, encoding="utf-8"):
    with open(path, "w", encoding=encoding) as f:
        f.write(content)
    log(f"Wrote: {path}")

def package_extension(base_path, source_dir, output_zip):
    with zipfile.ZipFile(str(output_zip), 'w', zipfile.ZIP_DEFLATED) as zf:
        for root, dirs, files in os.walk(str(source_dir)):
            for file in files:
                abs_path = Path(root) / file
                rel_path = abs_path.relative_to(source_dir)
                zf.write(str(abs_path), arcname=Path("writer.custom.panel") / rel_path)
    log(f"Packaged into: {output_zip}")

def main(*args):
    log("Start: main", log_path=None)

    base_path = BASE_PATH

    folders = [
        "writer.custom.panel",
        "writer.custom.panel/scripts/python"
    ]

    # Очистка кешей Uno (помогаем избежать конфликтов)
    log("Cleaning UNO cache before packaging...", log_path=None)
    clean_cache()

    ensure_dirs(base_path, folders)

    manifest_xml = """<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest">
  <manifest:file-entry path="/" Writable="true" Exclude="false"/>
  <manifest:file-entry path="/description.xml" source="description.xml"/>
  <manifest:file-entry path="/writer.custom.panel/description.xml" source="writer.custom.panel/description.xml"/>
  <manifest:file-entry path="/writer.custom.panel/manifest.xml" source="writer.custom.panel/manifest.xml"/>
  <manifest:file-entry path="/writer.custom.panel/scripts/python/Module.py" source="writer.custom.panel/scripts/python/Module.py"/>
</manifest:manifest>"""

    description_xml = """<?xml version="1.0" encoding="UTF-8"?>
<oor:component-description xmlns:oor="http://openoffice.org/2004/office"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:manifest="http://openoffice.org/2001/manifest">
  <oor:extension-description>
    <oor:name>{name}</oor:name>
    <oor:publisher>{publisher}</oor:publisher>
    <oor:description>Прототип dockable-панели в Writer (через расширение)</oor:description>
  </oor:extension-description>

  <oor:modules>
    <oor:module impl="com.yourname.WriterCustomPanel" />
  </oor:modules>

  <oor:dependencies/>
</oor:component-description>""".format(name=EXT_NAME, publisher=PUBLISHER_NAME)

    module_py = """# Файл: writer.custom.panel/scripts/python/Module.py
def show_dialog(*args):
    # Тестовый макрос; здесь будет точка входа для расширения
    ctx = XSCRIPTCONTEXT.getComponentContext()
    smgr = ctx.ServiceManager
    toolkit = smgr.createInstanceWithContext('com.sun.star.awt.Toolkit', ctx)
    msg = toolkit.createMessageBox(None, 0, 0, 'Writer Custom Extension', 'Диалог запущен из расширения!')
    msg.execute()
"""

    # Запишем manifest.xml и description.xml
    write_file(Path(base_path) / "writer.custom.panel" / "description.xml", description_xml)
    write_file(Path(base_path) / "writer.custom.panel" / "manifest.xml", manifest_xml)

    # Запишем Module.py
    module_dir = Path(base_path) / "writer.custom.panel" / "scripts" / "python"
    module_dir.mkdir(parents=True, exist_ok=True)
    write_file(module_dir / "Module.py", module_py)

    # Упаковать в .oxt
    zip_path = Path(base_path) / "writer.custom.panel.oxt"
    if zip_path.exists():
        zip_path.unlink()
    package_extension(base_path, Path(base_path) / "writer.custom.panel", zip_path)

def clean_cache():
    possible_paths = [
        r"C:\Users\start\AppData\Roaming\LibreOffice\4\user\uno_packages\cache\uno_packages",
        r"C:\Users\start\AppData\Roaming\LibreOffice\4\user\uno_packages\cache",
        r"C:\Users\start\AppData\Roaming\LibreOffice\5\user\uno_packages\cache\uno_packages",
    ]
    for p in possible_paths:
        p_path = Path(p)
        if p_path.exists():
            try:
                if p_path.is_dir():
                    shutil.rmtree(p_path)
                else:
                    p_path.unlink()
                log(f"Removed cache path: {p_path}")
            except Exception as e:
                log(f"Failed to remove cache {p_path}: {e}")

def start(*args):
    main(*args)
    # Финальное сообщение
    try:
        ctx = XSCRIPTCONTEXT.getComponentContext()
        smgr = ctx.ServiceManager
        toolkit = smgr.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)
        msg = toolkit.createMessageBox(None, 0, 0, "Готово", "Работа завершена")
        msg.execute()
    except Exception:
        pass

g_exportedScripts = (start,)
проблемы:
у меня установлено несколько расширений, одно из них выделяю в менеджере расширений и активизируется кнопка Параметры, а созданное расширение выделяю и эта кнопка НЕ активна, почему?
2 я не вижу Ципихович Эндрю в менеджере расширений, вижу "writer.custom.panel.oxt"
ЧЯДНТ?

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

#23
Цитата: Ципихович Эндрю от 23 апреля 2026, 13:11понял, что нужно писать расширение...
пробую впервые
здравствуйте, понял, что можно и без него, панель создаётся, код:
# -*- coding: utf-8 -*-
import uno
from com.sun.star.beans import PropertyValue

_panel_positions = {}

def show_custom_panel(*args):
    """Показать/создать кастомную панель"""
    ctx = XSCRIPTCONTEXT.getComponentContext()
    smgr = ctx.ServiceManager
   
    desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
    doc = desktop.getCurrentComponent()
   
    if doc is None:
        return "Нет открытого документа!"
   
    frame = doc.getCurrentController().getFrame()
    layout_mgr = frame.LayoutManager
    toolbar_url = "private:resource/toolbar/custom_mypanel"
   
    # Проверяем существование
    element = layout_mgr.getElement(toolbar_url)
   
    if element is None:
        recreate_panel(ctx, smgr, layout_mgr, toolbar_url)
    elif not layout_mgr.isElementVisible(toolbar_url):
        layout_mgr.showElement(toolbar_url)
        layout_mgr.floatWindow(toolbar_url)
        restore_position(layout_mgr, toolbar_url)
    else:
        layout_mgr.floatWindow(toolbar_url)
        restore_position(layout_mgr, toolbar_url)
   
    return "Панель показана!"


def recreate_panel(ctx, smgr, layout_mgr, toolbar_url):
    """Пересоздать панель с несколькими элементами для нормального размера"""
   
    # Удаляем старые настройки
    supplier = smgr.createInstanceWithContext(
        "com.sun.star.ui.ModuleUIConfigurationManagerSupplier", ctx)
    module_cfg = supplier.getUIConfigurationManager("com.sun.star.text.TextDocument")
   
    if module_cfg.hasSettings(toolbar_url):
        module_cfg.removeSettings(toolbar_url)
   
    # Создаём настройки
    tb_settings = module_cfg.createSettings()
   
    # === ВАЖНО: UIName задаёт имя в списке панелей, не заголовок окна ===
    tb_settings.UIName = "Моя панель"
   
    # === ДОБАВЛЯЕМ НЕСКОЛЬКО ЭЛЕМЕНТОВ для нормального размера ===
   
    # Элемент 0: Кнопка Bold
    add_toolbar_item(tb_settings, 0, ".uno:Bold", "Жирный", False)
   
    # Элемент 1: Кнопка Italic
    add_toolbar_item(tb_settings, 1, ".uno:Italic", "Курсив", False)
   
    # Элемент 2: Разделитель
    add_separator(tb_settings, 2)
   
    # Элемент 3: Кнопка Underline
    add_toolbar_item(tb_settings, 3, ".uno:Underline", "Подчеркнутый", False)
   
    # Элемент 4: Кнопка-тумблер
    add_toolbar_item(tb_settings, 4, ".uno:Strikeout", "Зачеркнутый", True)
   
    # Сохраняем
    module_cfg.insertSettings(toolbar_url, tb_settings)
    module_cfg.store()
   
    # Показываем
    layout_mgr.showElement(toolbar_url)
    layout_mgr.floatWindow(toolbar_url)
   
    # Задаём начальный размер и позицию
    set_initial_position(layout_mgr, toolbar_url)


def add_toolbar_item(tb_settings, index, command, label, toggle):
    """Добавить кнопку в панель"""
    style = 96  # IMAGE + TEXT
    if toggle:
        style += 16384  # + TOGGLE
   
    props = []
    for name, value in [
        ("CommandURL", command),
        ("Label", label),
        ("Type", 0),
        ("Visible", True),
        ("Style", style),
    ]:
        p = PropertyValue()
        p.Name = name
        p.Value = value
        props.append(p)
   
    uno.invoke(tb_settings, "insertByIndex",
              (index, uno.Any("[]com.sun.star.beans.PropertyValue", tuple(props))))


def add_separator(tb_settings, index):
    """Добавить разделитель"""
    props = []
    p1 = PropertyValue()
    p1.Name = "Type"
    p1.Value = 1  # SEPARATOR_LINE
    props.append(p1)
   
    p2 = PropertyValue()
    p2.Name = "Visible"
    p2.Value = True
    props.append(p2)
   
    uno.invoke(tb_settings, "insertByIndex",
              (index, uno.Any("[]com.sun.star.beans.PropertyValue", tuple(props))))


def set_initial_position(layout_mgr, toolbar_url):
    """Установить начальную позицию и размер"""
    element = layout_mgr.getElement(toolbar_url)
    if element is None:
        return
   
    real_iface = element.RealInterface
    if real_iface is None:
        return
   
    try:
        if hasattr(real_iface, 'setPosSize'):
            # x=100, y=100, width=400, height=80 (широкая и низкая панель)
            real_iface.setPosSize(100, 100, 400, 80, 15)
    except:
        pass


def restore_position(layout_mgr, toolbar_url):
    """Восстановить сохранённую позицию"""
    global _panel_positions
   
    if toolbar_url not in _panel_positions:
        return
   
    pos = _panel_positions[toolbar_url]
    element = layout_mgr.getElement(toolbar_url)
   
    if element is None:
        return
   
    real_iface = element.RealInterface
    if real_iface is None:
        return
   
    try:
        if hasattr(real_iface, 'setPosSize'):
            real_iface.setPosSize(pos[0], pos[1], pos[2], pos[3], 15)
    except:
        pass


def save_panel_position(*args):
    """Сохранить текущую позицию"""
    ctx = XSCRIPTCONTEXT.getComponentContext()
    smgr = ctx.ServiceManager
    desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
    doc = desktop.getCurrentComponent()
   
    if doc is None:
        return "Нет документа!"
   
    frame = doc.getCurrentController().getFrame()
    layout_mgr = frame.LayoutManager
    toolbar_url = "private:resource/toolbar/custom_mypanel"
   
    element = layout_mgr.getElement(toolbar_url)
    if element is None:
        return "Панель не найдена!"
   
    real_iface = element.RealInterface
    if real_iface is None:
        return "RealInterface недоступен!"
   
    try:
        if hasattr(real_iface, 'getPosSize'):
            pos = real_iface.getPosSize()
            if pos is not None:
                global _panel_positions
                _panel_positions[toolbar_url] = (pos[0], pos[1], pos[2], pos[3])
                return f"Сохранено: x={pos[0]}, y={pos[1]}, w={pos[2]}, h={pos[3]}"
    except Exception as e:
        return f"Ошибка: {str(e)[:100]}"
   
    return "getPosSize недоступен"


g_exportedScripts = (show_custom_panel, save_panel_position)
кто-нибудь подскажет, нет всего, что задумано, а именно:
tb_settings.UIName = "Моя панель"
   
# === ДОБАВЛЯЕМ НЕСКОЛЬКО ЭЛЕМЕНТОВ для нормального размера ===