Monday 16 September 2013

Python Spyder-IDE plugin example

So the other day I wanted to create a plugin for Spyder-IDE to create my own plugin when I realised that there is very little code examples, or for that matter, very little documentation available.

So, what is one to do? Well, after reading through lots of code, very few websites, I decided to hack away and here is what I ended up with: my very own little "hello world" plugin for Spyder-IDE 2.30.dev6.

This code comes with no warranty whatsoever, and I unfortunately do not have time to provide any feedback or support, so you really are on your own.

1. Create a "p_helloworld.py" file in your WinPython folder (WinPython 3.3/python-3.3.2/Lib/site-packages/spyderplugins/p_helloworld.py) and copy the following code into it:

"""
HelloWorld Plugin

    - Basic implementation of Hello World plugin
    - Shows example of how to add Hello World to the menu, e.g.
        Run -> Hello World
    - Also adds a HelloWorld item to theConfiguration Page, e.g.
        Tools -> Preferences
"""

from spyderlib.qt.QtGui import QVBoxLayout, QGroupBox, QLabel
from spyderlib.qt.QtCore import SIGNAL

# Local imports
from spyderlib.baseconfig import get_translation
_ = get_translation("p_helloworld", dirname="spyderplugins")
from spyderlib.utils.qthelpers import get_icon, create_action
from spyderlib.plugins import SpyderPluginMixin, PluginConfigPage

from spyderplugins.widgets.helloworldgui import HelloWorldWidget


class HelloWorldConfigPage(PluginConfigPage):
    """
    The Configuration Page that can be found under Tools -> Preferences. This
    only displays a label.
    """
   
    def setup_page(self):
        """ Setup of the configuration page. All widgets need to be added here"""
       
        setup_group = QGroupBox(_("HelloWorld Plugin Configuration"))
        setup_label = QLabel(_("HelloWorld plugin configuration needs to be "\
                               "implemented here.\n"))
        setup_label.setWordWrap(True)

        # Warning: do not try to regroup the following QLabel contents with
        # widgets above -- this string was isolated here in a single QLabel
        # on purpose: to fix Issue 863
        setup_layout = QVBoxLayout()
        setup_layout.addWidget(setup_label)
        setup_group.setLayout(setup_layout)

        vlayout = QVBoxLayout()
        vlayout.addWidget(setup_group)
        vlayout.addStretch(1)
        self.setLayout(vlayout)

class HelloWorld(HelloWorldWidget, SpyderPluginMixin):
    """HelloWorld class that implements all the SpyderPluginMixin methods"""
    CONF_SECTION = 'helloworld'
    CONFIGWIDGET_CLASS = HelloWorldConfigPage
   
    def __init__(self, parent=None):
        HelloWorldWidget.__init__(self, parent=parent,
                              max_entries=self.get_option('max_entries', 50))
        SpyderPluginMixin.__init__(self, parent)
       
        # Initialize plugin
        self.initialize_plugin()
       
    #------ SpyderPluginWidget API ---------------------------------------------   
    def get_plugin_title(self):
        """Return widget title"""
        return _("HelloWorld")

    def get_plugin_icon(self):
        """Return widget icon"""
        return get_icon('helloworld.png')
   
    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        #return self.datatree\
        pass
   
    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []

    def on_first_registration(self):
        """Action to be performed on first plugin registration"""
        self.main.tabify_plugins(self.main.inspector, self)
        self.dockwidget.hide()
        #pass

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.connect(self, SIGNAL("edit_goto(QString,int,QString)"),
                     self.main.editor.load)
        self.connect(self, SIGNAL('redirect_stdio(bool)'),
                     self.main.redirect_internalshell_stdio)
        self.main.add_dockwidget(self)
       
        helloworld_act = create_action(self, _("Hello World"),
                                     icon=get_icon('helloworld.png'),
                                     triggered=self.show)
        helloworld_act.setEnabled(True)
        self.register_shortcut(helloworld_act, context="HelloWorld",
                               name="Run helloworld", default="F10")
       
        self.main.run_menu_actions += [helloworld_act]
        self.main.editor.pythonfile_dependent_actions += [helloworld_act]

    def refresh_plugin(self):
        """Refresh helloworld widget"""
        #self.remove_obsolete_items()  # FIXME: not implemented yet
        pass
       
    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True
           
    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        # The history depth option will be applied at
        # next Spyder startup, which is soon enough
        pass
   
    def show(self):
        """Show the helloworld dockwidget"""
        if self.dockwidget and not self.ismaximized:
            self.dockwidget.setVisible(True)
            self.dockwidget.setFocus()
            self.dockwidget.raise_()

    #------ Public API ---------------------------------------------------------   
   
       
