3.2. Config Tool Plugin APIs¶
The modules that are described in this section define the APIs to which plugins to the Config Tool can provide concrete implementations.
3.2.1. Background¶
Unlike Java, Python does not have an interface concept defined in the language. However the ability to specify interfaces indepenent of implementation is a very powerful mechanism which provides a cleaner definition of the published APIs within this application.
The zope.interface package, which was initially devloped by the
Zope community quite early on in Python’s evolution, is still a useful
framework. It defines an abstract interface using the standard
Python class, and encourages a few conventions that make it work. The
most important conventions are:
- the class that defines an interface should have a leading
Iin its name. - the class that defines the interface must derive from the
zope.interface.Interfaceclass. - abstract method declarations in the interface are defined just
as Python methods, but the
selfparameter must be omitted. The body of the method should just have apassor, ideally, a docstring describing the semantics of this abstract method. - Abstract attributes of the interface must use the
zope.interface.Attributeclass to define them. See examples below of some of these attributes.
The Zope package provides a framework that enables us to add logic
around interfaces and implementations. For example, we can define a
concrete factory class that will create an instance of this
interface. A concrete implementation of a particular interface should
use the decorator (@implementer) to declare to the Zope framework
which interface(s) this class is implementing.
A destinction is made between a “utility” interface, and an “adapter”
interface. This disctinction is important, and should be understood by
reading the documentation on the zope.interface package.
3.2.2. Top Level Utilities¶
Package to hold interface definitions for elements of the Qualcomm Device Configuration Tool using the zope.interface mechanism.
This module defines a number of utilities methods to make the use of Zope’s interface, component and configuration APIs a little easier.
Some understanding of the zope interface component might be useful: it’s definitely worth a read to help make sense of how the Config Tool uses it.
-
qct_interfaces.clear_components() → None[source]¶ This method is unlikely to be used in an application context, but it is useful in running unit tests. It clears out the zope configuration registry.
-
qct_interfaces.list_components() → List[Tuple[str, str]][source]¶ This method finds all the plugins for this tool. A plugin is defined as an installed Python package that has
config_tool_pluginentry point defined for it.Returns: a list of two-tuples: where the tuple contains the name of the Python package and its version.
-
qct_interfaces.list_packages() → List[Tuple[str, str]][source]¶ This method finds all the packages installed into this virtualenv, excluding the ones that look like config tool plugins.
Returns: a list of two-tuples: where the tuples contain the name of the Python package and its version.
-
qct_interfaces.load_components() → None[source]¶ This method finds all the
configure.zcmlfiles that have been defined as “data-files” for all the installed packages in the currently running Python environment.It achieves this by asking the
pkg_resourcesmodule to find all the installed packages. It returns a list ofpkg_resources.Distribution-derived objects, which can be queried to find out if aconfig_tool_pluginentry point has been declared for that package. The package calledpyconfigtoolis put at the beginning of the list so that any other installed packages can override definitions made in the defaultpyconfigtoolpackage.It then asks the
zope.configurationpackage to use these ZCML files to configure utilities, adapters and subscribers using thezope.interfacemechanisms.
-
qct_interfaces.make_registered_instance(iface, **kwargs)[source]¶ Create an instance of the interface
iface, and sets attributes on that instance. This could be seen as a simple factory method.This function works in the simple case where an adapter has been defined for the “null” adaption. This would be done in the ZCML like this:
<adapter for="" factory="packageA.module1.MyImplementation" provides="packageB.module2.IMyInterface" />
Then in the Python world you can create an instance of
MyImplementationwith the following:my_obj = make_registered_instance(packageB.module2.IMyInterface, param1=1, param2=2)
Note that in the definition of the interface,
param1andparam2ought to be defined as zope Attributes of that interface.
-
qct_interfaces.make_registered_named_instance(iface, name, **kwargs)[source]¶ Create an instance of the interface
iface, registered with the specifed name, and sets attributes on that instance. This could be seen as a simple factory method.This function works in the simple case where an adapter has been defined for the “null” adaption, but must have been named. This would be done in the ZCML like this:
<adapter for="" factory="packageA.module1.MyImplementation" provides="packageB.module2.IMyInterface" name="foo" />
Then in the Python world you can create an instance of
MyImplementationwith the following:my_obj = make_registered_named_instance(packageB.module2.IMyInterface, "foo", param1=1, param2=2)
Note that in the definition of the interface,
param1andparam2ought to be defined as zope Attributes of that interface.
3.2.3. Persistence of UI State¶
The definition of the IPersistState interface.
-
interface
qct_interfaces.persist.IPersistState[source]¶ The UI state of the Package Manager application is saved using this interface.
This interface will be used as a utility.
-
restore_config_state()¶ Return from the persistence store the path to the configuration database, and the URI of the previously connected device.
-
restore_application_window_state()¶ Return a two-tuple from the saved state:
- the top-level window geometry, as a QByteArray.
- the state of the docking areas, as a QByteArray
-
save_application_window_state(geometry, dock_state)¶ Save the top level window geometry and the state of the docking areas.
Parameters: - geometry (QByteArray) – The geometry of the main window.
- dock_state (QByteArray) – The positions and locations of the docking widgets on the main window.
-
STATE_VERSION= <zope.interface.interface.Attribute object>¶ A read-only attribute describing the version of the format that is being used to save the state. Changes to this value occur when incompatible format changes occur.
-
save_config_state(path_to_config_db, current_dev_uri)¶ Save the current user’s state, which consists of the file path to the current config database, and the URI of the currently chosen device connection.
-
3.2.4. Application Options¶
The definition of the IApplicationOptions interface.
-
interface
qct_interfaces.options.IApplicationOptions[source]¶ The command line arguments to the Package Manager application.
Rather than re-define all the command line options as interface attributes, the
optionsattribute of this class is simply theargparse.Namespaceinstance.This interface will be registered as a utility. This means that anwyhere in application code, the command line options to the app can be retreived using this pattern:
from zope.component import getUtility from pminterfaces.options import IApplicationOptions ... # e.g. get the verbosity command line argument: verbosity = getUtility(IApplicationOptions).options.verbosity
-
options= <zope.interface.interface.Attribute object>¶ The
argparse.Namespaceobject that is used to parse the command line arguments.
-
3.2.5. Device Configuration Database¶
The Config Tool maintains a tree of objects that define what is
visible on the user interface. This tree is tradionally referred to as
the “model” that the user interface interacts with, usually through
“views”. In this application the model objects are actually made
persistent simply by deriving them from the Zope Database’s base
class for persistence: persistent.Persistent.
This interface defines the methods to create and open one of these persisent object stores.
The definition of the IDeviceDatabase and IDatabaseConverter interfaces.
-
interface
qct_interfaces.database.IDatabaseConverter[source]¶ This interface is used to mark a conversion tool that converts the database object model to some external format and back again.
This is an utility interface that should be found by running the following:
impl = getUtility(IDatabaseConverter, "converter-name")
-
conversions_supported()¶ This method returns information about which direction this converter supports.
Returns: A two-tuple of booleans - the first indicates import support, and the second inicates export support.
-
export_from_database(root, to_location)¶ Actually perform the export to a file at the
to_location.
-
extensions_supported()¶ This method returns the list of filename extensions that this converter will export to and import from. This information might be used to display the files in a file selection dialog with the appropriate list of file extensions.
For example, the YAML converter could return
'('.yaml',).
-
conversion_description(export)¶ This method returns a human-readable description of the format. The boolean indicates whether the direction of the conversion is for export (
True) or import (False).
-
import_to_database(from_location, root, properties)¶ Actually perform the import from a file at the
from_location.
-
-
interface
qct_interfaces.database.IDatabaseLocation[source]¶ This interface defines a marker for a string URI that defines the location of the Device database.
-
location= <zope.interface.interface.Attribute object>¶ The URI of where the database should be located
-
-
interface
qct_interfaces.database.IDeviceDatabase[source]¶ This adapter defines the interface to creating and managing the ZODB that is used to hold the configuration.
An implementation of this interface can be created by running something like this:
from zope.component import getMultiAdapter from qct_interfaces.database import IDeviceDatabase ... dev_database = getMultiAdapter((database_file_location,), IDeviceCapabilities)
The
database_file_locationobject should be an implementation of theIDeviceDatabaseLocationmarker interface.-
close()¶ Close the datebase. It’s an error if the database is not open already.
-
open()¶ Open the database. If the file doesn’t exist, then a new database is created without a en empty root ConfigDomain in it. If the file does exist, then the file is loaded.
Return type: ConfigDomain object, which is the root domain in the tree.
-
3.2.6. Global Events¶
The definition of the IInstallationEvents interface.
-
interface
qct_interfaces.events.IDatabaseEvents[source]¶ Bases:
zope.interface.InterfaceThis utility defines a number of Signals that get fired when various global actions happen to device configuration databases. The standard usage within application code is something like this:
from zope.component import getUtility from qct_interfaces.events import IDatabaseEvents ... sig = getUtility(IDatabaseEvents).datebase_changed_signal()
and then the signal can be fired with:
sig.emit()
or connected to a callback function or method like this:
sig.connect(my_callback_func)
-
database_attribute_changed_signal()¶ Returns a signal that gets fired when a config attribute has been changed.
Returns: The QSignal that is fired. The data on the QSignal callback is the attribute that has been changed (an instance of ConfigAttributeBase).Return type: QSignal(attribute)
-
device_domain_selected_signal()¶ Returns a signal that gets fired when a domain item is selected in the Tree view of the database domains.
Returns: The QSignal that is fired. The data on the QSignal callback is the domain object (and instance of ConfigDomain).Return type: QSignal(domain)
-
3.2.7. User Interface¶
3.2.7.1. Themes¶
The definition of the ITheme interface.
-
interface
qct_interfaces.theme.ITheme[source]¶ This interface is used to abstract away how and from where theme information is loaded.
This interface is an adapter interface, and expects to be found by adapting an IApplicationOptions and an IPersistState instances. This allows ITheme implementations to choose from either the persisted state or from the command line options which theme to use.
Typical usage to get the real implementation of an ITheme interface will be done something like this:
from zope.component import getMultiAdapter theme = getMultiAdapter((getUtility(IApplicationOptions), getUtility(IPersistState)), ITheme)
-
stylesheet_location(area)¶ Return a string defining the location of a CSS file derived from the area name. This method will be used from WebView-based content, and therefore a path relative to the content itself needs to be returned. The caller needs to determine whether this file actually exists. The implementation of this object will simply apply the rules for its theming strategy for where its CSS files ought to be.
Returns: A string to the relative path of a CSS file.
-
get_stylesheet(area)¶ Return a string which is the theme information for a particular area. The areas available are not defined by this interface.
Implementation objects might, for example, implement this by looking for a file with the area name in particular folder.
Returns: A string of (usually) CSS-formatted data, or the empty string if no data is found for the specified area.
-
get_xml(name)¶ Some parts of the user interface are rendered using HTML markup. This method fetches markup for a particlarly named part of the user interface. This interface definition does not enumerate what these names are.
The returned XML can contain Kajiki markup so that parts of the XML can be replaced with context data.
Returns: The XML-compliant HTML markup.
-
3.2.7.2. Attribute Views¶
The definition of the IAttributeView interface.
-
interface
qct_interfaces.attributeviews.IAttributeView[source]¶ This adapter defines the interface to a view object that creates and manages the widgets that display and edit the attribute which are contained in a ConfigContainer.
The code that creates instances of this interface is done by runnng something like this:
from qct_interfaces import make_registered_named_instance from qct_interfaces.attributeviews import IAttributeView ... check_box_view = make_registered_named_instance(IAttributeView, 'view:qtcheckbox', config_attribute=attr) check_box_view.create()
Note the use of the
namein this call. The container implementation will look for the attribute view using the most specific to the least specific binding. This means that the code will look for an IAttributeView implmentation registered with the name:'item:' + attr.attr_key
first (e.g.
item:bluetooth_disable_role_switching), and if that doesn’t exist then:'class:' + attr.attr_type
next (e.g.
class:bool), and then finally with the view name defined in the attribute:'view:' + attr.attr_view
(e.g.
view:qtcheckbox).Plugins can choose to implement their own version of these implementations at whatever level they need to, and so change the presentation layer of the user interface.
-
create()¶ This method should construct the widget(s) required for displaying the attribute, which can be accessed by the
widgetattribute. The caller can then add the widget to the layout of the panel.
-
config_attribute= <zope.interface.interface.Attribute object>¶ The ConfigDomain attribute which the widget should be editing.
-
widget= <zope.interface.interface.Attribute object>¶ The top-level widget of this view. It is only valid after the
createmethod is called.
-
self_labelled= <zope.interface.interface.Attribute object>¶ Boolean value to indicate whether the view contructs its own label.
-
update()¶ This method updates the enabled state of the widget(s) associated with the view. This method gets called on all visible attributes whenever one of them is changed.
-