Development:Structure

From OpenLP
Jump to: navigation, search
Please note: This is a work in progess

It's always difficult to determine how to structure an application, not only before starting the application, but also WHILE developing your application. OpenLP has grown organically over the last 10 years, and this means that the codebase has become unwieldy with a number of circular imports and other questionable coding practices.

This page will hopefully help us to structure OpenLP's codebase better.

Module Structure

  • openlp
    • core
      • api
      • display
      • common
      • projector
      • mediaplayers
      • ui
      • widgets
    • plugins
      • songs
      • bibles
      • presentations
      • media
      • images
      • custom

Tips

  • Keep __init__.py fairly empty. One of the issues we've had in the past is importing a lot of things into __init__.py in an effort to make import statements shorter, but this has only caused more problems than it is worth, including the terror of coding: circular imports.
  • Keep import statements to a minimum. Only include the imports you absolutely need.
  • Use as full of a module path as possible in your import statements. This ties in with the first tip. Rather import from openlp.core.common.registry (avoiding circular imports) than importing openlp.core.common.registry contents into openlp.core.common and then importing from openlp.core.common.

GUI vs Library

Always keep GUI and Library code separate. The easiest way to do this is to make sure that all windows, dialogs, widgets and message boxes originate from another GUI element.

For example, if you encounter an error in your networking code while the user is downloading a Bible, rather let an exception bubble up to the import wizard and let the wizard handle the exception and show an error message.

Multiple Inheritance Uglies

Single inheritance is always simple. You have one parent class, and calling super() just calls the parent's __init__() method. Python supports multiple inheritance, and this can get quite tricky.

For more information on multiple inheritance and the super() function in Python, I highly recommend reading the following article: http://www.artima.com/weblogs/viewpost.jsp?thread=281127

Base Classes vs Mixins

In the OpenLP codebase, we make use of both base classes and mixins. The key difference between these two is that a mixin does not have an __init__() method. If you add an __init__() method to a mixin, you will encounter a number of inheritance-related error messages.

When adding mixins to your classes, make sure that you always add them LAST. For example:

class AlertsManager(QtCore.QObject, RegistryBase, LogMixin, RegistryProperties):
    pass
QtCore.QObject
This is a PyQt5 class. PyQt5 classes (thankfully) are already configured to handle multiple inheritance correctly. Place PyQt5 classes first where possible.
RegistryBase
This is a Python base class. This class will need the correct __init__() method in order to handle multiple inheritance correctly. Python base classes should be placed after PyQt5 classes.
LogMixin
This is a Python mixin class. Mixin classes should ALWAYS be placed last.
RegistryProperties
Also a mixin class.

Within OpenLP, the Ui_{name} classes are also mixins. Make sure they also go last.

Here's an example of a UI widget that inherits from PyQt5, our own base class, and a bunch of mixins. Take note of the order of the inherited classes.

class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixin, RegistryProperties):
    pass