Your time is precious. I will not waste it. You want to write a plugin for Scribes in 60 secs. And you want to do it without making sense of pixels on your screen, blinking aimlessly at source code or, God forbid, attempting to code. Congratulations, you've hit gold. Welcome to writing plugins for Scribes, quick and dirty edition.
Download scribesplugin
Update: The scribesplugin script now ships with Scribes. There's no need to download the tarball.
Download this tarball to your computer and untar it. It contains a Python script named scribesplugin. The script generates a working Scribes plugin.
wget http://scribes.sf.net/scribesplugin.tar.bz2
tar xjvf scribesplugin.tar.bzr
scribesplugin Usage
Open your favorite terminal and change to the directory scribesplugin is located. Now type the following command at the terminal.
./scribesplugin --name="Foo" --shortcut="<alt>
Hit enter and set your timer. Nevermind, times up! Congratulations! You've just created your first Scribes plugin. You did it in less than 60 seconds!

Lets see if it works. Press ctrl+q to close all instances of Scribes. Launch Scribes and press alt+BackSpace. You should see this.
Generic plugins are placed here.
~/.config/scribes/GenericPlugins/
Language specific plugins are place here.
~/.config/scribes/LanguagePlugins/
Plugin Files and Folders
You created a generic plugin. Via your terminal go to the generic plugin folder. We'll explore every file in your plugin.
cd ~/.config/scribes/GenericPlugins; ls
Plugin Names
You named your plugin Foo. I won't tell your "comp sci 101" professor about it. I promise. But we both know that's a terrible thing to do. Next time name your plugins more sensibly. For example, "ErrorChecker", "LineDeleter", "HelpBrowser", or "XMLSyntaxChecker" are better and more descriptive names to give your plugin. Don't put spaces or esoteric characters in your plugin names either. Unix doesn't like that. Stick to alphanumeric characters only. You spend 90% of your programming career naming things. It helps to be thoughtful and diligent about it.
The Plugin Loader - PluginFoo.py
PluginFoo.py is a plugin loader. The plugin loader gives Scribes information it needs to load plugins. Plugin loaders begin with the string, "Plugin." The name you provide scribesplugin is used to generate the plugin loader. Name your plugins with unique and descriptive names.
Update the "authors" field with your name and email. Update the "short_description" and "long_description" fields to reflect the purpose of your plugin.
Main Plugin Folder - Foo
Foo contains modules that implement your plugin. Foo is generated by scribesplugin based on the name you provided it.
Keyboard Shortcut Binder - Trigger.py
Scribes uses triggers to bind keyboard shortcuts to actions. In your case, you bound alt+backspace to an action that shows an information window and a message bar. In Scribes all keyboard bindings are implemented in Trigger.py as a matter of convention. Bind all keyboard shortcuts in Trigger.py
Note, trigger.py is only created if you use the --shortcut option of scribesplugin.
Notes on Keyboard Shortcuts
Your plugin should NOT use a keyboard shortcut already in use by Scribes. Keyboard shortcuts should follow the format understood by GTK+. <alt>b, <ctrl><shift>c and <ctrl>Delete are valid formats. alt+b, ctrl-shift-c or ctrl<Delete> are not.
Message Passing System - Signals.py (IMPORTANT!)
Signals are the foundation of Scribes' architecture. Objects, in Scribes, communicate almost exclusively via signals. It's an event-based message passing system. Programmers with a strong web development background may be find this system odd or even detestable.
Event-based programming is still somewhat esoteric in the web development sphere. Ajax only became cool a few years ago. But we've been doing Ajaxy stuff on the desktop for about 20 years and counting. Wrapping your head around callback, event-based, asynchronous and timer-based programming is essential for writing desktop applications and very useful for understanding Scribes' architectural design. Lets see how it works.
Anatomy of the Signal System
It's very simple.
All objects are specialists. They specialize in performing one function, or responsibility, extremely well. Objects can broadcasts signals before, during or after their tasks are finished. Interested objects listen to these signals and react to them. A signal may be a simple message or a message accompanied by data. Design junkies will immediately recognize this as the "Observer pattern."
All objects keep to themselves. They mind their own business. They never dabble in the workings of other objects. To enforce this encapsulation, almost all objects in Scribes are private. No kiddin! They are not designed to be inherited, called, introspected, delegated, abstracted, interfaced, factorized, abridged, polymorphisized, decorated, or whatever obtuse programming technique the overzealous genius engineer in you will attempt to do.
It's a message passing system at its simplest, purest and finest form. We won't discuss the benefits and drawbacks of this system. All I can say is that this system is robust, scales very well and works for me. Scribes uses GObject for signal management.
Signals.py For Reals
Add new signals to this module as needed.
Signal Alert
You're under no obligation to use signal-based programming when writing plugins for Scribes. Use whatever programming style makes you happy.
The Mediator - Manager.py
Objects listen to and broadcast signals, but how? Simple, they use the manager object. Manager.py is called a "Mediator" in software engineering jargon. Objects that want to broadcast or listen for signals link to the manager. Manager.py is also responsible for creating these objects. Manager links to these objects during their creation. Manager is also the gateway between your plugin and the outside world, in particular the editor.
Since Manager.py is such a key figure, it's okay for it to have public methods. Afterall, the world interacts with your plugin via it. In fact, it's one of the few types of objects that has public methods. Most manager objects will have a public "destroy" and "activate" method. "destroy" should emit a signal to destroys all created objects. This is necessary for unloading plugins. For plugins that implement graphic user interfaces they may even have a "gui" attribute. If you choose to use signals for object communication initialize new objects in Manager.py. Manager.py is generated by scribesplugin.
Foo.py
Foo.py is where you do your thing. Hack new features. This tutorial is a quickie. We won't get into implementation details or design philosophies. Use your best judgment and experience. When in doubt study other plugins in Scribes or email me. Foo.py is automatically generated by scribesplugin. The name of this module depends of the name you provide scribesplugin.
Exceptional Handling - Exceptions.py
Put your exceptions in Exceptions.py. Exceptions.py is generated by scribesplugin. Remove this module if you don't need it.
Utility Functions - Utils.py
Global utility functions, constants and variables go in Utils.py. Utils.py is generated by scribesplugin. Remove this file if you don't need it.
The Bullshit-Free API Technology - Editor.py
If you've been around the block long enough, you've probably encountered it. Libraries and frameworks that demand you study hundreds of hours of blogs, wikis, tutorials and books to imprint "hello world" on your flat screen. And to do that, you'd need to inherit this and interface with that. Then proceed to import this from that and that from this. That's after you've created the abstract public factory, whose sole purpose for existing is just to piss you off.
Inevitably, you spend many full moons studying the code of the library or framework in question just to figure out what the hell is going on. Only to reckon, disappointingly, that relevant information is scattered all over the place. It's an exasperating and frustrating experience. I know. I've been there.
That's why I designed the "Bullshit-Free API Technology" (TM) for Scribes. And for a limited time offer you can get the ...
All jokes aside. Scribes has just one API, the "editor" object.
The editor object represents an instance of a Scribes editor. You get all the information you need about Scribes from it. You can also tell Scribes to perform common operations with it.
Examples:
editor.window - get window object
editor.update_message - show feedback message
editor.instances - a list of running editor objects
editor.new - creates a new editor
editor.focus_file - focus a particular document
editor.version - version of Scribes
editor.uri - uri of the file open
Editor.py houses all the public APIs available for Scribes. It's perhaps the only module you need to concern yourself with. Almost all modules, besides Editor.py, are implementation details and are private. Read the signals section above for why this is so. There are noticeable exceptions. I can count these exceptions in one hand. For the most part, Editor.py is essentially Scribes documentation.
Isn't that a relief? Thank God for "Bullshit-Free API Technology."
Language Specific Plugins
Language specific plugins are loaded for specific file types.
./scribesplugin --name="PythonFoo" --shortcut="BackSpace" --language="python"
Congratulations! You've just created a plugin for Python source codes.This plugin will be loaded only when you're editing Python files. Create a python file and open it in Scribes. Now press alt+shift+BackSpace. You'll see this.
Again this plugin only works for Python source code because it's language specific. Language plugins are located at:
~/.config/scribes/LanguagePlugins
scribesplugin script
scribesplugin creates a working plugins that saves you the trouble and time of setting up plugins manually. It's left to you to modify, explore, experiment and extend the plugin as you wish.
--name is the only required option for scribesplugin. It names your plugin.
--shortcut binds your plugin to a keyboard shortcut. It's optional.
--language makes your plugin language specific. It's optional.
scribesplugin will be shipped with Scribes in version 0.4
You The Man
You did well. Pat yourself on the back. You made two Scribes plugin without even blinking.
I appreciate feedback.










Will there be a repository or something to share our plugins with the world! :)
ReplyDelete@Anonymous: Yes
ReplyDelete