Source code for plugins
#
##
## SPDX-FileCopyrightText: © 2007-2024 Benedict Verhegghe <bverheg@gmail.com>
## SPDX-License-Identifier: GPL-3.0-or-later
##
## This file is part of pyFormex 3.5 (Thu Feb 8 19:11:13 CET 2024)
## pyFormex is a tool for generating, manipulating and transforming 3D
## geometrical models by sequences of mathematical operations.
## Home page: https://pyformex.org
## Project page: https://savannah.nongnu.org/projects/pyformex/
## Development: https://gitlab.com/bverheg/pyformex
## Distributed under the GNU General Public License version 3 or later.
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see http://www.gnu.org/licenses/.
##
"""pyFormex plugins initialisation.
This module contains the functions to detect and load the pyFormex
plugin menus.
"""
import sys
from types import ModuleType
import importlib
import pyformex as pf
[docs]class PluginMenu():
"""A registered reloadable plugin menu.
A plugin menu is a Python module living in an accessible package.
The default package is pyformex.plugins. The module should define
a function ``create_menu(before='help')``. This function should create
and return a :class:`Menu` created as follows::
menu.Menu(name, menu_items, parent=pf.GUI.menu, before=before)
Here ``name`` is the displayed menu name and menu_items is the list
of items in the menu. The menu should be a child of the main pyFormex
menu, and be inserted before the Help menu (or a preceding menu).
The plugin module can also define a function ``on_reload()``. If so,
this function will be called when the plugin is reloaded.
The plugin menu should then be registered by creating a PluginMenu
instance.
Parameters
----------
name: str
The name of the menu. This is the name as appearing in the menu bar.
This is normally a single short capitalized word. It should be
different from all other plugin menus (unless you want to override
another plugin with the same name).
modname: str, optional
The name of the module that contains the plugin menu.
The default value is the name converted to lower case and with
'_menu' appended.
package: str, optional
The dotted name of the package containing the plugin module.
The default is 'pyformex.plugins'.
"""
# The register is kept in order of registering
_register_ = {}
def __init__(self, name, modname=None, package='pyformex.plugins'):
self.name = name
self.modname = modname if modname else self.name.lower() + '_menu'
self.pkg = package
self.module = None # not loaded
self.__class__._register_[self.name] = self
@property
def fullname(self):
return f"{self.pkg}.{self.modname}"
@property
def nicename(self):
return self.name + ' Menu'
[docs] def load(self):
"""Load the plugin menu"""
if pf.debugon(pf.DEBUG.PLUGIN):
print(" Loading plugin menu:", self.fullname)
importlib.import_module(self.fullname)
module = self.module = sys.modules.get(self.fullname, None)
if isinstance(module, ModuleType) and hasattr(module, 'create_menu'):
self.show()
[docs] def refresh(self):
"""Reload the plugin module"""
importlib.import_module(self.fullname)
module = self.module
if isinstance(module, ModuleType) and hasattr(module, 'create_menu'):
importlib.reload(module)
else:
pf.error("No such module: %s" % self.fullname)
[docs] def show(self, before='help'):
"""Show the menu."""
if self.module and not pf.GUI.menu[self.name]:
self.menu = self.module.create_menu(before=before)
self.menu.insertItems([
("Reload Menu", self.reload),
("Close Menu", self.close),
])
[docs] def close(self):
"""Close the menu."""
if pf.debugon(pf.DEBUG.PLUGIN):
print(" Closing plugin menu:", self.name)
pf.GUI.menu.removeItem(self.name)
[docs] def reload(self):
"""Reload the menu."""
action = pf.GUI.menu.action(self.name)
before = pf.GUI.menu.nextItem(action)
self.close()
self.refresh()
try:
self.module.on_reload()
except Exception:
pass
self.show(before=before)
[docs] @classmethod
def list(clas):
"""Return a list of registered plugin menus.
Returns
-------
list of tuple
A list of tuples (name, nicename), where name is the
module name of the plugin and Nice Name is the beautified
displayed name for user readability. Thus 'geometry_menu'
can be displayed as 'Geometry Menu').
"""
return [(m.name, m.nicename) for m in clas._register_.values()]
# TODO: TEMPORARY
@classmethod
def get(clas, name):
return clas._register_.get(name, None)
# Register the pyFormex plugin menus
PluginMenu('Geometry')
PluginMenu('Mesh')
PluginMenu('Surface')
PluginMenu('Tools')
PluginMenu('Draw2d')
PluginMenu('Nurbs')
PluginMenu('Dxf')
PluginMenu('Bifmesh')
PluginMenu('Jobs')
PluginMenu('Postproc')
[docs]def loadConfiguredPlugins(ok_plugins=None):
"""Load or unload plugin menus.
Loads the specified plugins and unloads all others. If None
specified, load the user configured plugins.
"""
if ok_plugins is None:
ok_plugins = pf.cfg['gui/pluginmenus']
if pf.debugon(pf.DEBUG.PLUGIN):
print("Configured plugins:", ok_plugins)
for p in PluginMenu._register_.values():
if pf.debugon(pf.DEBUG.PLUGIN):
print("Plugin menu:", p.name)
if p.name in ok_plugins:
p.load()
else:
p.close()
# End