diff --git a/.flake8 b/.flake8 deleted file mode 100644 index a390455..0000000 --- a/.flake8 +++ /dev/null @@ -1,10 +0,0 @@ -[flake8] -max-line-length = 120 -ignore = E203, E501, W503, F722, F821 # Incompatible with black, F722, F821 because of bug in Flake with names and strings - -exclude = - .git - .history - .venv - .vscode - __pycache__ diff --git a/.gitignore b/.gitignore index 74ac996..6bb9bd6 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ _PackagesForInstall .venv # VS Code -.vscode +# .vscode .vscode/settings.json .local diff --git a/.vscode/settings.json b/.vscode/settings.json index de153a1..04cb4ff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,7 +16,11 @@ "python.testing.unittestEnabled": false, "python.linting.pylintEnabled": false, "python.linting.flake8Enabled": true, - "python.linting.flake8Args": [], + "python.linting.flake8Args": [ + "--max-line-length=120", + "--ignore=E402,E203,E501,W503,F722,F821", + "--exclude=.git,.history,.venv,.vscode,__pycache__" + ], "python.linting.enabled": true, "gitlab.instanceUrl": "https://gitlab-ncsa.ubisoft.org/", "blender.addon.sourceDirectory": "./stampinfo", @@ -25,11 +29,15 @@ "python.testing.nosetestsEnabled": false, "restructuredtext.confPath": "${​​​​​​​​workspaceFolder}​​​​​​​​\\docs", "spellright.language": [ - "en" + "en", + "fr" ], "spellright.documentTypes": [ "markdown", "latex", "plaintext" - ] + ], + "blender.addon.sourceDirectory": "./stampinfo", + "restructuredtext.confPath": "${​​​​​​​​workspaceFolder}​​​​​​​​\\docs", + "esbonio.sphinx.confDir": "" } \ No newline at end of file diff --git a/.vscode/settings.shared.json b/.vscode/settings.shared.json new file mode 100644 index 0000000..1568d91 --- /dev/null +++ b/.vscode/settings.shared.json @@ -0,0 +1,42 @@ +{ + "editor.formatOnSave": true, + "python.pythonPath": ".venv\\Scripts\\python.exe", + "python.formatting.provider": "black", + "python.formatting.blackArgs": [ + "--line-length", + "120" + ], + "python.testing.unittestArgs": [ + "-v", + "-s", + "./tests", + "-p", + "test_*.py" + ], + "python.testing.unittestEnabled": false, + "python.linting.pylintEnabled": false, + "python.linting.flake8Enabled": true, + "python.linting.flake8Args": [ + "--max-line-length=120", + "--ignore=E402,E203,E501,W503,F722,F821", + "--exclude=.git,.history,.venv,.vscode,__pycache__" + ], + "python.linting.enabled": true, + "gitlab.instanceUrl": "https://gitlab-ncsa.ubisoft.org/", + "blender.addon.sourceDirectory": "./stampinfo", + "python.testing.promptToConfigure": false, + "python.testing.pytestEnabled": false, + "python.testing.nosetestsEnabled": false, + "restructuredtext.confPath": "${​​​​​​​​workspaceFolder}​​​​​​​​\\docs", + "spellright.language": [ + "en", + "fr" + ], + "spellright.documentTypes": [ + "markdown", + "latex", + "plaintext" + ], + "blender.addon.sourceDirectory": "./stampinfo", + "restructuredtext.confPath": "${​​​​​​​​workspaceFolder}​​​​​​​​\\docs" +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 649f7f4..bb2c533 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ +# 1.1.1 (2022-05-11) + +## Code + +- Code cleaning and updated to match the implementation of Ubisoft Shot Manager add-on +- Added custom class of Logger +- changed the global debug variable gbWkDebug to a variable devDebug stored in config.py # 1.0.17 (2022-01-15) -evaluateRenderResolutionForStampInfo + +- Added function evaluateRenderResolutionForStampInfo + # 1.0.15 (2021-11-02) @@ -10,7 +19,6 @@ evaluateRenderResolutionForStampInfo - Added a text field for Sequence - Set date and time on by default -

# 1.0.14 (2021-10-29) @@ -19,8 +27,6 @@ evaluateRenderResolutionForStampInfo - Logos built-in path was broken -

- # 1.0.13 (2021-09-28) ## Installation @@ -28,8 +34,6 @@ evaluateRenderResolutionForStampInfo - Improved again error catching for better user feedback when not in admin mode -

- # 1.0.12 (2021-09-23) ## Installation @@ -37,8 +41,6 @@ evaluateRenderResolutionForStampInfo - Improved error catching with pip download timeout -

- # 1.0.11 (2021-09-23) ## Documentation @@ -50,8 +52,6 @@ evaluateRenderResolutionForStampInfo - Provided better user feedback at install time in case of errors -

- # 1.0.9 (2021-08-20) ## UI @@ -63,16 +63,12 @@ evaluateRenderResolutionForStampInfo - Fix: Frame ranges are now disabled again when unchecked in the UI -

- # 1.0.8 (05/07/2021) - Rewamped UI - Improved documentation -

- # 1.0.1 (2021-06-12) - Removed handlers and use of the compositing editor that were used to generate the final images @@ -80,8 +76,6 @@ because code was unstable and complicated - Added 2 Render buttons and a batch based on the VSE to create the final images -