#===============================================================================
# The following statements are required to register this 3rd party plugin:
#===============================================================================
PLUGIN_CLASS = HelloWorld


1. Create a "helloworldgui.py" file in your WinPython folder (WinPython 3.3/python-3.3.2/Lib/site-packages/spyderplugins/widgets/helloworldgui.py) and copy the following code into it:

"""
HelloWorld widget

    - Basic implementation of a GUI for the Hello World plugin
"""

from __future__ import with_statement

from spyderlib.qt.QtGui import (QHBoxLayout, QWidget, QVBoxLayout,QLabel)
from spyderlib.qt.QtCore import QTextCodec
locale_codec = QTextCodec.codecForLocale()

import sys

#def is_helloworld_installed():
#    from spyderlib.utils.programs import is_module_installed
#    return is_module_installed('cProfile') and is_module_installed('pstats')


class HelloWorldWidget(QWidget):
    """
    HelloWorld widget
    """
   
    def __init__(self, parent, max_entries=100):
        """ Creates a very basic window with some text """
       
        HELLO_WORLD_MESSAGE = \
            "The Plugins for Spyder consists out of three main classes: \n\n" \
            "1. HelloWorld\n\n" \
            "\tThe HelloWorld class inherits all its methods from\n" \
            "\tSpyderPluginMixin and the HelloWorldWidget and performs all\n" \
            "\tthe processing required by the GUI. \n\n" \
            "2. HelloWorldConfigPage\n\n" \
            "\tThe HelloWorldConfig class inherits all its methods from\n" \
            "\tPluginConfigPage to create a configuration page that can be\n" \
            "\tfound under Tools -> Preferences\n\n" \
            "3. HelloWorldWidget\n\n" \
            "\tThe HelloWorldWidget class inherits all its methods from\n" \
            "\tQWidget to create the actual plugin GUI interface that \n" \
            "\tdisplays this message on screen\n\n"

        QWidget.__init__(self, parent)
       
        self.setWindowTitle("HelloWorld")
       
        self.output = None
        self.error_output = None
       
        self._last_wdir = None
        self._last_args = None
        self._last_pythonpath = None
       
        self.textlabel = QLabel(HELLO_WORLD_MESSAGE)       

        hlayout1 = QHBoxLayout()
        hlayout1.addWidget(self.textlabel)
        hlayout1.addStretch()
         
        layout = QVBoxLayout()
        layout.addLayout(hlayout1)
        self.setLayout(layout)
           

def test():
    """Run HelloWorld widget test"""
    from spyderlib.utils.qthelpers import qapplication
    app = qapplication()
    widget = HelloWorldWidget(None)
    widget.resize(400, 300)
    widget.show()
    sys.exit(app.exec_())
   
if __name__ == '__main__':
    test()
 

3. Create a "helloworld.png" file in your WinPython folder (WinPython 3.3/python-3.3.2/Lib/site-packages/spyderplugins/images/helloworld.png) by copying the following image file.



 That's it! Close and re-open Spyder-IDE and you should now be able to see a HelloWorld plugin under both the [Run] and the [External Tools] menu.