Обработка событий в плагине

В разделе Основы создания плагинов для OutWiker было показано, как создать простейший плагин, который ничего не делает кроме как загружается и упоминается в списке плагинов. В данном разделе будет показано, как обрабатывать различные события. Прежде чем читать данный раздел рекомендуется ознакомиться с разделами Application. Экземпляр класса ApplicationParams и События.

Примечание

Для выполнения примеров данного раздела необходимо перевести OutWiker в режим отладки, для чего в файле outwiker.ini в секции [General] необходимо добавить строку debug = True. Чтобы найти файл outwiker.ini, см. раздел Где хранятся настройки программы?

В этом разделе будет создан плагин с именем ExampleEventsPlugin, который будет выводить в лог работы информацию о некоторых событиях, возникающих в процессе работы OutWiker. Исходные коды данного плагина расположены в папке plugins/examples/eventsplugin/ исходных кодов программы OutWiker.

Сначала создадим новую папку с плагином, которая будет называться, например, eventsplugin (имя папки не существенно). Данная папка должна располагаться в папке с плагинами (см. раздел Как установить плагин?).

Внутри этой папки создадим три файла:

  1. __init__.py - пустой файл.
  2. plugin.py - файл с классом плагина.
  3. plugin.xml - файл манифеста плагина.

Таким образом, структура каталога eventsplugin будет следующая:

..
└── eventsplugin
    ├── __init__.py
    ├── plugin.py
    └── plugin.xml

Содержимое файла plugin.xml соответствует структуре, описанной в разделе Основы создания плагинов для OutWiker, и не представляет особого интереса:

<?xml version="1.1" encoding="UTF-8" ?>
<info>
    <name>ExampleEventsPlugin</name>
    <updates>http://example.com/pluginname/plugin.xml</updates>
    <requirements>
        <os>Windows, Linux</os>
        <packages>
            <core>1.3</core>
            <actions>1.1</actions>
            <gui>1.5</gui>
            <pages>2.0</pages>
            <utilites>1.0</utilites>
            <libs>1.0</libs>
        </packages>
    </requirements>

    <data lang="en">
        <website>http://example.com/pluginnameEn</website>
        <description>Description.</description>

        <author>
            <name>Author name</name>
            <email>example@example.com</email>
            <site>http://example.com</site>
        </author>

        <changelog>
            <version number="0.1" date="August 20, 2017">
                <download os="all">http://example.com/eventsplugin-0.1.zip</download>
                <change>The first version.</change>
            </version>
        </changelog>
    </data>

    <data lang="ru">
        <website>http://example.com/pluginnameRu</website>
        <description>Описание.</description>

        <author>
            <name>Имя автора</name>
            <email>example@example.com</email>
            <site>http://example.com</site>
        </author>

        <changelog>
            <version number="0.1" date="20.08.2017">
                <download os="all">http://example.com/eventsplugin-0.1.zip</download>
                <change>Первая версия.</change>
            </version>
        </changelog>
    </data>
</info>

Файл plugin.py для начала будет минимально необходимый, только чтобы убедиться, что плагин загружается:

# -*- coding: utf-8 -*-

from outwiker.core.pluginbase import Plugin


class PluginExampleEvents(Plugin):
    def __init__(self, application):
        super(PluginExampleEvents, self).__init__(application)

    #########################################
    # Properties and methods to overloading #
    #########################################

    @property
    def name(self):
        return u"ExampleEventsPlugin"

    @property
    def description(self):
        return _(u"Example plugin")

    def initialize(self):
        pass

    def destroy(self):
        pass

Запустите OutWiker и убедитесь, что плагин успешно загружен, он должен появиться в списке плагинов в разделе «Плагины» диалога настроек:

Список плагинов

Теперь добавим функциональность плагину. Мы могли бы все делать в рамках класса PluginExampleEvents, но лучше пусть этот класс работает только в процессе загрузки плагина, а функциональность плагина мы поместим в новый класс eventsplugin.controller.Controller, поэтому в папке eventsplugin создадим еще один файл controller.py с классом контроллера:

# -*- coding: UTF-8 -*-

import logging


logger = logging.getLogger('ExampleEventsPlugin')


class Controller(object):
    def __init__(self, application):
        self._application = application

    def initialize(self):
        logger.info(u'Initialize.')

    def destroy(self):
        logger.info(u'Destroy.')

Метод initialize() будет вызываться во время инициализации плагина (его загрузки или включении в диалоге настроек), а метод destroy() будет вызываться перед выгрузкой плагина (во время завершения программы OutWiker или после отключения плагина в диалоге настроек).

Пока единственное, что делает класс eventsplugin.controller.Controller - это сообщает в логе работы о том, что плагин инициализируется и выгружается.

Чтобы начать использовать данный класс, изменим содержимое файла plugin.py:

# -*- coding: utf-8 -*-

from outwiker.core.pluginbase import Plugin

from .controller import Controller


class PluginExampleEvents(Plugin):
    def __init__(self, application):
        super(PluginExampleEvents, self).__init__(application)
        self.controller = Controller(application)

    #########################################
    # Properties and methods to overloading #
    #########################################

    @property
    def name(self):
        return u"ExampleEventsPlugin"

    @property
    def description(self):
        return _(u"Example plugin")

    def initialize(self):
        self.controller.initialize()

    def destroy(self):
        self.controller.destroy()

Перезапустите OutWiker и в логе работы вы должны увидеть строки вида

