diff options
Diffstat (limited to 'trunk/HACKING')
-rw-r--r-- | trunk/HACKING | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/trunk/HACKING b/trunk/HACKING new file mode 100644 index 0000000..cafab29 --- /dev/null +++ b/trunk/HACKING @@ -0,0 +1,173 @@ +HACKING Dimbola for fun and very little profit +============================================== + + +Introduction +------------ + +This file has some notes about how Dimbola should be developed. + +All the generally accepted good Python coding stuff applies: PEP8 +formatting, using only spaces and no tabs for indentation, etc. We +cover here only things specific to the Dimbola project. + + +Overview of the code base +------------------------- + +* dimbola-gtk -- the main program; this is intended to be a one-liner + +* dimbola/*.py -- Python package with the core of the app + +* dimbola/plugins/*_plugin.py -- plugins that come with the core app; + as much as possible is put into plugins + +* dimbola/ui.ui and dimbola/plugins/*.ui -- glade/GtkBuilder files; these + are loaded automatically + +* dimbola/*_tests.py and dimbola/plugins/*_tests.py -- unit tests for + the corresponding files + + +Development priorities +---------------------- + +The main priority should always be to fix bugs. Each release should +fix all bugs, if at all possible. It is obvious that this is not +always possible, but it is the goal anyway. Better to attempt something +really good and almost get it than to play it safe. + + +Version numbering +----------------- + +The canonical location of the version number is in dimbola/__init__.py +as the version variable. setup.py will extract it from there. ui.py will +use dimbola.version to set the version number in the Help/About dialog +box. + +Version number until 1.0 will be as follows: + + 0.0.x pre-alpha versions, not intended to be usable at all + 0.x.0 alpha and beta versions; these are intended to be usable, + though they are probably really buggy + 1.0.0 first release intended to be usable by a non-hacker + +Eventually, a ROADMAP file will be written to specify what 1.0.0 should +contain. + + +Writing plugins +--------------- + +To write a plugin, import the dimbola package, and subclass dimbola.Plugin. +The initializer MUST have the following signature: + + def __init__(self, mwc): + +To use the plugin, put it in a file in dimbola/plugins/foo_plugin.py. + +Plugins may access the MainWindowController's attributes and methods. + +When a Plugin subclass is instantiated, it MUST NOT cause any side +effects: it must not modify the user interface, connect to signals, +or whatever. All of that must be done in the enable() method, and +un-done in the disable() method. This is necessary so that the +user may enable and disable any installed plugin. + +Plugins may add new items to menus via MWC.add_to_menu. The UI file +needs to specify a name for the menu widget, and this name is used +with add_to_menu. Additionally, the menu may have separators whose +names begin with prepend_separator or append_separator, and add_to_menu +will put the new menu item before or after such a separator. + +To remove the menu item (when the plugin is disabled), the +MWC.remove_from_menu method may be used. + +Similarily for sidebar sections: see MWC.add_to_sidebar and +MWC.remove_from_sidebar. + +Plugins may have their own user interface definition files. For +a plugin named foo_plugin.py, the file foo.ui is loaded automatically, +if it exists. The plugin may access the widgets via +MainWindowController.widgets. + + +Signals and hooks +----------------- + +We try use GObject signals for communicating between parts of +the code. This decouples different modules, which is a good thing. +For example, the thumbnail grid is implemented using +model/view/controller. When the list of photos in the model +changes, the view must redraw itself. Rather than having the model +call a method on the view, the view connects to a signal in the +model. Thus, the model need not know anything about the view. +Indeed, other things than the view can make use of the same +signal. + +In order to connect to a signal, you need to know the object the +signal applies to. This is awkward when a plugin provides the +object and the signal. We work around this by adding the signal +to a well-known object, the MainWindowController, using the +new_hook method. + +Thus, in Dimbola, a hook is a custom signal added to MWC. + + +Unit tests and coverage +----------------------- + +The goal is to make Dimbola a very good program. Part of that is to make +it very reliable, and the way to get there is to have an automated unit test +suite with very good coverage. + +Currently, there is abysmal coverage. The current code was written in haste, +to get a proof-of-concept prototype done very quickly. Writing tests would +have slowed things down. + +The strategy to get to good coverage is as follows: all of the current +GUI code exists in dimbola/db.py and dimbola/ui.py. db.py may get some +unit tests later on, but ui.py is going to be test-less. However, as +much code as possible will be broken out of it, into other modules, and +those modules will have unit tests. The goal is to have those other +modules have 100% statement coverage. + +Almost all the code will be called in response to GTK+ events and signals, +from callback functions. The general design principle here, as far as +testing is concerned, is that the actual callback function will be not +be tested, but it will be as simple as possible and call functions +elsewhere to do the actual work. Those functions will be unit tested. + + +Using Glade +----------- + +The UI is defined using Glade and GtkBuilder. Everything that can +reasonably be done in Glade shall be done in Glade. + +Widgets in the .glade file and their signal callbacks will be +connected automatically based on a naming convention. For a widget +named foo, and its signal named bar, a callback will be connected +automatically if the code has a method called on_foo_bar in a relevant +controller object. + +This means the .glade file should not have any signal handlers defined. + +Widgets that do not have callbacks can use whatever name Glade gives +them by default (e.g., "textview123"). If a callback is needed, the +widget should be renamed in a sensible way that indicates its use +(e.g., "photo_tags_textview"). + +See dimbola/gtkapp.py for details on the magic. + + +Background processing +--------------------- + +Threads are evil, at least in the context of Python and GTK+. We do not +use (explicit) threads in Dimbola. Instead, we use glib.idle_add and +the Python standard library module multiprocessing. The latter is wrapped +inside the BackgroundManager and BackgroundJob classes; see the +dimbola/bgjobs.py module. + |