blext/examples/simple/blext_simple_example/preferences.py

186 lines
5.0 KiB
Python

"""Addon preferences, encapsulating the various global modifications that the user may make to how this addon functions."""
import logging
from pathlib import Path
import bpy
from .services.init_settings import INIT_SETTINGS
from .utils import logger
log = logger.get(__name__)
####################
# - Constants
####################
LOG_LEVEL_MAP: dict[str, logger.LogLevel] = {
'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARNING': logging.WARNING,
'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL,
}
####################
# - Preferences
####################
class SimpleAddonPrefs(bpy.types.AddonPreferences):
"""Manages user preferences and settings."""
bl_idname = __package__
####################
# - Properties
####################
# Logging
## File Logging
use_log_file: bpy.props.BoolProperty( # type: ignore
name='Log to File',
description='Whether to use a file for addon logging',
default=INIT_SETTINGS.use_log_file,
update=lambda self, _: self.on_addon_logging_changed(),
)
log_level_file: bpy.props.EnumProperty( # type: ignore
name='File Log Level',
description='Level of addon logging to expose in the file',
items=[
('DEBUG', 'Debug', 'Debug'),
('INFO', 'Info', 'Info'),
('WARNING', 'Warning', 'Warning'),
('ERROR', 'Error', 'Error'),
('CRITICAL', 'Critical', 'Critical'),
],
default=INIT_SETTINGS.log_file_level,
update=lambda self, _: self.on_addon_logging_changed(),
)
bl__log_file_path: bpy.props.StringProperty( # type: ignore
name='Log Path',
description='Path to the Addon Log File',
subtype='FILE_PATH',
default=str(INIT_SETTINGS.log_file_path),
update=lambda self, _: self.on_addon_logging_changed(),
)
@property
def log_file_path(self) -> Path:
"""Retrieve the configured file-logging path as a `pathlib.Path`."""
return Path(bpy.path.abspath(self.bl__log_file_path))
@log_file_path.setter
def log_file_path(self, path: Path) -> None:
"""Set the configured file-logging path as a `pathlib.Path`."""
self.bl__log_file_path = str(path.resolve())
## Console Logging
use_log_console: bpy.props.BoolProperty( # type: ignore
name='Log to Console',
description='Whether to use the console for addon logging',
default=INIT_SETTINGS.use_log_console,
update=lambda self, _: self.on_addon_logging_changed(),
)
log_level_console: bpy.props.EnumProperty( # type: ignore
name='Console Log Level',
description='Level of addon logging to expose in the console',
items=[
('DEBUG', 'Debug', 'Debug'),
('INFO', 'Info', 'Info'),
('WARNING', 'Warning', 'Warning'),
('ERROR', 'Error', 'Error'),
('CRITICAL', 'Critical', 'Critical'),
],
default=INIT_SETTINGS.log_console_level,
update=lambda self, _: self.on_addon_logging_changed(),
)
####################
# - Events: Properties Changed
####################
def setup_logger(self, _logger: logging.Logger) -> None:
"""Setup a logger using the settings declared in the addon preferences.
Args:
_logger: The logger to configure using settings in the addon preferences.
"""
logger.update_logger(
logger.console_handler,
logger.file_handler,
_logger,
file_path=self.log_file_path if self.use_log_file else None,
file_level=LOG_LEVEL_MAP[self.log_level_file],
console_level=LOG_LEVEL_MAP[self.log_level_console]
if self.use_log_console
else None,
)
def on_addon_logging_changed(self) -> None:
"""Called to reconfigure all loggers to match newly-altered addon preferences.
This causes ex. changes to desired console log level to immediately be applied, but only the this addon's loggers.
Parameters:
single_logger_to_setup: When set, only this logger will be setup.
Otherwise, **all addon loggers will be setup**.
"""
log.info('Reconfiguring Loggers')
for _logger in logger.all_addon_loggers():
self.setup_logger(_logger)
log.info('Loggers Reconfigured')
####################
# - UI
####################
def draw(self, _: bpy.types.Context) -> None:
"""Draw the addon preferences within its panel in Blender's preferences.
Notes:
Run by Blender when this addon's preferences need to be displayed.
Parameters:
context: The Blender context object.
"""
layout = self.layout
####################
# - Logging
####################
# Box w/Split: Log Level
box = layout.box()
row = box.row()
row.alignment = 'CENTER'
row.label(text='Logging')
split = box.split(factor=0.5)
## Split Col: Console Logging
col = split.column()
row = col.row()
row.prop(self, 'use_log_console', toggle=True)
row = col.row()
row.enabled = self.use_log_console
row.prop(self, 'log_level_console')
## Split Col: File Logging
col = split.column()
row = col.row()
row.prop(self, 'use_log_file', toggle=True)
row = col.row()
row.enabled = self.use_log_file
row.prop(self, 'bl__log_file_path')
row = col.row()
row.enabled = self.use_log_file
row.prop(self, 'log_level_file')
####################
# - Blender Registration
####################
BL_REGISTER = [
SimpleAddonPrefs,
]