INFO       2017-08-20 21:36:40,987   ExampleEventsPlugin - controller - Initialize.
INFO       2017-08-20 21:57:32,939   ExampleEventsPlugin - controller - Destroy.

Отключите и включите плагин в диалоге настроек, чтобы убедиться, что плагин инициализируется и выгружается.

Если вы работаете под Linux или запускаете OutWiker из исходников под Windows, то данный текст лога работы должен выводиться в консоль. Если вы запускаете OutWiker с помощью файла outwiker.exe под Windows, то данные строки вы должны увидеть в файле outwiker.log в папке профиля программы (см. раздел Где находится папка профиля программы?). Если плагин загружен, но данные строки не появляются в логе работы, убедитесь, что вы перевели OutWiker в отладочный режим (см. примечание в начале данного раздела).

Теперь вернемся в файл controller.py и подпишемся на некоторые события из класса outwiker.core.application.ApplicationParams. Измененный файл controller.py будет выглядеть следующим образом:

# -*- coding: UTF-8 -*-

import logging


logger = logging.getLogger('ExampleEventsPlugin')


class Controller(object):
    def __init__(self, application):
        self._application = application

    def initialize(self):
        logger.info(u'Initialize.')
        self._application.onWikiOpen += self._onWikiOpen
        self._application.onPageSelect += self._onPageSelect
        self._application.onPageUpdate += self._onPageUpdate
        self._application.onEditorPopupMenu += self._onEditorPopupMenu

    def destroy(self):
        logger.info(u'Destroy.')
        self._application.onWikiOpen -= self._onWikiOpen
        self._application.onPageSelect -= self._onPageSelect
        self._application.onPageUpdate -= self._onPageUpdate
        self._application.onEditorPopupMenu -= self._onEditorPopupMenu

    def _onWikiOpen(self, root):
        logger.info(u'onWikiOpen. Path to notes: {}'.format(root.path))

    def _onPageSelect(self, sender):
        if sender is None:
            logger.info(u'onPageSelect. No page selected.')
        else:
            logger.info(u'onPageSelect. Selected page: {}'.format(sender.subpath))

    def _onPageUpdate(self, sender, *args, **kwargs):
        logger.info(u'onPageUpdate. Updated page: {}'.format(sender.subpath))

    def _onEditorPopupMenu(self, page, params):
        logger.info(u'onEditorPopupMenu. Current page: {}'.format(page.subpath))

В классе eventsplugin.controller.Controller была добавлена подписка на следующие события:

onWikiOpen
Вызывается после открытия дерева заметок.
onPageSelect
Вызывается после выбора новой страницы в дереве заметок.
onPageUpdate
Вызывается после изменения содержимого страницы.
onEditorPopupMenu
Вызывается перед созданием контекстного меню после клика правой кнопкой мыши в текстовом редакторе.

Чтобы увидеть эти события, пооткрывайте и позакрывайте дерево заметок, поизменяйте содержимое страниц и повызывайте контекстное меню с помощью правой кнопки мыши. Подробнее о параметрах каждого из этих событий написано в описании класса outwiker.core.application.ApplicationParams.

Лог работы может выглядеть примерно следующим образом:

INFO       2017-08-20 22:33:14,963   ExampleEventsPlugin - controller - Initialize.
INFO       2017-08-20 22:33:18,139   ExampleEventsPlugin - controller - onPageSelect. Selected page: test/page_01
INFO       2017-08-20 22:33:18,140   ExampleEventsPlugin - controller - onWikiOpen. Path to notes: /home/jenyay/sync/wiki/samplewiki
INFO       2017-08-20 22:33:18,140   root - plugin - Opening wiki /home/jenyay/sync/wiki/samplewiki: 1.09291815758 sec
INFO       2017-08-20 22:33:20,662   ExampleEventsPlugin - controller - onPageSelect. No page selected.
INFO       2017-08-20 22:33:21,801   ExampleEventsPlugin - controller - onPageUpdate. Updated page: # Поиск
INFO       2017-08-20 22:33:21,803   ExampleEventsPlugin - controller - onPageUpdate. Updated page: # Поиск
INFO       2017-08-20 22:33:21,804   ExampleEventsPlugin - controller - onPageUpdate. Updated page: # Поиск
INFO       2017-08-20 22:33:21,859   ExampleEventsPlugin - controller - onPageSelect. Selected page: # Поиск
INFO       2017-08-20 22:33:22,618   ExampleEventsPlugin - controller - onPageUpdate. Updated page: # Поиск
INFO       2017-08-20 22:33:22,621   ExampleEventsPlugin - controller - onPageUpdate. Updated page: # Поиск
INFO       2017-08-20 22:33:22,624   ExampleEventsPlugin - controller - onPageUpdate. Updated page: # Поиск
INFO       2017-08-20 22:33:22,864   ExampleEventsPlugin - controller - onPageSelect. Selected page: test/page_01
INFO       2017-08-20 22:33:53,506   ExampleEventsPlugin - controller - onEditorPopupMenu. Current page: test/page_01

Плагин готов. В данном разделе мы создали плагин, который обрабатывает некоторые события из класса outwiker.core.application.ApplicationParams и выводит текст в лог работы. Исходные коды данного плагина расположены в папке plugins/examples/eventsplugin/.

Развивая идею данного плагина, был создан плагин DebugEvents, который подобным образом следит за всеми событиями из класса outwiker.core.application.ApplicationParams. Этот плагин предназначен исключительно для разработчиков плагина, поэтому не упоминается на сайте программы. Найти его можно в исходных кодах в папке plugins/debugevents.