calibre/manual/creating_plugins.rst
2012-05-30 11:32:00 +05:30

214 lines
9.9 KiB
ReStructuredText

.. include:: global.rst
.. _pluginstutorial:
Writing your own plugins to extend |app|'s functionality
====================================================================
|app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the built-in plugins in |app| by going to :guilabel:`Preferences->Plugins`.
Here, we will teach you how to create your own plugins to add new features to |app|.
.. contents:: Contents
:depth: 2
:local:
.. note:: This only applies to calibre releases >= 0.7.53
Anatomy of a |app| plugin
---------------------------
A |app| plugin is very simple, it's just a zip file that contains some python code
and any other resources like image files needed by the plugin. Without further ado,
let's see a basic example.
Suppose you have an installation of |app| that you are using to self publish various e-documents in EPUB and MOBI
formats. You would like all files generated by |app| to have their publisher set as "Hello world", here's how to do it.
Create a file named :file:`__init__.py` (this is a special name and must always be used for the main file of your plugin)
and enter the following Python code into it:
.. literalinclude:: plugin_examples/helloworld/__init__.py
:lines: 10-
That's all. To add this code to |app| as a plugin, simply create a zip file with::
zip plugin.zip __init__.py
Add this plugin to |app| via :guilabel:`Preferences->Plugins`.
You can download the Hello World plugin from
`helloworld_plugin.zip <http://calibre-ebook.com/downloads/helloworld_plugin.zip>`_.
Every time you use calibre to convert a book, the plugin's :meth:`run` method will be called and the
converted book will have its publisher set to "Hello World". This is a trivial plugin, lets move on to
a more complex example that actually adds a component to the user interface.
A User Interface plugin
-------------------------
This plugin will be spread over a few files (to keep the code clean). It will show you how to get resources
(images or data files) from the plugin zip file, allow users to configure your plugin,
how to create elements in the |app| user interface and how to access
and query the books database in |app|.
You can download this plugin from `interface_demo_plugin.zip <http://calibre-ebook.com/downloads/interface_demo_plugin.zip>`_
The first thing to note is that this zip file has a lot more files in it, explained below, pay particular attention to
``plugin-import-name-interface_demo.txt``.
**plugin-import-name-interface_demo.txt**
An empty text file used to enable the multi-file plugin magic. This file must be present in all plugins that use
more than one .py file. It should be empty and its filename must be of the form: plugin-import-name-**some_name**.txt
The presence of this file allows you to import code from the .py files present inside the zip file, using a statement like::
from calibre_plugins.some_name.some_module import some_object
The prefix ``calibre_plugins`` must always be present. ``some_name`` comes from the filename of the empty text file.
``some_module`` refers to :file:`some_module.py` file inside the zip file. Note that this importing is just as
powerful as regular python imports. You can create packages and subpackages of .py modules inside the zip file,
just like you would normally (by defining __init__.py in each sub directory), and everything should Just Work.
The name you use for ``some_name`` enters a global namespace shared by all plugins, **so make it as unique as possible**.
But remember that it must be a valid python identifier (only alphabets, numbers and the underscore).
**__init__.py**
As before, the file that defines the plugin class
**main.py**
This file contains the actual code that does something useful
**ui.py**
This file defines the interface part of the plugin
**images/icon.png**
The icon for this plugin
**about.txt**
A text file with information about the plugin
Now let's look at the code.
__init__.py
^^^^^^^^^^^^^
First, the obligatory ``__init__.py`` to define the plugin metadata:
.. literalinclude:: plugin_examples/interface_demo/__init__.py
:lines: 10-
The only noteworthy feature is the field :attr:`actual_plugin`. Since |app| has both command line and GUI interfaces,
GUI plugins like this one should not load any GUI libraries in __init__.py. The actual_plugin field does this for you,
by telling |app| that the actual plugin is to be found in another file inside your zip archive, which will only be loaded
in a GUI context.
Remember that for this to work, you must have a plugin-import-name-some_name.txt file in your plugin zip file,
as discussed above.
Also there are a couple of methods for enabling user configuration of the plugin. These are discussed below.
ui.py
^^^^^^^^
Now let's look at ui.py which defines the actual GUI plugin. The source code is heavily commented and should be self explanatory:
.. literalinclude:: plugin_examples/interface_demo/ui.py
:lines: 16-
main.py
^^^^^^^^^
The actual logic to implement the Interface Plugin Demo dialog.
.. literalinclude:: plugin_examples/interface_demo/main.py
:lines: 16-
Getting resources from the plugin zip file
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|app|'s plugin loading system defines a couple of built-in functions that allow you to conveniently get files from the plugin zip file.
**get_resources(name_or_list_of_names)**
This function should be called with a list of paths to files inside the zip file. For example to access the file icon.png in
the directory images in the zip file, you would use: ``images/icon.png``. Always use a forward slash as the path separator,
even on windows. When you pass in a single name, the function will return the raw bytes of that file or None if the name
was not found in the zip file. If you pass in more than one name then it returns a dict mapping the names to bytes.
If a name is not found, it will not be present in the returned dict.
**get_icons(name_or_list_of_names)**
A convenience wrapper for get_resources() that creates QIcon objects from the raw bytes returned by get_resources.
If a name is not found in the zip file the corresponding QIcon will be null.
Enabling user configuration of your plugin
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To allow users to configure your plugin, you must define three methods in your base plugin class, '**is_customizable**, **config_widget** and **save_settings** as shown below:
.. literalinclude:: plugin_examples/interface_demo/__init__.py
:pyobject: InterfacePluginDemo.is_customizable
.. literalinclude:: plugin_examples/interface_demo/__init__.py
:pyobject: InterfacePluginDemo.config_widget
.. literalinclude:: plugin_examples/interface_demo/__init__.py
:pyobject: InterfacePluginDemo.save_settings
|app| has many different ways to store configuration data (a legacy of its long history). The recommended way is to use the **JSONConfig** class, which stores your configuration information in a .json file.
The code to manage configuration data in the demo plugin is in config.py:
.. literalinclude:: plugin_examples/interface_demo/config.py
:lines: 10-
The ``prefs`` object is now available throughout the plugin code by a simple::
from calibre_plugins.interface_demo.config import prefs
You can see the ``prefs`` object being used in main.py:
.. literalinclude:: plugin_examples/interface_demo/main.py
:pyobject: DemoDialog.config
The different types of plugins
--------------------------------
As you may have noticed above, a plugin in |app| is a class. There are different classes for the different types of plugins in |app|.
Details on each class, including the base class of all plugins can be found in :ref:`plugins`.
Debugging plugins
-------------------
The first, most important step is to run |app| in debug mode. You can do this from the command line with::
calibre-debug -g
Or from within calibre by right-clicking the preferences button or using the `Ctrl+Shift+R` keyboard shortcut.
When running from the command line, debug output will be printed to the console, when running from within |app| the output will go to a txt file.
You can insert print statements anywhere in your plugin code, they will be output in debug mode. Remember, this is python, you really shouldn't need anything more than print statements to debug ;) I developed all of |app| using just this debugging technique.
It can get tiresome to keep re-adding a plugin to calibre to test small changes. The plugin zip files are stored in the calibre config directory in plugins/ (goto Preferences->Misc and click open config directory to see the config directory).
Once you've located the zip file of your plugin you can then directly update it with your changes instead of re-adding it each time. To do so from the command line, in the directory that contains your plugin source code, use::
calibre -s; sleep 4s; zip -R /path/to/plugin/zip/file.zip *; calibre
This will shutdown a running calibre. Wait for the shutdown to complete, then update your plugin files and relaunch calibre.
It relies on the freely available zip command line tool.
More plugin examples
----------------------
You can find a list of many, sophisticated |app| plugins `here <http://www.mobileread.com/forums/showthread.php?t=118764>`_.
Sharing your plugins with others
----------------------------------
If you would like to share the plugins you have created with other users of |app|, post your plugin in a new thread in the
`calibre plugins forum <http://www.mobileread.com/forums/forumdisplay.php?f=237>`_.