Plugins tutorial

rockiger edited this page Oct 24, 2017 · 23 revisions

This page gives a very brief introduction to the plugin interface. See plugin API documentation for full information.

An Enki plugin is a Python package, that implements a Plugin class. If it is placed in the userplugins directory it can be activated in the plugin manager. On Linux, macOS you will find this directory under /home/USERNAME/.config/enki/userplugins/; on Windows you will find it under C:\Users\USERNAME\.config\enki\userplugins. You can find the location of that directory in the Settings > Plugins.

Path to userplugins directory

(Enki has another plugin directory under enki/plugins. Plugins placed there are always loaded on startup. In most cases, it is better to place plugins in the userplugins directory. The enki/plugins directory should only be used by Enki developers.)

At minimum the plugin needs the following attributes:

"""A docstring describing the plugin in one sentence"""

__pluginname__ = "PLUGINNAME"
__author__ = "AUTHOR"
__version__ = "VERSION"

class Plugin:
  def __init__(self):
  	pass
  
  def terminate:
    pass

The Plugin class needs the two methods defined, they are called when the plugin is loaded and unloaded, respectively.

Hello world plugin

Let's create our first plugin. This manual gives step-by-step instructions. The final code may be found at enki/example-plugin/helloworld.py in Enki's Git repository. If you want to test it quickly, just copy this file to userplugins and activate the plugin under Settings > Plugins.

The beginning

For creating the "Hello, world" plugin, go to Enki sources directory, and create a file helloworld.py in the userplugins directory with the next code:

"""A hello world plugin for Enki"""
from PyQt5.QtWidgets import QMessageBox

# core is main enki.core singleton. It is usually used for getting pointers to other singletons
from enki.core.core import core

__pluginname__ = "Hello World"
__author__ = "Hello Author"
__version__ = "0.0.1"

class Plugin:
    """During initialization, core imports all modules and packages in enki/plugins directory,
    searches for Plugin class in every module and creates an instance
    """
    def __init__(self):
        QMessageBox.information(core.mainWindow(), "Hello, world", "Plugin loaded")
    
    def terminate(self):
        """This method is called by core for each plugin during termination
        """
        QMessageBox.information(core.mainWindow(), "Hello, world", "Plugin terminated")

Now activate the plugin under Settings > Plugins and see, if your plugin is loaded.

Hello World plugin in pluginmanager

Add new action to the main menu

class Plugin:
    def __init__(self):
        self._addAction()

    def _addAction(self):
        """Add action to main menu
        This action uses embedded icons. You can find list of icons in icons/ directory at project root
        """
        action = core.actionManager().addAction( "mHelp/aSayHello",
                                                 "Say Hello...",
                                                 QIcon(':enkiicons/enki.png'))
        core.actionManager().setDefaultShortcut(action, "Ctrl+Alt+Shift+H")
        action.triggered.connect(self._sayHello)
        
    def _sayHello(self):
        """Handler for main menu action
        """
        QMessageBox.information(core.mainWindow(), "Hello, world", "Menu action has been triggered!")

Don't forget to add import QIcon at the top of the file.

Restart Enki and look, if you have a new action in the Help menu. You even might go to Settings > Application shortcuts and set a new shortcut for it.

Add dock widget to the main window

import enki.widgets.dockwidget

class MyDock(enki.widgets.dockwidget.DockWidget):
    def __init__(self):
        enki.widgets.dockwidget.DockWidget.__init__(self,
                                                    core.mainWindow(),
                                                    "Hello dock",
                                                    QIcon(":enkiicons/help.png"),
                                                    "Alt+H")
        
        self.label = QLabel("This is Hello World dock")
        self.setWidget(self.label)

class Plugin:
    def __init__(self):
        self._createDock()

    def _createDock(self):
        """Create dock widget and add it to main window
        """
        self._dock = MyDock()
        core.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._dock)

Read and write settings from the config

class Plugin:
    def __init__(self):
        self._readSettings()

    def _readSettings(self):
        """Read settings from core configuration file
        """
        if not "HelloWorld" in core.config():  # first start
            core.config()["HelloWorld"] = {}
            core.config()["HelloWorld"]["VeryImportantSetting"] = "the correct value"
        self._dock.label.setText('Option value: %s' % core.config()["HelloWorld"]["VeryImportantSetting"])

    def _writeSettings(self):
        """This method is never called.
        Just an example implementation
        """
        core.config()["HelloWorld"]["VeryImportantSetting"] = "new value"
        # Don't forget to flush config!
        # if settings has been edited with main settings dialogue, it will be flushed automatically
        core.config().flush()

Here we dynamically add our option to the main configuration file during the first start. You also can edit enki/config/enki.default.json

Get notified when something interesting happens

class Plugin:
    def __init__(self):
        core.workspace().currentDocumentChanged.connect(self._onDocumentChanged)

    def _onDocumentChanged(self, old, new):
        """Current document has been changed. Let's notify the user
        """
        if new is not None:
            core.mainWindow().appendMessage("Current file is '%s'" % new.fileName(), 1000)

Check the API docs for other useful signals

Add my options to the settings dialogue

from enki.core.uisettings import TextOption

class SettingsPage(QWidget):
    """Settings page for Hello World plugin
    """
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self._layout = QHBoxLayout(self)
        self._label = QLabel("Very important option", self)
        self._layout.addWidget(self._label)
        self.edit = QLineEdit(self)
        self._layout.addWidget(self.edit)

class Plugin:
    def __init__(self):
        core.uiSettingsManager().dialogAccepted.connect(self._applySettings)
        core.uiSettingsManager().aboutToExecute.connect(self._onSettingsDialogAboutToExecute)

    def _applySettings(self):
        """Dialog has been accepted. Apply settings
        """
        self._dock.label.setText('Option value: %s' % core.config()["HelloWorld"]["VeryImportantOption"])
    
    def _onSettingsDialogAboutToExecute(self, dialog):
        """UI settings dialogue is about to execute.
        Add own options
        """
        page = SettingsPage(dialog)
        dialog.appendPage(u"Hello World", page, QIcon(':/enkiicons/help.png'))

        # Options
        dialog.appendOption(TextOption(dialog, core.config(), "HelloWorld/VeryImportantOption", page.edit))

Now go to Edit > Settings > Hello World, edit the text and check, if it is updated on UI. Then restart Enki and check, if the new value has been loaded from settings.

Congratulations! We just have created an absolutely useless plugin. And you are ready to create something more valuable. Full plugin API documentation and Enki team will help you.

Read also about coding style and rules at the Hacking guide.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.