Plugins

Plugins are python modules that add extra features. Sometimes they are used as a way to implement features with special dependencies: the sound plugin, for example, uses gstreamer, winsound, and others.

Structure

Start a plugin with this code:

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

from Plugin import Plugin

class MainClass(Plugin):

    def __init__(self, controller, msn):
        Plugin.__init__(self, controller, msn)

        self.description = _('A few words about your plugin')
        self.authors = {'Name': 'email'}
        self.website = 'url'
        self.displayName = _('Plugin Name')
        self.name = 'PluginName'

        self.enabled = False

    def start(self):
        self.enabled = True

    def stop(self):
        self.enabled = False

    def check(self):
        return (True, 'Ok')

__init__() is called once, when the plugin class is created

start() and stop() are called when the user enables/disables the plugin and at startup/shutdown respectively

check() returns a tuple (bool, str) indicating if the user can enable that plugin. If the bool is False, a error message with that str is displayed. Always handle here module dependencies, because if you leave them unhandled, the plugin won't display in the list.

Plugin config

In your plugin __init__:

self.config = controller.getConfig()
self.config.readPluginConfig(self.name)

Getting and setting values:

self.config.getPluginValue(self.name, 'key', 'default value')
self.config.setPluginValue(self.name, 'key', 'new value')

Plugin coding methods

currentSong

See CurrentSongPlugins

slashCommands

controller.Slash.register

Parser

Roger's Parser.py is, or should be, fully pluggable. So, someone should write docs to prove it.

<object>

Implementations: Youtube, Custom Emoticons

Our version of htmltextview supports some special handling of the <object> html tag. Note that this implementation is incompatible with the html standard, and very emesene specific.

<object class="class" type="application/x-myplugin" />

The class attribute is not the same CSS class attribute as you may expect, it is a object identifier that can be shared across similar objects (useful in custom emoticons)

This <object> tags are handled by callbacks specified in ConversationUI.textview.customObjectsCallbacks. It is a dict {'mime/type': (h1, h2)}

TODO: continue

Code replacement plugins (hacks)

module.class.function = self.newfunction

GUI changes

If you want to change something in the GUI, just access to that object and do the changes. But sometimes accessing to the object isn't simple.

With the main window it's very easy, just go to controller.mainWindow and there you have the window object with all the widgets. But conversations are a bit more complex, because they are created on the fly. There are two helpful ways to get all the ConversationUIs: Plugin.getOpenConversations() and the 'new-conversation-ui' signal on conversationManager.

Plugin.getOpenConversations(), as the function name suggests, returns a list of open conversations. Each item is a Conversation instance -- not a ConversationUI instance!. The ConversationUI is in the ".ui" attribute of each Conversation object.

The 'new-conversation-ui' signal is a bit more complex, you have to connect it, define a callback with special parameters, and disconnect it later. On start() add this:

self.convmanagerId = self.controller.conversationManager.connect('new-conversation-ui', self.callback)

That tells conversationManager to call the "callback" function in your plugin (replace that with a descriptive name) every time a ConversationUI is created. A signal identifier is returned and saved in self.convmanagerId - that allows to disconnect the signal later, in stop():

self.controller.conversationManager.disconnect(self.convmanagerId)

Now to the function definition:

    def callback(self, conversationManager, conversation, window):

Only the "conversation" parameter is useful here, everything else is required by signal definition:

  • self: because we are inside the plugin main class (chapter 9...)
  • conversationManager: the first argument for all gobject callbacks is the object that emits the signal. (describing it just for completeness, but right now that doesn't matter)
  • conversation: The conversation object itself, woo-hoo!
  • window: The ConversationWindow? that contains this conversation. Same thing as conversation.parentConversationWindow

..now, what to do inside that function and the getOpenConversations loop? The same thing, so I'd suggest calling another function that alters a single ConversationUI (DRY). If you still don't know how to hack on it, you should read the init on ConversationUI.py or the pygtk tutorial