- -------- # 0.9.x - Production versions diff --git a/requirements-dev.txt b/requirements-dev.txt index c53d2f6..1eb6c7d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,18 +1,45 @@ +alabaster==0.7.12 appdirs==1.4.4 -attrs==19.3.0 -black==19.10b0 -click==7.1.2 +attrs==21.4.0 +Babel==2.10.1 +black==22.3.0 +certifi==2021.10.8 +charset-normalizer==2.0.12 +click==8.1.3 +colorama==0.4.4 +docutils==0.17.1 fake-bpy-module-2.82==20200514 -flake8==3.8.1 -flake8-black==0.1.2 -flake8-bugbear==20.1.4 -importlib-metadata==1.6.0 +flake8==4.0.1 +flake8-black==0.3.2 +flake8-bugbear==22.4.25 +idna==3.3 +imagesize==1.3.0 +importlib-metadata==4.11.3 +Jinja2==3.1.2 +MarkupSafe==2.1.1 mccabe==0.6.1 -pathspec==0.8.0 -pycodestyle==2.6.0 -pyflakes==2.2.0 -regex==2020.5.14 -toml==0.10.1 -typed-ast==1.4.1 -typing==3.7.4.1 -zipp==3.1.0 +mypy-extensions==0.4.3 +packaging==21.3 +pathspec==0.9.0 +platformdirs==2.5.2 +pycodestyle==2.8.0 +pyflakes==2.4.0 +Pygments==2.12.0 +pyparsing==3.0.9 +pytz==2022.1 +regex==2022.4.24 +requests==2.27.1 +snowballstemmer==2.2.0 +Sphinx==4.5.0 +sphinxcontrib-applehelp==1.0.2 +sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-htmlhelp==2.0.0 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-serializinghtml==1.1.5 +toml==0.10.2 +tomli==2.0.1 +typed-ast==1.5.3 +typing==3.7.4.3 +urllib3==1.26.9 +zipp==3.8.0 diff --git a/stampinfo/__init__.py b/stampinfo/__init__.py index ae94c56..0fdcfcf 100644 --- a/stampinfo/__init__.py +++ b/stampinfo/__init__.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -18,14 +18,13 @@ """ Main init """ -import logging -import os -from pathlib import Path +# import os +# from pathlib import Path import bpy import bpy.utils.previews -from bpy.props import PointerProperty +from bpy.props import IntProperty, PointerProperty from .config import config @@ -43,14 +42,18 @@ importlib.reload(stamper) importlib.reload(debug) +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + bl_info = { "name": "Stamp Info", "author": "Julien Blervaque (aka Werwack) - Ubisoft", "description": "Stamp scene information on the rendered images", - "blender": (3, 0, 0), - "version": (1, 0, 17), - "location": "Right panel in the 3D View", + "blender": (3, 1, 0), + "version": (1, 1, 1), + "location": "View3D > Stamp Info", "wiki_url": "https://ubisoft-stampinfo.readthedocs.io", "tracker_url": "https://github.com/ubisoft/stampinfo/issues", "support": "COMMUNITY", @@ -65,36 +68,38 @@ # Logging ########### -_logger = logging.getLogger(__name__) -_logger.propagate = False -MODULE_PATH = Path(__file__).parent.parent -logging.basicConfig(level=logging.INFO) -_logger.setLevel(logging.DEBUG) # CRITICAL ERROR WARNING INFO DEBUG NOTSET +# _logger = sm_logging.getLogger(__name__) +# _logger.propagate = False +# MODULE_PATH = Path(__file__).parent.parent +# logging.basicConfig(level=logging.INFO) +# _logger.setLevel(logging.DEBUG) # CRITICAL ERROR WARNING INFO DEBUG NOTSET + +import logging pil_logger = logging.getLogger("PIL") pil_logger.setLevel(logging.INFO) -# _logger.info(f"Logger {}") -# _logger.warning(f"logger {}") -# _logger.error(f"error {}") -# _logger.debug(f"debug {}") +# # _logger.info(f"Logger {}") +# # _logger.warning(f"logger {}") +# # _logger.error(f"error {}") +# # _logger.debug(f"debug {}") -class Formatter(logging.Formatter): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) +# class Formatter(logging.Formatter): +# def __init__(self, *args, **kwargs): +# super().__init__(*args, **kwargs) - def format(self, record: logging.LogRecord): - """ - The role of this custom formatter is: - - append filepath and lineno to logging format but shorten path to files, to make logs more clear - - to append "./" at the begining to permit going to the line quickly with VS Code CTRL+click from terminal - """ - s = super().format(record) - # s = record - pathname = Path(record.pathname).relative_to(MODULE_PATH) - s += f" [{os.curdir}{os.sep}{pathname}:{record.lineno}]" - return s +# def format(self, record: logging.LogRecord): +# """ +# The role of this custom formatter is: +# - append filepath and lineno to logging format but shorten path to files, to make logs more clear +# - to append "./" at the begining to permit going to the line quickly with VS Code CTRL+click from terminal +# """ +# s = super().format(record) +# # s = record +# pathname = Path(record.pathname).relative_to(MODULE_PATH) +# s += f" [{os.curdir}{os.sep}{pathname}:{record.lineno}]" +# return s classes = ( @@ -114,20 +119,29 @@ def stampInfo_resetProperties(): def register(): + + config.initGlobalVariables() + from .utils import utils_ui utils_ui.register() utils.display_addon_registered_version("Stamp Info") - config.initGlobalVariables() - # logging - ################### - if len(_logger.handlers) == 0: - _logger.setLevel(logging.WARNING) - formatter = Formatter("{asctime} {levelname[0]} {name:<36} - {message:<80}", style="{") - handler = logging.StreamHandler() - handler.setFormatter(formatter) - _logger.addHandler(handler) + sm_logging.initialize() + if config.devDebug: + _logger.setLevel("DEBUG") # CRITICAL ERROR WARNING INFO DEBUG NOTSET + + logger_level = f"Logger level: {sm_logging.getLevelName()}" + versionTupple = utils.display_addon_registered_version("Stamp Info", more_info=logger_level) + + # # logging + # ################### + # if len(_logger.handlers) == 0: + # _logger.setLevel(logging.WARNING) + # formatter = Formatter("{asctime} {levelname[0]} {name:<36} - {message:<80}", style="{") + # handler = logging.StreamHandler() + # handler.setFormatter(formatter) + # _logger.addHandler(handler) # install dependencies and required Python libraries ################### @@ -149,6 +163,14 @@ def register(): from .addon_prefs import addon_prefs from .operators import render_operators + ################### + # update data + ################### + + bpy.types.WindowManager.UAS_stamp_info_version = IntProperty( + name="Add-on Version Int", description="Add-on version as integer", default=versionTupple[1] + ) + for cls in classes: bpy.utils.register_class(cls) @@ -201,6 +223,8 @@ def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls) + del bpy.types.WindowManager.UAS_stamp_info_version + utils_ui.unregister() config.releaseGlobalVariables() diff --git a/stampinfo/addon_prefs/addon_prefs.py b/stampinfo/addon_prefs/addon_prefs.py index 18c1d62..3888bb8 100644 --- a/stampinfo/addon_prefs/addon_prefs.py +++ b/stampinfo/addon_prefs/addon_prefs.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -21,24 +21,55 @@ import bpy from bpy.types import AddonPreferences -from bpy.props import BoolProperty +from bpy.props import IntProperty, BoolProperty + +from .addon_prefs_ui import draw_addon_prefs + +from stampinfo.utils import utils from ..config import config from ..ui.dependencies_ui import drawDependencies +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + class UAS_StampInfo_AddonPrefs(AddonPreferences): """ - Use this to get these prefs: - prefs = context.preferences.addons["stampinfo"].preferences + Use this to get these prefs: + prefs = context.preferences.addons["stampinfo"].preferences """ # this must match the add-on name, use '__package__' # when defining this in a submodule of a python package bl_idname = "stampinfo" + def version(self): + """Return the add-on version in the form of a tupple made by: + - a string x.y.z (eg: "1.21.3") + - an integer. x.y.z becomes xxyyyzzz (eg: "1.21.3" becomes 1021003) + Return None if the addon has not been found + """ + return utils.addonVersion("Stamp Info") + + newAvailableVersion: IntProperty( + description="Store the version of the latest release of the add-on as an integer if there is an online release" + "\nthat is more recent than this version. If there is none then the value is 0", + # default=2005001, + default=1007016, + ) + + isInitialized: BoolProperty( + default=False, + ) + + def initialize_stamp_info_prefs(self): + print("\nInitializing Stamp Info Preferences...") + install_failed: BoolProperty( - name="Install failed", default=False, + name="Install failed", + default=False, ) mediaFirstFrameIsZero: BoolProperty( @@ -72,62 +103,18 @@ class UAS_StampInfo_AddonPrefs(AddonPreferences): options=set(), ) + ################################################################################## + # Draw + ################################################################################## def draw(self, context): - layout = self.layout - splitFactor = 0.3 - - box = layout.box() - row = box.row() - row.separator(factor=3) - subCol = row.column() - subCol.prop(self, "mediaFirstFrameIsZero") - subCol.prop(self, "write_still") - - layout.separator(factor=0.5) - layout.label(text="Technical Settings:") - box = layout.box() - box.label(text="Stamped Images Compositing:") - row = box.row() - row.separator(factor=3) - subCol = row.column() - subCol.prop(self, "delete_temp_scene") - subCol.prop(self, "delete_temp_images") - - # Dependencies - ############### - drawDependencies(context, layout) - - # Dev and debug - ############### - box = layout.box() - - split = box.split(factor=splitFactor) - rowLeft = split.row() - rowLeft.label(text="Development and Debug:") - rowRight = split.row() - - if config.devDebug: - strDebug = " *** Debug Mode is On ***" - rowRight.alignment = "RIGHT" - rowRight.alert = True - rowRight.label(text=strDebug) - - split = box.split(factor=splitFactor) - rowLeft = split.row() - rowLeft.alignment = "RIGHT" - rowLeft.label(text="Debug Mode") - rowRight = split.row() - if config.devDebug: - rowRight.alert = True - rowRight.operator("uas_stamp_info.enable_debug", text="Turn Off").enable_debug = False - else: - rowRight.operator("uas_stamp_info.enable_debug", text="Turn On").enable_debug = True + draw_addon_prefs(self, context) # ----------------------------------------------------------- # UI user preferences - Not exposed # ----------------------------------------------------------- panelExpanded_mode: BoolProperty( - name="Expand Render Mode Properties", default=True, + name="Expand Render Mode Properties", + default=True, ) @@ -135,11 +122,14 @@ def draw(self, context): def register(): + _logger.debug_ext(" - Registering Add-on Prefs Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) def unregister(): + _logger.debug_ext(" - Unregistering Add-on Prefs Package", form="UNREG") + for cls in reversed(_classes): bpy.utils.unregister_class(cls) - diff --git a/stampinfo/addon_prefs/addon_prefs_ui.py b/stampinfo/addon_prefs/addon_prefs_ui.py new file mode 100644 index 0000000..83cf72e --- /dev/null +++ b/stampinfo/addon_prefs/addon_prefs_ui.py @@ -0,0 +1,82 @@ +# GPLv3 License +# +# Copyright (C) 2021 Ubisoft +# +# 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 2 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 . + +""" +UI for the Add-on Preferences +""" + +from stampinfo.config import config +from stampinfo.ui.dependencies_ui import drawDependencies +from stampinfo.utils.utils_ui import collapsable_panel + +################################################################################## +# Draw +################################################################################## + + +def draw_addon_prefs(self, context): + layout = self.layout + # layout = layout.column(align=False) + + splitFactor = 0.3 + + box = layout.box() + row = box.row() + row.separator(factor=3) + subCol = row.column() + subCol.prop(self, "mediaFirstFrameIsZero") + subCol.prop(self, "write_still") + + layout.separator(factor=0.5) + layout.label(text="Technical Settings:") + box = layout.box() + box.label(text="Stamped Images Compositing:") + row = box.row() + row.separator(factor=3) + subCol = row.column() + subCol.prop(self, "delete_temp_scene") + subCol.prop(self, "delete_temp_images") + + # Dependencies + ############### + drawDependencies(context, layout) + + # Dev and debug + ############### + box = layout.box() + + split = box.split(factor=splitFactor) + rowLeft = split.row() + rowLeft.label(text="Development and Debug:") + rowRight = split.row() + + if config.devDebug: + strDebug = " *** Debug Mode is On ***" + rowRight.alignment = "RIGHT" + rowRight.alert = True + rowRight.label(text=strDebug) + + split = box.split(factor=splitFactor) + rowLeft = split.row() + rowLeft.alignment = "RIGHT" + rowLeft.label(text="Debug Mode") + rowRight = split.row() + if config.devDebug: + rowRight.alert = True + rowRight.operator("uas_stamp_info.enable_debug", text="Turn Off").enable_debug = False + else: + rowRight.operator("uas_stamp_info.enable_debug", text="Turn On").enable_debug = True diff --git a/stampinfo/config/config.py b/stampinfo/config/config.py index d8f2754..e64c3f8 100644 --- a/stampinfo/config/config.py +++ b/stampinfo/config/config.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -33,8 +33,8 @@ def initGlobalVariables(): else: devDebug = False - # force debug mode - # devDebug = False + # change this value to force debug at start time + devDebug = True global devDebug_keepVSEContent devDebug_keepVSEContent = True and devDebug @@ -42,7 +42,19 @@ def initGlobalVariables(): if devDebug: print("Dev debug: ", devDebug) + global devDebug_ignoreLoggerFormatting + devDebug_ignoreLoggerFormatting = True and devDebug + def releaseGlobalVariables(): pass + + +def getLoggingTags(): + tags = dict() + + # debug tags + tags["DEPRECATED"] = False + + return tags diff --git a/stampinfo/config/sm_logging.py b/stampinfo/config/sm_logging.py new file mode 100644 index 0000000..29d4f8e --- /dev/null +++ b/stampinfo/config/sm_logging.py @@ -0,0 +1,328 @@ +# GPLv3 License +# +# Copyright (C) 2022 Ubisoft +# +# 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 2 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 . + +""" +Logging for Stamp Info + +Read this: https://stackoverflow.com/questions/7274732/extending-the-python-logger + +""" + +import os +from pathlib import Path + +import logging + +# from logging import DEBUG, INFO, ERROR + +from ..utils.utils_python import asciiColor +from ..config import config + + +def getLogger(name): + return _logger + + +def getLevelName(): + return logging.getLevelName(_logger.level) + + +# cf https://stackoverflow.com/questions/7274732/extending-the-python-logger + + +class SM_Logger(logging.getLoggerClass()): + def __init__(self, name): + super(SM_Logger, self).__init__(name) + + self._prefix = "StampInfo" + self._defaultColor = "WHITE" + self._defaultForm = "STD" + + # colors in terminal: https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output + # Eg: + # _YELLOW = '\33[33m' + # _ENDCOLOR = '\033[0m' + # print(f"{_YELLOW}text{_ENDCOLOR}") + + # 1; :bold + self._colors = { + "BLUE": "\33[34m", + "BLUE_LIGHT": "\33[1;34m", + "GREEN": "\33[32m", + "GREEN_LIGHT": "\33[1;32m", + "GRAY": "\33[1;30m", + "YELLOW": "\33[33m", + "YELLOW_LIGHT": "\33[1;33m", + "ORANGE": asciiColor(255, 165, 0), + "RED": "\33[31m", + "RED_BG": "\33[41m", + "PINK": asciiColor(255, 192, 203), + "PURPLE": "\33[35m", + "PURPULE_LIGHT": "\33[1;35m", + "TURQUOISE": "\33[36m", + "TURQUOISE_LIGHT": "\33[1;36m", + "WHITE": "\33[37m", + } + + # self._formatter_standard = Formatter("\33[36m" + "~Basic +++" + " {message:<140}" + "\033[0m", style="{") + self._formatter_standard = Formatter(self._prefix + " - " + " {message:<110}", style="{") + self._formatter_basic = Formatter("\33[36m" + "~Basic +++" + " {message:<140}" + "\033[0m", style="{") + self._formatter_other = Formatter("\33[36m" + "S OTHER M+" + " {message:<140}" + "\033[0m", style="{") + self._formatter = { + "STD": self._formatter_standard, + "BASIC": self._formatter_basic, + "OTHER": self._formatter_other, + } + + self.tags = config.getLoggingTags() + + @property + def prefix(self): + return self._prefix + + @prefix.setter + def prefix(self, value): + self._prefix = value + + def _getFormatter(self, col="", form="DEFAULT"): + color = self._colors[col] if col != "" else "" + _ENDCOLOR = "\033[0m" + + if "STD" == form: + f = Formatter(color + self._prefix + " {message:<120}" + _ENDCOLOR, style="{") + elif "REG" == form: + if "" == col: + color = self._colors["YELLOW"] + f = Formatter(color + "{message:<90}" + _ENDCOLOR, style="{") + elif "UNREG" == form: + if "" == col: + color = self._colors["GRAY"] + f = Formatter(color + "{message:<90}" + _ENDCOLOR, style="{") + elif "DEPRECATED" == form: + if "" == col: + color = self._colors["ORANGE"] + f = Formatter(color + "[DEPRECATED] {message:<90}" + _ENDCOLOR, style="{") + elif "WARNING" == form: + if "" == col: + color = self._colors["ORANGE"] + f = Formatter(color + self.prefix + " *** Warning" + " {message:<140}" + _ENDCOLOR, style="{") + elif "ERROR" == form: + if "" == col: + color = self._colors["RED"] + f = Formatter(color + "Stamp Info: " + " {message:<140}" + _ENDCOLOR, style="{") + elif "CRITICAL" == form: + if "" == col: + color = self._colors["RED_BG"] + f = Formatter(color + "Stamp Info: " + " {message:<140}" + _ENDCOLOR, style="{") + elif "OTHER" == form: + f = Formatter(color + "~Other +++" + " {message:<140}" + _ENDCOLOR, style="{") + else: # "DEFAULT" + f = Formatter("\033[0m" + "~default +++" + " {message:<140}", style="{") + return f + + # def debug_basic(self, msg, extra=None, color="GREEN", formatter="OTHER"): + # ch = logging.StreamHandler() + # ch.setLevel(logging.DEBUG) + # ### ch.setFormatter(self.formatter_basic) + # # _logger.handlers[0].setFormatter(self.formatter_basic) + # _logger.handlers[0].setFormatter(self._formatter[formatter]) + # super(SM_Logger, self).debug((self._colors[color] + "{}\033[0m").format(msg), extra=extra) + # # ch.setFormatter(self.formatter_other) + # _logger.handlers[0].setFormatter(self._formatter["OTHER"]) + + def debug_form(self, col="GREEN", form="DEFAULT"): + """Set formatter. To use before call to debug() + eg: _logger.debug_form(col="GREEN", form="STD") + _logger.debug("debug test timer green") + """ + ch = logging.StreamHandler() + ch.setLevel(logging.DEBUG) + _logger.handlers[0].setFormatter(self._getFormatter(col, form)) + + def _print_ext(self, mode, msg, extra=None, col="", form="STD", tag=None, display=True): + """ + Args: + mode: "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL" + """ + if not display: + return + + # accept or silence message display according to tags (if return is enabled then tag is ignored) + if tag is not None: + if tag in self.tags: + if not self.tags[tag]: + return + + if "DEPRECATED" == tag: + form = "DEPRECATED" + + _logger.handlers[0].setFormatter(self._getFormatter(col, form)) + + # Note: marvellous parameter: stacklevel allows to get the call from the sender, otherwise + # it is this function that is used and the path is not good + # cf https://stackoverflow.com/questions/14406347/python-logging-check-location-of-log-files + # and https://www.py4u.net/discuss/157715 + if "DEBUG" == mode: + super(SM_Logger, self).debug(("{}").format(msg), extra=extra, stacklevel=2) + elif "WARNING" == mode: + super(SM_Logger, self).warning(("{}").format(msg), extra=extra, stacklevel=2) + elif "ERROR" == mode: + super(SM_Logger, self).error(("{}").format(msg), extra=extra, stacklevel=2) + elif "CRITICAL" == mode: + super(SM_Logger, self).critical(("{}").format(msg), extra=extra, stacklevel=2) + # INFO + else: + super(SM_Logger, self).info(("{}").format(msg), extra=extra, stacklevel=2) + + _logger.handlers[0].setFormatter(self._getFormatter(self._defaultColor, self._defaultForm)) + + def debug_ext(self, msg, extra=None, col="", form="STD", tag=None, display=True): + """ + eg: + _logger.debug_ext(f"message: {text}", tag="DEPRECATED") + _logger.warning_ext(f"message: {text}") + """ + self._print_ext("DEBUG", msg, extra=extra, col=col, form=form, tag=tag, display=display) + + def info_ext(self, msg, extra=None, col="BLUE_LIGHT", form="STD", tag=None, display=True): + self._print_ext("INFO", msg, extra=extra, col=col, form=form, tag=tag, display=display) + + def warning_ext(self, msg, extra=None, col="ORANGE", form="WARNING", tag=None, display=True): + self._print_ext("WARNING", msg, extra=extra, col=col, form=form, tag=tag, display=display) + + def error_ext(self, msg, extra=None, col="RED", form="ERROR", tag=None, display=True): + self._print_ext("ERROR", msg, extra=extra, col=col, form=form, tag=tag, display=display) + + def critical_ext(self, msg, extra=None, col="RED_BG", form="CRITICAL", tag=None, display=True): + self._print_ext("CRITICAL", msg, extra=extra, col=col, form=form, tag=tag, display=display) + + +class Formatter(logging.Formatter): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def format(self, record: logging.LogRecord): + """ + The role of this custom formatter is: + - append filepath and lineno to logging format but shorten path to files, to make logs more clear + - to append "./" at the begining to permit going to the line quickly with VS Code Ctrl + Click from terminal + """ + s = super().format(record) + + MODULE_PATH = Path(__file__).parent.parent + # print("MODULE_PATH:" + str(MODULE_PATH)) + pathname = Path(record.pathname).relative_to(MODULE_PATH) + s += f" [{os.curdir}{os.sep}{pathname}:{record.lineno}]" + return s + + +def loggerFormatTest(message=""): + return + + print(message) + # warning: put back to the application mode afterward + _logger.setLevel(logging.DEBUG) + + _logger.debug_ext("debug message") + _logger.info_ext("info message") + _logger.warning_ext("warning message") + _logger.error_ext("error message") + _logger.critical_ext("critical message") + + print("\n") + _logger.debug_ext("debug_ext", col="RED", form="STD") + _logger.debug_ext("debug_ext", col="BLUE", form="BASIC") + + mes = " - Registering Test Package" + print(mes) + _logger.debug_ext(mes, form="REG") + _logger.debug_ext("debug test override", col="RED", form="REG") + + # use normal debug() call with aformmatter set before + _logger.debug_form(col="GREEN", form="STD") + _logger.debug("debug test timer green") + + +logging.setLoggerClass(SM_Logger) +_logger = logging.getLogger(__name__) + +# https://docs.python.org/fr/3/howto/logging.html +# https://stackoverflow.com/questions/11581794/how-do-i-change-the-format-of-a-python-log-message-on-a-per-logger-basis + + +def initialize(): + + if config.devDebug: + # print("Initializing Logger...") + # print(f"len(_logger.handlers): {len(_logger.handlers)}") + pass + + _logger.propagate = False + + logging.basicConfig(level=logging.INFO) + _logger.setLevel(logging.INFO) # CRITICAL ERROR WARNING INFO DEBUG NOTSET + + # _logger.setLevel(logging.DEBUG) + formatter = None + + # call config.initGlobalVariables() to get the config variables (currently done in Stamp Info init) + if config.devDebug_ignoreLoggerFormatting: + ch = "~" # "\u02EB" + formatter = Formatter("\33[36m" + ch + " {message:<140}" + "\033[0m", style="{") + else: + # formatter = Formatter("{asctime} {levelname[0]} {name:<30} - {message:<80}", style="{") + formatter = Formatter(_logger.prefix + " " + " {message:<80}", style="{") + + if len(_logger.handlers) == 0: + handler = logging.StreamHandler() + handler.setFormatter(formatter) + _logger.addHandler(handler) + + else: + _logger.handlers[0].setFormatter(formatter) + + # handler = logging.FileHandler(get_log_file()) + # handler.setFormatter(formatter) + # _logger.addHandler(handler) + + loggerFormatTest() + + +# def get_logs_directory(): +# def _get_logs_directory(): +# import tempfile + +# if "MIXER_USER_LOGS_DIR" in os.environ: +# username = os.getlogin() +# base_shared_path = Path(os.environ["MIXER_USER_LOGS_DIR"]) +# if os.path.exists(base_shared_path): +# return os.path.join(os.fspath(base_shared_path), username) +# logger.error( +# f"MIXER_USER_LOGS_DIR env var set to {base_shared_path}, but directory does not exists. Falling back to default location." +# ) +# return os.path.join(os.fspath(tempfile.gettempdir()), "mixer") + +# dir = _get_logs_directory() +# if not os.path.exists(dir): +# os.makedirs(dir) +# return dir + + +# def get_log_file(): +# from mixer.share_data import share_data + +# return os.path.join(get_logs_directory(), f"mixer_logs_{share_data.run_id}.log") diff --git a/stampinfo/icons/__init__.py b/stampinfo/icons/__init__.py index 55e65c4..df4c3ad 100644 --- a/stampinfo/icons/__init__.py +++ b/stampinfo/icons/__init__.py @@ -25,8 +25,13 @@ from pathlib import Path import bpy.utils.previews +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + def register(): + _logger.debug_ext(" - Registering Icons Package", form="REG") global icons_col @@ -39,6 +44,7 @@ def register(): def unregister(): + _logger.debug_ext(" - Unregistering Icons Package", form="UNREG") global icons_col diff --git a/stampinfo/infoImage.py b/stampinfo/infoImage.py index a325126..202ec2d 100644 --- a/stampinfo/infoImage.py +++ b/stampinfo/infoImage.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -33,15 +33,15 @@ getInfoFileFullPath, ) -import logging +from stampinfo.config import sm_logging -_logger = logging.getLogger(__name__) +_logger = sm_logging.getLogger(__name__) # Preparation of the files def renderTmpImageWithStampedInfo(scene, currentFrame, renderPath=None, renderFilename=None, verbose=False): - """ Called by the Pre renderer callback - Preparation of the files + """Called by the Pre renderer callback + Preparation of the files """ # Notes # - Image origine is at TOP LEFT corner @@ -492,7 +492,10 @@ def _debug_drawPadding(borderInd): # textProp = "Corner Note: " if stampLabel else "" textProp = userSettings.cornerNote if stampValue else "" img_draw.text( - (currentTextRight - (font.getsize(textProp))[0], currentTextTop), textProp, font=font, fill=alertColorRGBA, + (currentTextRight - (font.getsize(textProp))[0], currentTextTop), + textProp, + font=font, + fill=alertColorRGBA, ) # ---------- fps and 3D edit ------------- @@ -561,7 +564,9 @@ def _debug_drawPadding(borderInd): # draw box img_draw.line( - [(colBoxLeft, currentBoxTop), (colBoxRight, currentBoxTop)], fill=textColorGray, width=boxLineThickness, + [(colBoxLeft, currentBoxTop), (colBoxRight, currentBoxTop)], + fill=textColorGray, + width=boxLineThickness, ) img_draw.line( [(colBoxLeft, currentBoxBottom), (colBoxRight, currentBoxBottom)], @@ -569,10 +574,14 @@ def _debug_drawPadding(borderInd): width=boxLineThickness, ) img_draw.line( - [(colBoxLeft, currentBoxTop), (colBoxLeft, currentBoxBottom)], fill=textColorGray, width=boxLineThickness, + [(colBoxLeft, currentBoxTop), (colBoxLeft, currentBoxBottom)], + fill=textColorGray, + width=boxLineThickness, ) img_draw.line( - [(colBoxRight, currentBoxTop), (colBoxRight, currentBoxBottom)], fill=textColorGray, width=boxLineThickness, + [(colBoxRight, currentBoxTop), (colBoxRight, currentBoxBottom)], + fill=textColorGray, + width=boxLineThickness, ) # --------------------------------- @@ -704,7 +713,10 @@ def _debug_drawPadding(borderInd): # (currentTextRight - (font.getsize(textProp))[0], currentTextTop), textProp, font=font, fill=textColorRGBA, # ) img_draw.text( - (col04, currentTextTop), textProp, font=font, fill=textColorRGBA, + (col04, currentTextTop), + textProp, + font=font, + fill=textColorRGBA, ) # ---------- file ------------- @@ -778,7 +790,7 @@ def drawRangesAndFrame( color, ): """ - framemode can be '3DFRAME' or 'VIDEOFRAME' + framemode can be '3DFRAME' or 'VIDEOFRAME' """ userSettings = scene.UAS_StampInfo_Settings diff --git a/stampinfo/install/addon_error_prefs.py b/stampinfo/install/addon_error_prefs.py index 50e4294..1b84b16 100644 --- a/stampinfo/install/addon_error_prefs.py +++ b/stampinfo/install/addon_error_prefs.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -19,19 +19,23 @@ add-on global preferences """ +import sys + import bpy from bpy.types import AddonPreferences from bpy.props import BoolProperty, StringProperty from ..ui.dependencies_ui import drawDependencies -import sys +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) class UAS_StampInfo_AddonErrorPrefs(AddonPreferences): """ - Use this to get these prefs: - prefs = context.preferences.addons["stampinfo"].preferences + Use this to get these prefs: + prefs = context.preferences.addons["stampinfo"].preferences """ # this must match the add-on name, use '__package__' @@ -49,13 +53,16 @@ class UAS_StampInfo_AddonErrorPrefs(AddonPreferences): # ------------------------------ install_failed: BoolProperty( - name="Install Failed", default=True, + name="Install Failed", + default=True, ) error_message: StringProperty( - name="Error Message", default="", + name="Error Message", + default="", ) verbose: BoolProperty( - name="Verbose", default=False, + name="Verbose", + default=False, ) ################################################################################## @@ -128,17 +135,14 @@ def draw(self, context): def register(): + _logger.debug_ext(" - Registering Addon Error Prefs Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) - prefs_addon = bpy.context.preferences.addons["stampinfo"].preferences - if prefs_addon.verbose: - print(" - Registering Add-on Installation Error Preferences\n") - def unregister(): - prefs_addon = bpy.context.preferences.addons["stampinfo"].preferences - if prefs_addon.verbose: - print(" - Unregistering Add-on Installation Error Preferences") + _logger.debug_ext(" - Unregistering Addon Error Prefs Package", form="UNREG") + for cls in reversed(_classes): bpy.utils.unregister_class(cls) diff --git a/stampinfo/install/install_dependencies.py b/stampinfo/install/install_dependencies.py index 9c61692..10a90c3 100644 --- a/stampinfo/install/install_dependencies.py +++ b/stampinfo/install/install_dependencies.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -23,9 +23,9 @@ from ..utils.utils_os import internet_on, module_can_be_imported, is_admin from . import addon_error_prefs -import logging +from stampinfo.config import sm_logging -_logger = logging.getLogger(__name__) +_logger = sm_logging.getLogger(__name__) def install_library(lib_names, pip_retries=2, pip_timeout=-100): diff --git a/stampinfo/operators/debug.py b/stampinfo/operators/debug.py index 8ad98f7..9897c79 100644 --- a/stampinfo/operators/debug.py +++ b/stampinfo/operators/debug.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -27,9 +27,9 @@ from ..config import config from ..utils import utils_render -import logging +from stampinfo.config import sm_logging -_logger = logging.getLogger(__name__) +_logger = sm_logging.getLogger(__name__) class UAS_Stamp_Info_OT_EnableDebug(Operator): @@ -86,10 +86,14 @@ def draw(self, context): def register(): + _logger.debug_ext(" - Registering Debug Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) def unregister(): + _logger.debug_ext(" - Unregistering Debug Package", form="UNREG") + for cls in reversed(_classes): bpy.utils.unregister_class(cls) diff --git a/stampinfo/operators/render_operators.py b/stampinfo/operators/render_operators.py index ee2a8e1..33903c4 100644 --- a/stampinfo/operators/render_operators.py +++ b/stampinfo/operators/render_operators.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -33,6 +33,10 @@ from stampinfo import stamper from ..config import config +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + class UAS_PT_StampInfo_Render(Operator): bl_idname = "uas_stampinfo.render" @@ -181,7 +185,10 @@ def execute(self, context): + ".png" ) stampInfoSettings.renderTmpImageWithStampedInfo( - scene, renderFrame, renderPath=tempFramedRenderPath, renderFilename=tempFramedRenderFilenameStill, + scene, + renderFrame, + renderPath=tempFramedRenderPath, + renderFilename=tempFramedRenderFilenameStill, ) elif "ANIMATION" == self.renderMode: @@ -300,10 +307,14 @@ def execute(self, context): def register(): + _logger.debug_ext(" - Registering Render Operators Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) def unregister(): + _logger.debug_ext(" - Unregistering Render Operators Package", form="UNREG") + for cls in reversed(_classes): bpy.utils.unregister_class(cls) diff --git a/stampinfo/properties/stampInfoSettings.py b/stampinfo/properties/stampInfoSettings.py index fafe21e..eef4e2a 100644 --- a/stampinfo/properties/stampInfoSettings.py +++ b/stampinfo/properties/stampInfoSettings.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -19,21 +19,20 @@ General settings """ -import os +# import os from pathlib import Path from stampinfo import stamper import bpy from bpy.props import StringProperty, BoolProperty, IntProperty, FloatProperty, EnumProperty +from stampinfo import config from stampinfo import infoImage from stampinfo.utils import utils -import logging +from stampinfo.config import sm_logging -_logger = logging.getLogger(__name__) - -# from stampinfo.__init__ import bl_info +_logger = sm_logging.getLogger(__name__) # global vars if "gbWkDebug" not in vars() and "gbWkDebug" not in globals(): @@ -56,10 +55,10 @@ class UAS_StampInfoSettings(bpy.types.PropertyGroup): def version(self): - """ Return the add-on version in the form of a tupple made by: - - a string x.y.z (eg: "1.21.3") - - an integer. x.y.z becomes xxyyyzzz (eg: "1.21.3" becomes 1021003) - Return None if the addon has not been found + """Return the add-on version in the form of a tupple made by: + - a string x.y.z (eg: "1.21.3") + - an integer. x.y.z becomes xxyyyzzz (eg: "1.21.3" becomes 1021003) + Return None if the addon has not been found """ return utils.addonVersion("Stamp Info") @@ -224,7 +223,9 @@ def buildLogosList(self, context): # ---------- shared settings --------- animRangeUsed: BoolProperty( - name="Frame Range", description="Stamp the range of the animation, in frames", default=True, + name="Frame Range", + description="Stamp the range of the animation, in frames", + default=True, ) handlesUsed: BoolProperty( name="Frame Handles", @@ -296,7 +297,10 @@ def buildLogosList(self, context): # used by production scripts to specify another file than the current one # if set to "" then it is ignored and the current file name is used customFileFullPath: StringProperty( - name="Custom File Name", description="Enter a path and name of the file to display", default="", options=set(), + name="Custom File Name", + description="Enter a path and name of the file to display", + default="", + options=set(), ) # ---------- shot manager ------------- @@ -320,7 +324,11 @@ def buildLogosList(self, context): ) shotHandles: IntProperty( - name="Shot Handles Duration", description="Duration of the handles of the shot", default=10, min=0, soft_max=50, + name="Shot Handles Duration", + description="Duration of the handles of the shot", + default=10, + min=0, + soft_max=50, ) # ---------- Camera properties ------------- diff --git a/stampinfo/stamper.py b/stampinfo/stamper.py index df3a1e6..dd692a0 100644 --- a/stampinfo/stamper.py +++ b/stampinfo/stamper.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -24,9 +24,9 @@ import bpy import bpy.utils.previews -import logging +from stampinfo.config import sm_logging -_logger = logging.getLogger(__name__) +_logger = sm_logging.getLogger(__name__) def getRenderRange(scene): @@ -36,8 +36,7 @@ def getRenderRange(scene): # wk fix: now retunrs an array of ints! def getRenderResolution(scene): - """Get the current scene rendered image output resolution as float tupple (not int !) and with taking into account the render percentage - """ + """Get the current scene rendered image output resolution as float tupple (not int !) and with taking into account the render percentage""" renderResolution = ( scene.render.resolution_x * scene.render.resolution_percentage * 0.01, scene.render.resolution_y * scene.render.resolution_percentage * 0.01, @@ -115,8 +114,7 @@ def evaluateRenderResolutionForStampInfo(imageRes, resPercentage=100): def getInnerHeight(scene): - """Get the height (integer) in pixels of the image between the 2 borders according to the current mode - """ + """Get the height (integer) in pixels of the image between the 2 borders according to the current mode""" innerH = -1 if "OVER" == scene.UAS_StampInfo_Settings.stampInfoRenderMode: @@ -216,7 +214,7 @@ def getInfoFileFullPath(scene, renderFrameInd=None): def getStampInfoRenderFilepath(scene, useTempDir=False): """Get a functional render file path to render the temporary files - + Returns: If the file is not saved and the path is relative then a temporary file path is returned """ filepath = scene.render.filepath @@ -235,8 +233,7 @@ def getTempBGImageBaseName(): def createTempBGImage(scene): - """Create the temporaty image used to set the render size (not the one with the stamped info) - """ + """Create the temporaty image used to set the render size (not the one with the stamped info)""" from PIL import Image print("\n createTempBGImage ") @@ -274,8 +271,7 @@ def deleteTempImage(scene): def deletePreviousInfoImage(scene, currentFrame): - """Delete only the info image file rendered in the previous frame - """ + """Delete only the info image file rendered in the previous frame""" print("\n deletePreviousInfoImage [ ") rangeStart = getRenderRange(scene)[0] diff --git a/stampinfo/ui/__init__.py b/stampinfo/ui/__init__.py index 23b95cc..b0662d2 100644 --- a/stampinfo/ui/__init__.py +++ b/stampinfo/ui/__init__.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -24,8 +24,14 @@ # from . import si_ui +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + def register(): + _logger.debug_ext(" - Registering UI Package", form="REG") + about.register() prefs.register() @@ -34,6 +40,8 @@ def register(): def unregister(): + _logger.debug_ext(" - Unregistering UI Package", form="UNREG") + # si_ui.unregister() prefs.unregister() about.unregister() diff --git a/stampinfo/ui/about.py b/stampinfo/ui/about.py index 0c8ea62..ff40329 100644 --- a/stampinfo/ui/about.py +++ b/stampinfo/ui/about.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -25,6 +25,10 @@ from ..ui.dependencies_ui import drawDependencies from ..utils.utils import addonCategory +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + class UAS_StampInfo_OT_About(Operator): bl_idname = "uas_stamp_info.about" @@ -102,10 +106,14 @@ def execute(self, context): def register(): + _logger.debug_ext(" - Registering About Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) def unregister(): + _logger.debug_ext(" - Unregistering About Package", form="UNREG") + for cls in reversed(_classes): bpy.utils.unregister_class(cls) diff --git a/stampinfo/ui/dependencies_ui.py b/stampinfo/ui/dependencies_ui.py index ebe266a..3b78504 100644 --- a/stampinfo/ui/dependencies_ui.py +++ b/stampinfo/ui/dependencies_ui.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 diff --git a/stampinfo/ui/prefs.py b/stampinfo/ui/prefs.py index bf133c8..8202d5f 100644 --- a/stampinfo/ui/prefs.py +++ b/stampinfo/ui/prefs.py @@ -22,6 +22,10 @@ import bpy from bpy.types import Menu +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + ############# # Preferences @@ -63,10 +67,14 @@ def draw(self, context): def register(): + _logger.debug_ext(" - Registering Prefs Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) def unregister(): + _logger.debug_ext(" - Unregistering Prefs Package", form="UNREG") + for cls in reversed(_classes): bpy.utils.unregister_class(cls) diff --git a/stampinfo/ui/si_ui.py b/stampinfo/ui/si_ui.py index c2d6ff9..d40d583 100644 --- a/stampinfo/ui/si_ui.py +++ b/stampinfo/ui/si_ui.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -19,7 +19,7 @@ Main panel UI """ -import logging +from stampinfo.config import sm_logging import bpy import bpy.utils.previews @@ -39,7 +39,7 @@ from stampinfo.operators import debug -_logger = logging.getLogger(__name__) +_logger = sm_logging.getLogger(__name__) importlib.reload(stampInfoSettings) @@ -70,6 +70,7 @@ def draw_header(self, context): row.operator("uas_stamp_info.about", text="", icon_value=icon.icon_id) def draw_header_preset(self, context): + prefs = context.preferences.addons["stampinfo"].preferences layout = self.layout layout.emboss = "NONE" row = layout.row(align=True) @@ -96,6 +97,12 @@ def draw_header_preset(self, context): row.separator(factor=2) row.menu("UAS_MT_StampInfo_prefs_mainmenu", icon="PREFERENCES", text="") + if prefs.newAvailableVersion: + row.separator(factor=0.5) + subRow = row.row() + subRow.alert = True + subRow.operator("uas_shot_manager.update_dialog", text="", icon="WORLD_DATA") + row.separator(factor=1.0) def draw(self, context): @@ -695,10 +702,14 @@ def module_can_be_imported(name): def register(): + _logger.debug_ext(" - Registering Main UI Package", form="REG") + for cls in classes: bpy.utils.register_class(cls) def unregister(): + _logger.debug_ext(" - Unregistering Main UI Package", form="UNREG") + for cls in reversed(classes): bpy.utils.unregister_class(cls) diff --git a/stampinfo/utils/utils.py b/stampinfo/utils/utils.py index 0456f04..d0a0687 100644 --- a/stampinfo/utils/utils.py +++ b/stampinfo/utils/utils.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -28,22 +28,20 @@ import os -import logging +from stampinfo.config import sm_logging -_logger = logging.getLogger(__name__) +_logger = sm_logging.getLogger(__name__) def convertVersionStrToInt(versionStr): - """ Convert a string formated like "1.23.48" to a version integer such as 1023048 - """ + """Convert a string formated like "1.23.48" to a version integer such as 1023048""" formatedVersion = "{:02}{:03}{:03}" versionSplitted = versionStr.split(".") return int(formatedVersion.format(int(versionSplitted[0]), int(versionSplitted[1]), int(versionSplitted[2]))) def convertVersionIntToStr(versionInt): - """ Convert an integer formated like 1023048 to a version string such as "1.23.48" - """ + """Convert an integer formated like 1023048 to a version string such as "1.23.48" """ versionIntStr = str(versionInt) length = len(versionIntStr) versionStr = ( @@ -57,10 +55,10 @@ def convertVersionIntToStr(versionInt): def addonVersion(addonName): - """ Return the add-on version in the form of a tupple made by: - - a string x.y.z (eg: "1.21.3") - - an integer. x.y.z becomes xxyyyzzz (eg: "1.21.3" becomes 1021003) - Return None if the addon has not been found + """Return the add-on version in the form of a tupple made by: + - a string x.y.z (eg: "1.21.3") + - an integer. x.y.z becomes xxyyyzzz (eg: "1.21.3" becomes 1021003) + Return None if the addon has not been found """ import addon_utils @@ -90,17 +88,17 @@ def addonVersion(addonName): return versions -def display_addon_registered_version(addon_name): +def display_addon_registered_version(addon_name, more_info=""): versionTupple = addonVersion(addon_name) if versionTupple is not None: print( - "\n*** *** Registering " + "\n*** *** Registering Ubisoft " + addon_name + " Add-on - version: " + versionTupple[0] - + " (" - + str(versionTupple[1]) - + ") *** ***" + + f" ({versionTupple[1]})" + + (f" - {more_info}" if more_info != "" else "") + + " *** ***" ) else: print('\n *** Cannot find registered version for add-on "' + addon_name + '" ***\n') @@ -162,7 +160,9 @@ def openMedia(media_filepath, inExternalPlayer=False): # bpy.ops.render.view_show() bpy.ops.image.open( - filepath=media_filepath, relative_path=False, show_multiview=False, + filepath=media_filepath, + relative_path=False, + show_multiview=False, ) # bpy.data.images.[image_name].reload() @@ -179,8 +179,8 @@ def openMedia(media_filepath, inExternalPlayer=False): def add_background_video_to_cam( camera: bpy.types.Camera, movie_path, frame_start, alpha=-1, proxyRenderSize="PROXY_50" ): - """ Camera argument: use camera.data, not the camera object - proxyRenderSize is PROXY_25, PROXY_50, PROXY_75, PROXY_100, FULL + """Camera argument: use camera.data, not the camera object + proxyRenderSize is PROXY_25, PROXY_50, PROXY_75, PROXY_100, FULL """ print("add_background_video_to_cam") movie_path = Path(movie_path) @@ -212,8 +212,7 @@ def add_background_video_to_cam( def findFirstUniqueName(originalItem, name, itemsArray): - """ Return a string that correspont to name.xxx as the first unique name in the array - """ + """Return a string that correspont to name.xxx as the first unique name in the array""" itemInd = 0 numDuplicatesFound = 0 newIndexStr = ".{:03}" @@ -229,8 +228,8 @@ def findFirstUniqueName(originalItem, name, itemsArray): def getSceneVSE(vsm_sceneName, createVseTab=False): - """ Return the scene that has the name held by vsm_sceneName and adds a VSE in it if there is not already one. - Use .sequence_editor to get the vse of the scene + """Return the scene that has the name held by vsm_sceneName and adds a VSE in it if there is not already one. + Use .sequence_editor to get the vse of the scene """ vsm_scene = None @@ -277,8 +276,7 @@ def getSceneVSE(vsm_sceneName, createVseTab=False): def duplicateObject(sourceObject): - """ Duplicate (deepcopy) an object and place it in the same collection - """ + """Duplicate (deepcopy) an object and place it in the same collection""" newObject = sourceObject.copy() newObject.animation_data.action = sourceObject.animation_data.action.copy() newObject.data = sourceObject.data.copy() @@ -290,4 +288,3 @@ def duplicateObject(sourceObject): else: (sourceObject.users_scene)[0].collection.objects.link(newObject) return newObject - diff --git a/stampinfo/utils/utils_filenames.py b/stampinfo/utils/utils_filenames.py index da27957..00aa036 100644 --- a/stampinfo/utils/utils_filenames.py +++ b/stampinfo/utils/utils_filenames.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -23,6 +23,10 @@ from pathlib import Path import bpy +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + class SequencePath: # to do: @@ -32,7 +36,7 @@ class SequencePath: # - support absolute and relative paths """ Split a file path into parts. Dedicated to sequence filename management. - + Returns an instance made of: - fullpath: the file path and name - parent: the file path without the file name AND with a "\" at the end @@ -40,7 +44,7 @@ class SequencePath: - stem: the name of the file without extension - seq_name: the name of the sequence when # are removed - suffix: the file extension - + When the initial sequence path and name is submitted with no extension then it is seen as a path Eg.: myPath = SequencePath("c:\temp\mySequence_####.png") @@ -326,10 +330,14 @@ def run_sequence_path_tests(at_frame=None): def register(): + _logger.debug_ext(" - Registering Filenames Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) def unregister(): + _logger.debug_ext(" - Unregistering Filenames Package", form="UNREG") + for cls in reversed(_classes): bpy.utils.unregister_class(cls) diff --git a/stampinfo/utils/utils_inspectors.py b/stampinfo/utils/utils_inspectors.py index 2a4a468..f6b5ad8 100644 --- a/stampinfo/utils/utils_inspectors.py +++ b/stampinfo/utils/utils_inspectors.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 diff --git a/stampinfo/utils/utils_python.py b/stampinfo/utils/utils_python.py new file mode 100644 index 0000000..d1ba1c4 --- /dev/null +++ b/stampinfo/utils/utils_python.py @@ -0,0 +1,32 @@ +# GPLv3 License +# +# Copyright (C) 2022 Ubisoft +# +# 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 2 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 . + +""" +Functions useful in a generic context +""" + + +def copyString(str1): + resStr = "" + for c in str1: + resStr += c + return resStr + + +def asciiColor(r, g, b): + """Convert rgb values to ascii color string""" + return f"\33[38;2;{r};{g};{b}m" diff --git a/stampinfo/utils/utils_render.py b/stampinfo/utils/utils_render.py index c62ffa3..f79298a 100644 --- a/stampinfo/utils/utils_render.py +++ b/stampinfo/utils/utils_render.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -25,9 +25,9 @@ import bpy from bpy.types import Operator -import logging +from stampinfo.config import sm_logging -_logger = logging.getLogger(__name__) +_logger = sm_logging.getLogger(__name__) def isRenderPathValid(scene): @@ -47,7 +47,7 @@ def getRenderOutputFilename(scene, fileNameOnly=False): outputFiles = list() filePath = bpy.path.abspath(scene.render.filepath) - + # print("\n *** images defs:") # print(f" From {scene.frame_start} to {scene.frame_end} by {scene.frame_step}") @@ -118,4 +118,3 @@ def execute(self, context): # bpy.ops.render.opengl ( animation = True ) return {"FINISHED"} - diff --git a/stampinfo/utils/utils_ui.py b/stampinfo/utils/utils_ui.py index 4152b3d..6b7ec68 100644 --- a/stampinfo/utils/utils_ui.py +++ b/stampinfo/utils/utils_ui.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -34,23 +34,52 @@ from .utils_os import open_folder +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + + ################### # UI ################### def collapsable_panel( - layout: bpy.types.UILayout, data: bpy.types.AnyType, property: str, alert: bool = False, **kwargs + layout: bpy.types.UILayout, + data: bpy.types.AnyType, + property: str, + alert: bool = False, + text=None, ): - row = layout.row() + """Draw an arrow to collapse or extend a panel. + Return the title row + Args: + layout: parent component + data: the object with the properties + property: the boolean used to store the rolled-down state of the panel + alert: is the title bar of the panel is drawn in alert mode + text: the title of the panel + eg: collapsable_panel(layout, addon_props, "display_users", text="Server Users") + if addon_props.addonPrefs_ui_expanded: ... + """ + row = layout.row(align=True) + row.alignment = "LEFT" + # row.scale_x = 0.9 + row.alert = alert row.prop( - data, property, icon="TRIA_DOWN" if getattr(data, property) else "TRIA_RIGHT", icon_only=True, emboss=False, + data, + property, + icon="TRIA_DOWN" if getattr(data, property) else "TRIA_RIGHT", + icon_only=True, + emboss=False, + text=text, ) if alert: - row.alert = True row.label(text="", icon="ERROR") - row.label(**kwargs) - return getattr(data, property) + # row.label(text=text) + row.alert = False + + return row ################### @@ -228,14 +257,75 @@ def execute(self, context): #################################################################### -_classes = (UAS_StampInfo_OpenExplorer, UAS_OT_Open_Documentation_Url, UAS_OpenFileBrowser, UAS_StampInfo_OT_Querybox) + +class UAS_ShotManager_UpdateDialog(Operator): + bl_idname = "uas_shot_manager.update_dialog" + bl_label = "Add-on Update Available" + bl_description = "Open a dialog window presenting the available update of the add-on" + + # can be a web url or an intranet path + url: StringProperty(default="") + + addonName: StringProperty(default="") + + def invoke(self, context, event): + self.addonName = "Ubisoft Stamp Info" + self.url = "https://github.com/ubisoft/stampinfo/releases/latest" + + return context.window_manager.invoke_props_dialog(self, width=450) + + def draw(self, context): + prefs = context.preferences.addons["stampinfo"].preferences + + layout = self.layout + box = layout.box() + col = box.column() + + sepRow = col.row() + sepRow.separator(factor=0.5) + + row = col.row() + newVersionStr = f"V. {convertVersionIntToStr(prefs.newAvailableVersion)}" + row.label(text=f"A new version of {self.addonName} is available on GitHub: {newVersionStr}") + + sepRow = col.row() + sepRow.separator(factor=0.5) + + row = col.row() + row.label(text="You can download it here:") + + doc_op = row.operator("stampinfo.open_documentation_url", text="Download Latest", icon="URL") + doc_op.path = self.url + doc_op.tooltip = "Open latest Stamp Info download page: " + doc_op.path + + sepRow = col.row() + sepRow.separator(factor=0.5) + + def execute(self, context): + return {"FINISHED"} + + +#################################################################### + + +_classes = ( + UAS_StampInfo_OpenExplorer, + UAS_OT_Open_Documentation_Url, + UAS_OpenFileBrowser, + UAS_StampInfo_OT_Querybox, + UAS_ShotManager_UpdateDialog, +) def register(): + _logger.debug_ext(" - Registering Utils UI Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) def unregister(): + _logger.debug_ext(" - Unregistering Utils UI Package", form="UNREG") + for cls in reversed(_classes): bpy.utils.unregister_class(cls) diff --git a/stampinfo/utils/utils_vse_render.py b/stampinfo/utils/utils_vse_render.py index efba8a3..0ddbc96 100644 --- a/stampinfo/utils/utils_vse_render.py +++ b/stampinfo/utils/utils_vse_render.py @@ -1,6 +1,6 @@ # GPLv3 License # -# Copyright (C) 2021 Ubisoft +# Copyright (C) 2022 Ubisoft # # 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 @@ -35,6 +35,11 @@ from ..utils import utils +from stampinfo.config import sm_logging + +_logger = sm_logging.getLogger(__name__) + + # # ------------------------------------------------------------------------# # # VSE tool Panel # # # ------------------------------------------------------------------------# @@ -1016,6 +1021,8 @@ def compositeVideoInVSE( def register(): + _logger.debug_ext(" - Registering Utils VSE Render Package", form="REG") + for cls in _classes: bpy.utils.register_class(cls) @@ -1025,6 +1032,8 @@ def register(): def unregister(): + _logger.debug_ext(" - Unregistering Utils VSE Render Package", form="UNREG") + del bpy.types.WindowManager.stampinfo_vse_render bpy.utils.unregister_class(UAS_VSE_OpenFileBrowser)