Example: Gaphor services#

In this example we’re doing something a little less trivial. In Gaphor, services are defined as entry points. Each service is a class, and takes parameters with names that match other services. This allows services to depend on other services.

It looks something like this:

# entry point name: my_service
class MyService:
    ...

# entry point name: my_other_service
class MyOtherService:
    def __init__(self, my_service):
        ...

Let’s first load the entry points.

from sphinx.ext.autodoc.mock import mock
from gaphor.entrypoint import load_entry_points


with mock(["gi.repository.Gtk", "gi.repository.Gdk", "gi.repository.GdkPixbuf", "gi.repository.GtkSource", "gi.repository.Adw"]):
    entry_points = load_entry_points("gaphor.services")

entry_points
No translations were found for language C.UTF-8: [Errno 2] No translation file found for domain: 'gaphor'
{'auto_layout': gaphor.plugins.autolayout.pydot.AutoLayoutService,
 'component_registry': gaphor.services.componentregistry.ComponentRegistry,
 'consolewindow': gaphor.plugins.console.consolewindow.ConsoleWindow,
 'copy': gaphor.services.copyservice.CopyService,
 'diagram_export': gaphor.plugins.diagramexport.DiagramExport,
 'diagrams': gaphor.ui.diagrams.Diagrams,
 'element_dispatcher': gaphor.core.modeling.elementdispatcher.ElementDispatcher,
 'element_factory': gaphor.core.modeling.elementfactory.ElementFactory,
 'elementeditor': gaphor.ui.elementeditor.ElementEditor,
 'event_manager': gaphor.core.eventmanager.EventManager,
 'export_menu': gaphor.ui.menufragment.MenuFragment,
 'file_manager': gaphor.ui.filemanager.FileManager,
 'main_window': gaphor.ui.mainwindow.MainWindow,
 'model_browser': gaphor.ui.treecomponent.TreeComponent,
 'modeling_language': gaphor.services.modelinglanguage.ModelingLanguageService,
 'properties': gaphor.services.properties.Properties,
 'recent_files': gaphor.ui.recentfiles.RecentFiles,
 'sanitizer': gaphor.UML.sanitizerservice.SanitizerService,
 'toolbox': gaphor.ui.toolbox.Toolbox,
 'tools_menu': gaphor.ui.menufragment.MenuFragment,
 'undo_manager': gaphor.services.undomanager.UndoManager,
 'xmi_export': gaphor.plugins.xmiexport.XMIExport}

Now let’s create a component in our model for every service.

from gaphor import UML
from gaphor.core.modeling import ElementFactory

element_factory = ElementFactory()

def create_component(name):
    c = element_factory.create(UML.Component)
    c.name = name
    return c

components = {name: create_component(name) for name in entry_points}
components
{'auto_layout': <gaphor.UML.uml.Component element 5bb16414-a2d9-11ed-8b6a-0242ac110002>,
 'component_registry': <gaphor.UML.uml.Component element 5bb16c52-a2d9-11ed-8b6a-0242ac110002>,
 'consolewindow': <gaphor.UML.uml.Component element 5bb16dec-a2d9-11ed-8b6a-0242ac110002>,
 'copy': <gaphor.UML.uml.Component element 5bb16f5e-a2d9-11ed-8b6a-0242ac110002>,
 'diagram_export': <gaphor.UML.uml.Component element 5bb1709e-a2d9-11ed-8b6a-0242ac110002>,
 'diagrams': <gaphor.UML.uml.Component element 5bb171c0-a2d9-11ed-8b6a-0242ac110002>,
 'element_dispatcher': <gaphor.UML.uml.Component element 5bb172d8-a2d9-11ed-8b6a-0242ac110002>,
 'element_factory': <gaphor.UML.uml.Component element 5bb17418-a2d9-11ed-8b6a-0242ac110002>,
 'elementeditor': <gaphor.UML.uml.Component element 5bb17576-a2d9-11ed-8b6a-0242ac110002>,
 'event_manager': <gaphor.UML.uml.Component element 5bb1768e-a2d9-11ed-8b6a-0242ac110002>,
 'export_menu': <gaphor.UML.uml.Component element 5bb17788-a2d9-11ed-8b6a-0242ac110002>,
 'file_manager': <gaphor.UML.uml.Component element 5bb17882-a2d9-11ed-8b6a-0242ac110002>,
 'main_window': <gaphor.UML.uml.Component element 5bb17972-a2d9-11ed-8b6a-0242ac110002>,
 'model_browser': <gaphor.UML.uml.Component element 5bb17a58-a2d9-11ed-8b6a-0242ac110002>,
 'modeling_language': <gaphor.UML.uml.Component element 5bb17b70-a2d9-11ed-8b6a-0242ac110002>,
 'properties': <gaphor.UML.uml.Component element 5bb17c56-a2d9-11ed-8b6a-0242ac110002>,
 'recent_files': <gaphor.UML.uml.Component element 5bb17d46-a2d9-11ed-8b6a-0242ac110002>,
 'sanitizer': <gaphor.UML.uml.Component element 5bb17e22-a2d9-11ed-8b6a-0242ac110002>,
 'toolbox': <gaphor.UML.uml.Component element 5bb17f08-a2d9-11ed-8b6a-0242ac110002>,
 'tools_menu': <gaphor.UML.uml.Component element 5bb17fee-a2d9-11ed-8b6a-0242ac110002>,
 'undo_manager': <gaphor.UML.uml.Component element 5bb180d4-a2d9-11ed-8b6a-0242ac110002>,
 'xmi_export': <gaphor.UML.uml.Component element 5bb181ba-a2d9-11ed-8b6a-0242ac110002>}

With all components mapped, we can create dependencies between those components, based on the constructor parameter names.

import inspect

for name, cls in entry_points.items():
    for param_name in inspect.signature(cls).parameters:
        if param_name not in components:
            continue

        dep = element_factory.create(UML.Usage)
        dep.client = components[name]
        dep.supplier = components[param_name]

With all elements in the model, we can create a diagram. Let’s drop the components and dependencies on the diagram and let auto-layout do its magic.

To make the dependency look good, we have to add a style sheet. If you create a new diagram via the GUI, this element is automatically added.

from gaphor.core.modeling import Diagram, StyleSheet
from gaphor.diagram.drop import drop

element_factory.create(StyleSheet)
diagram = element_factory.create(Diagram)

for element in element_factory.lselect():
    drop(element, diagram, x=0, y=0)

Last step is to layout and draw the diagram.

from gaphor.extensions.ipython import auto_layout, draw

auto_layout(diagram)

draw(diagram, format="svg")
_images/4a0d8ecb6c7f825df3692df91b812b8140e5a79e22300b409cf4d31997527abc.svg

That’s all. As you can see from the diagram, a lot of services rely on EventManager.