Cleanup Note: This article should be cleaned up to be more readable and/or to more closely follow the Wiki article guidelines.
The content of this article is out-of-date and should be updated with the latest details.
A plugin is an extension module which can be loaded at runtime. It adds new functionality to Snowberry. The plugins are independent and can consist of one or more Python modules.
This article also describes guidelines of Snowberry's design. You get a general overview on the modules and plugins of Snowberry, and we also offer you a tutorial for making plugins to add Snowberry your own functionality.
About the Snowberry architecture
The main idea was to make Snowberry easy to extend and customize. Python offers a possibility to load plugins at runtime making it easy to fulfill the extensibility requirement. Python has also some other advantages like effortless portability, readability of code and good documentation.
The basis for the system architecture is a combination of plugin and MVC architectures. The program has been divided into modules and plugins where modules create a kind of a frame for the plugins. More information can be found in the API documentation of Snowberry. [Apidoc]
There are 12 modules which are located in your system-specific folder <SYSTEM> [6.1]:
- addons.py – contains all the loaded addons in a table and handles all action considering the addon database.
- events.py – offers the service of sending events to all modules that are registered to being listeners.
- language.py – makes the translation of texts of the UI and stores them.
- parser.py – The function of this module is to make it easier for other modules to read file contents considering configuration, profile and meta data files.
- paths.py – offers other modules and plugins the services with which they find out the locations of configuration, profile and meta data files.
- plugins.py – loads all the plugins from their home folder; used by snowberry.py.
- profiles.py – contains all information about all profiles and the values of the settings. Also manages all changes of these.
- settings.py – contains the information about the components and different settings that the program contains (but not their values which are in profiles.py). It creates the command line for the settings and helps profiles to decide which settings are suitable for them.
- snowberry.py – acts as the main; starts the program.
- template.py – is an empty template that can be used as the basis for creating new modules.
- ui.py – acts as an interface which different modules use to get to use the wxWidget (the UI) services. This module creates the UI areas. It also acts as a middleman for wxWidget events.
- widgets.py – describes the different widget components.
The seven original plugins of Snowberry are located in both <SYSTEM>/plugins and <HOME>/plugins. The plugins are loaded in alphabetical order – that the reason for the naming of the tabs:
- help.py – creates and refreshes the contents of the help area.
- launcher.py – creates the play button, handles the play command and generates the full command line for the game.
- profilelist.py – creates the profile list, the context menu for that and the buttons for creating a new profile, deleting a profile and duplicating one.
- tab10_summary.py – creates the Summary page and refreshes its contents when user changes the profile.
- tab30_addons.py – creates the Addons page and the context menu for the addons tree. It also creates buttons for installing and uninstalling addons, and for changing their settings and load order.
- tab40_settings.py – creates the Settings page and takes care of its refreshing. Creates new settings if needed and manages the values of the settings.
- wizard.py – implements the modal dialog for changing the basic settings of the system.
Implementing new functionality
Generally speaking the plugins act as independent controllers which, by using the modules, take care of the appearance of the UI presented by ui.py. The internal communications of the application is implemented using the services offered by events.py. A more precise information about the interfaces that the modules and plugins offer is available in the API Documentation of Snowberry. [Apidoc]
Life span of a plugin
- Plugin is initialized: init().
- Plugin creates one or more widgets during init().
- Plugin registers itself as a Command and Notify events listener.
- When plugin notices a right kind of Command event it reacts by performing an assigned action.
Breakdown Note: This article should be broken down to multiple articles so that each one covers a more specific topic. Remember to categorize!
Tutorial: Creating a plugin
Creating Python plug-ins is a rather straightforward process. This section describes the things you need to know to create functioning Snowberry plug-ins. Snowberry assumes all the plug-ins to be valid Python code and reside under folder named plugins in your Snowberry installation directory.
The initial step
First of all, you should decide a name for your plug-in. The name of the file is important in a sense that Snowberry looks in and loads (in runtime) the files and directories in an alphabetical order. From these files Snowberry then loads the possible user interface widgets to be shown in the application when appropriate. Snowberry plugins folder already contains some built-in plugins you can examine as examples how to create plug-ins and to acquire some insight how they interact with the rest of the system. Of course, you should decide in beforehand the functionality your plug-in is going to add to Snowberry.
Snowberry (ui.py) doesn't encourage the plug-ins to place their user interface widgets explicitly but places them in the order they have been read in to the system. It is possible to explicitly place the widgets, but in general not encouraged. Placing the parts explicitly by yourself it is very likely you wreck the layout algorithm and data structures ui.py uses to manage the user interface. Thus, allowing the whole user interface to become unreadable. Moreover, if you won't use the widgets provided by widgets.py, you are not able to get the benefits of automatically updating the user interface when language settings have been changed.
The second step: Inside the plugin model
As described earlier, Snowberry loads the plug-ins in an alphabetical order at runtime. Your plug-in should be placed in a separate folder of its own with a name followed by a dot and an extension "plugin". There is an example plug-in coming with the standard Snowberry distribution, "example.plugin". Each and every plug-in bundle should include a sub-folder called contents, in which are placed the actual Python code modules the addon is supposed to provide. Another sub-folder each plug-in should include is conf, which includes the configuration data and other resources the plug-in might have.
The same rules apply to these Python modules as to the addons in general; they shall be loaded in an alphabetical order.
To be effectively loaded, every plug-in that is placed in the plugins directory must have a method called init. Init doesn't take any arguments and it doesn't return anything. It should contain all the necessary initialization code setting up the plug-in to function properly. Usually this means creating the widgets to be displayed and registering the callback functions that implement the handling of events and notifications received from the system. Snowberry uses only events and notifications to broadcast system-wide happenings.
To actually create the widgets and register your event and notification handlers, you have to import the appropriate modules ui.py and events.py, respectively.
If you don't register to listen to events or notifications from the system, your plug-in isn't aware of the happenings in the rest of the system. Also, events.py is used to notify the rest of the system regarding global events taking place in your module.
The third step: The actual code
You should remember creating all of your widgets through ui.py and the interface it exposes. This is done by requesting a part of a user interface from ui.py and using this part to load the widgets. In the following there is some short code snippets taken from tab1_summary.py that will demonstrate the basic functionality of any widget.
In the very first lines of code after the copyright information you can find lines like the following:
import string import ui, events import profiles as pr import addons as ao import language
As you can see, there are several other modules loaded besides just those ui.py and events.py described earlier. The next lines initialize the user interface:
area = ui.createTab(SUMMARY)
This creates a summary tab to the tab area. You'll notice this when running Snowberry. The next lines are usually something like these:
area.setBorder(6) area.setWeight(0) global titleLabel titleLabel = area.createText() titleLabel.setTitleStyle() area.createLine()
They are setting up the user interface widgets and formatting them. The last line in the init function adds the notify listener of this module:
If there were a command listener also, it would be called like this:
The rest of the code deals with the actual code for handling the internal plug-in functionality. One thing you should remember is that if you change some of the visible parts of the user interface, you'll have to call the function updateLayout() in ui.py for you specific area to update the layout (so that the end user can see the updates).
Sending events would be done calling function send() or sendAfter() in events.py. Both of these functions take an event or a notification as their parameter. The difference is that by calling send you allow the events to be sent immediately to all of the registered listeners. SendAfter() sends the events after all of the registered listeners have been delivered the previously sent event.
These are the actual main lines of creating plug-ins to Snowberry. You can easily find out more information in the plugins folder by taking a look at the code in plugins already contained there