Приклад: послуги Gaphor

У цьому прикладі ми робимо дещо менш тривіальне. У Gaphor сервіси визначаються як [точки входу] (https://packaging.python.org/en/latest/specifications/entry-points/). Кожен сервіс є класом і отримує параметри з іменами, які збігаються з іменами інших сервісів. Це дозволяє сервісам залежати від інших сервісів.

Виглядає це приблизно так:

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

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

Давайте спочатку завантажимо точки входу.

from gaphor.entrypoint import load_entry_points

entry_points = load_entry_points("gaphor.services")

entry_points
{'auto_layout': gaphor.plugins.autolayout.pydot.AutoLayoutService,
 'component_registry': gaphor.services.componentregistry.ComponentRegistry,
 'console_window': gaphor.plugins.console.consolewindow.ConsoleWindow,
 'diagram_align': gaphor.ui.diagramalign.DiagramAlign,
 'diagram_export': gaphor.plugins.diagramexport.export.DiagramExport,
 'diagrams': gaphor.ui.diagrams.Diagrams,
 'element_dispatcher': gaphor.core.modeling.elementdispatcher.ElementDispatcher,
 'element_editor': gaphor.ui.elementeditor.ElementEditor,
 'element_factory': gaphor.core.modeling.elementfactory.ElementFactory,
 'event_manager': gaphor.core.eventmanager.EventManager,
 'export_menu': gaphor.ui.menufragment.MenuFragment,
 'file_manager': gaphor.ui.filemanager.FileManager,
 'html_report': gaphor.plugins.htmlreport.report.HtmlReportExport,
 'main_window': gaphor.ui.mainwindow.MainWindow,
 'model_browser': gaphor.ui.modelbrowser.ModelBrowser,
 'model_changed': gaphor.ui.modelchanged.ModelChanged,
 'modeling_language': gaphor.services.modelinglanguage.ModelingLanguageService,
 'properties': gaphor.services.properties.Properties,
 'recent_files': gaphor.ui.recentfiles.RecentFiles,
 'recovery': gaphor.storage.recovery.Recovery,
 'sanitizer': gaphor.UML.sanitizerservice.SanitizerService,
 'toolbox': gaphor.ui.toolbox.Toolbox,
 'tools_menu': gaphor.ui.menufragment.MenuFragment,
 'undo_actions': gaphor.ui.undoactions.UndoActions,
 'undo_manager': gaphor.services.undomanager.UndoManager}

Тепер давайте створимо компонент у нашій моделі для кожної служби.

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 f8205170-6b85-11f1-b7e8-bad58c1a6489>,
 'component_registry': <gaphor.UML.uml.Component element f82055ee-6b85-11f1-b7e8-bad58c1a6489>,
 'console_window': <gaphor.UML.uml.Component element f82057ce-6b85-11f1-b7e8-bad58c1a6489>,
 'diagram_align': <gaphor.UML.uml.Component element f820592c-6b85-11f1-b7e8-bad58c1a6489>,
 'diagram_export': <gaphor.UML.uml.Component element f8205a9e-6b85-11f1-b7e8-bad58c1a6489>,
 'diagrams': <gaphor.UML.uml.Component element f8205bca-6b85-11f1-b7e8-bad58c1a6489>,
 'element_dispatcher': <gaphor.UML.uml.Component element f8205ce2-6b85-11f1-b7e8-bad58c1a6489>,
 'element_editor': <gaphor.UML.uml.Component element f8205df0-6b85-11f1-b7e8-bad58c1a6489>,
 'element_factory': <gaphor.UML.uml.Component element f8205ef4-6b85-11f1-b7e8-bad58c1a6489>,
 'event_manager': <gaphor.UML.uml.Component element f8205fee-6b85-11f1-b7e8-bad58c1a6489>,
 'export_menu': <gaphor.UML.uml.Component element f82060e8-6b85-11f1-b7e8-bad58c1a6489>,
 'file_manager': <gaphor.UML.uml.Component element f82061e2-6b85-11f1-b7e8-bad58c1a6489>,
 'html_report': <gaphor.UML.uml.Component element f82062dc-6b85-11f1-b7e8-bad58c1a6489>,
 'main_window': <gaphor.UML.uml.Component element f82063d6-6b85-11f1-b7e8-bad58c1a6489>,
 'model_browser': <gaphor.UML.uml.Component element f82064c6-6b85-11f1-b7e8-bad58c1a6489>,
 'model_changed': <gaphor.UML.uml.Component element f82065b6-6b85-11f1-b7e8-bad58c1a6489>,
 'modeling_language': <gaphor.UML.uml.Component element f820669c-6b85-11f1-b7e8-bad58c1a6489>,
 'properties': <gaphor.UML.uml.Component element f8206782-6b85-11f1-b7e8-bad58c1a6489>,
 'recent_files': <gaphor.UML.uml.Component element f8206872-6b85-11f1-b7e8-bad58c1a6489>,
 'recovery': <gaphor.UML.uml.Component element f8206994-6b85-11f1-b7e8-bad58c1a6489>,
 'sanitizer': <gaphor.UML.uml.Component element f8206c64-6b85-11f1-b7e8-bad58c1a6489>,
 'toolbox': <gaphor.UML.uml.Component element f8206d9a-6b85-11f1-b7e8-bad58c1a6489>,
 'tools_menu': <gaphor.UML.uml.Component element f8206ed0-6b85-11f1-b7e8-bad58c1a6489>,
 'undo_actions': <gaphor.UML.uml.Component element f8206fd4-6b85-11f1-b7e8-bad58c1a6489>,
 'undo_manager': <gaphor.UML.uml.Component element f82070ce-6b85-11f1-b7e8-bad58c1a6489>}

Коли всі компоненти зіставлені, ми можемо створювати залежності між цими компонентами на основі імен параметрів конструктора.

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]

З усіма елементами моделі ми можемо створити діаграму. Давайте відкинемо компоненти та залежності на діаграмі та дозволимо автоматичному компонуванню зробити свою магію.

Щоб залежність виглядала добре, нам потрібно додати таблицю стилів. Якщо ви створюєте нову діаграму через GUI, цей елемент додається автоматично.

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

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

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

Останнім кроком є макет і малювання діаграми.

from gaphor.extensions.ipython import auto_layout, draw

auto_layout(diagram)

draw(diagram, format="svg")
_images/38a427e1e1757886d8dbe68601f33791443b66d62cbd65d377dfaddfc73692b9.svg

Ось і все. Як ви можете бачити на діаграмі, багато служб покладаються на EventManager.