From e574aca703f0803706201af76644e0177f2b5c63 Mon Sep 17 00:00:00 2001 From: SuddenDevelopment Date: Fri, 24 Feb 2023 23:36:37 -0500 Subject: [PATCH 1/2] updating to current stuff --- __init__.py | 47 +++++++------ config.py | 8 +++ icons.py | 39 +++++++++++ install.py | 34 ++++++++++ operators.py | 39 +++++++++++ preferences.py | 37 +++++++++++ ui.py | 27 ++++++-- version.py | 174 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 380 insertions(+), 25 deletions(-) create mode 100644 config.py create mode 100644 icons.py create mode 100644 install.py create mode 100644 operators.py create mode 100644 preferences.py create mode 100644 version.py diff --git a/__init__.py b/__init__.py index d1e82b0..e405afd 100644 --- a/__init__.py +++ b/__init__.py @@ -14,37 +14,42 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from . import ui +from . import operators +from . import preferences +from . import properties +from . import install + + bl_info = { - "name": "My Awesome Add-on", - "description": "Single line describing my awesome add-on.", - "author": "Aaron Powell", - "version": (1, 0), - "blender": (2, 80, 0), - "location": "Properties > Render > My Awesome Panel", - "warning": "", # used for warning icon and text in add-ons panel - "wiki_url": "http://my.wiki.url", - "tracker_url": "http://my.bugtracker.url", - "support": "COMMUNITY", - "category": "Render" - } - -import bpy + "name": "My Awesome Add-on", + "description": "Single line describing my awesome add-on.", + "author": "Aaron Powell, Anthony Aragues", + "version": (0, 0, 1), + "blender": (3, 4, 0), + "location": "Properties > Render > My Awesome Panel", + "warning": "", # used for warning icon and text in add-ons panel + "wiki_url": "http://my.wiki.url", + "tracker_url": "http://my.bugtracker.url", + "support": "COMMUNITY", + "category": "Render" +} -# -# Add additional functions here -# def register(): - from . import properties - from . import ui properties.register() + operators.register() + preferences.register() ui.register() + install.addPackages() + def unregister(): - from . import properties - from . import ui properties.unregister() + operators.unregister() + preferences.unregister() ui.unregister() + if __name__ == '__main__': register() diff --git a/config.py b/config.py new file mode 100644 index 0000000..b42bd0b --- /dev/null +++ b/config.py @@ -0,0 +1,8 @@ +# ALL THE APPLICATION CONSTANTS + +ADDON_PREFIX = 'TMP' +ADDON_NAME = 'Template' + +ICONS_DIRECTORY = 'icons' + +PIP_PACKAGES = [] diff --git a/icons.py b/icons.py new file mode 100644 index 0000000..604e293 --- /dev/null +++ b/icons.py @@ -0,0 +1,39 @@ +import os +import bpy +from . import config + + +arrIcons = None +strDirectory = os.path.join(os.path.dirname(__file__), config.ICONS_DIRECTORY) + +# 0. from . import icons +# 1. update the default path above +# 2. put initIcons in the primary register chain +# 3. put delIcons in the unregister chain +# example use = row.operator("key.set_space", text="SET",icon_value = icons.getIconId("set_space_16")) + + +def getIconId(strIcon): + # The initialize_icons_collection function needs to be called first. + return getIcon(strIcon).icon_id + + +def getIcon(strIcon, strPath=None): + if strPath == None: + strPath = os.path.join(strDirectory, strIcon + ".png") + if strIcon in arrIcons: + return arrIcons[strIcon] + try: + return arrIcons.load(strIcon, strPath, "IMAGE") + except: + print("couldn't find file for icon: ", strIcon, strPath) + return None + + +def initIcons(): + global arrIcons + arrIcons = bpy.utils.previews.new() + + +def delIcons(): + bpy.utils.previews.remove(arrIcons) diff --git a/install.py b/install.py new file mode 100644 index 0000000..5a68b68 --- /dev/null +++ b/install.py @@ -0,0 +1,34 @@ +import bpy +import subprocess +import pathlib +import pkgutil +from . import config + + +def addPip(strLibrary, reinstall=False): + if pkgutil.find_loader(strLibrary) is None: + try: + python_path = bpy.app.binary_path_python + except AttributeError: + import sys + python_path = next( + (pathlib.Path(sys.prefix)/"bin").glob("python*")) + + sub = subprocess.run([python_path, "-m", "ensurepip"]) + if sub.returncode != 0: + return False + + mode = "--upgrade" + if reinstall: + mode = "--force-reinstall" + + sub = subprocess.run( + [python_path, "-m", "pip", "install", mode, "--user", strLibrary]) + if sub.returncode != 0: + return False + return True + + +def addPackages(): + for strPackage in config.PIP_PACKAGES: + addPip(strPackage) diff --git a/operators.py b/operators.py new file mode 100644 index 0000000..c62a4ae --- /dev/null +++ b/operators.py @@ -0,0 +1,39 @@ +import bpy +from . import config + +# collect all the classes to register +arrClasses = [] + + +def prefix_name(opClass): + opClass.__name__ = config.ADDON_PREFIX.upper() + '_OT_' + opClass.__name__ + arrClasses.append(opClass) + return opClass + + +@prefix_name +class shoot_first(bpy.types.Operator): + """first operators tooltip""" + bl_idname = f'{config.ADDON_PREFIX.lower()}.shoot_first' + bl_label = "Shoot" + bl_options = {'UNDO'} + + @classmethod + def poll(cls, context): + # put stuff here as a condition fo enabling the operator return True or False for enabling + return True + + def execute(self, context): + print("I shot!") + return {'FINISHED'} + + +#### || CLASS MAINTENANCE ||#### +def register(): + for i in arrClasses: + bpy.utils.register_class(i) + + +def unregister(): + for i in reversed(arrClasses): + bpy.utils.unregister_class(i) diff --git a/preferences.py b/preferences.py new file mode 100644 index 0000000..cf9ede2 --- /dev/null +++ b/preferences.py @@ -0,0 +1,37 @@ +import bpy +from . import config + +arrClasses = [] + + +def prefix_name(opClass): + opClass.__name__ = config.ADDON_PREFIX.upper() + opClass.__name__ + arrClasses.append(opClass) + return opClass + + +@prefix_name +class Preferences(bpy.types.AddonPreferences): + bl_idname = __package__ + + some_path: bpy.props.StringProperty( + name="Custom Folder Path", + subtype='FILE_PATH', + ) + + def draw(self, context): + layout = self.layout + layout.label(text="PREFERENCES") + row = layout.row() + row.prop(self, "some_path") + + +#### || CLASS MAINTENANCE ||#### +def register(): + for i in arrClasses: + bpy.utils.register_class(i) + + +def unregister(): + for i in reversed(arrClasses): + bpy.utils.unregister_class(i) diff --git a/ui.py b/ui.py index 8aa67b7..8e03ce8 100644 --- a/ui.py +++ b/ui.py @@ -15,24 +15,43 @@ # along with this program. If not, see . import bpy +import sys from bpy.types import Panel +from . import config + +# collect all the classes to register +arrClasses = [] + + +def prefix_name(opClass): + opClass.__name__ = config.ADDON_PREFIX.upper() + '_PT_' + opClass.__name__ + arrClasses.append(opClass) + return opClass # # Add additional functions here # + +@prefix_name class MyPanel(Panel): - bl_label = 'My Awesome Panel' + bl_label = config.ADDON_NAME bl_space_type = 'PROPERTIES' - bl_region_type= 'WINDOW' + bl_region_type = 'WINDOW' bl_context = 'render' def draw(self, context): row = self.layout.row() row.prop(context.scene, 'my_property') + row.op() + +#### || CLASS MAINTENANCE ||#### def register(): - bpy.utils.register_class(MyPanel) + for i in arrClasses: + bpy.utils.register_class(i) + def unregister(): - bpy.utils.unregister_class(MyPanel) + for i in reversed(arrClasses): + bpy.utils.unregister_class(i) diff --git a/version.py b/version.py new file mode 100644 index 0000000..1993e63 --- /dev/null +++ b/version.py @@ -0,0 +1,174 @@ +import bpy +import requests +import json +import textwrap +import os +import time +from . import config + +# This fun little script created by Anthony Aragues: https://AnthonyAragues.com +# 1. Update the defaults and constants at the top +# 2. add the register(bl_info) and unregister() in init chain +# 3. call the draw function from the panel you want it to appear: version.draw_version_box(self, context) + +URL = 'https://anthonyaragues.com/animix_check.json' +PREFIX = 'TMP' +if config and config.ADDON_PREFIX: + PREFIX = config.ADDON_PREFIX +ALLOW_DISMISS_VERSION = True +CHECK_FREQUENCY = 86400 * 30 +CHECK_EVERY = 10 +objDefault = { + "version": "0", + "ver_message": "Update Available", + "bm_url": "blendermarket url", + "gm_url": "gumrd url", + "message": "", + "btn_name": "", + "url": "", + "hide_message": True, + "hide_version": True +} +# ----====|| EVERYTHING BELOW HERE SHOULD NOT NEED TO BE MODIFIED MANUALLY ||====---- +VERSION_FILE = os.path.join(os.path.dirname( + __file__), f'{PREFIX}_version.json') +arrClasses = [] + + +def prefix_name(opClass): + opClass.__name__ = PREFIX.upper() + '_OT_' + opClass.__name__ + arrClasses.append(opClass) + return opClass + + +@prefix_name +class Hide_Version(bpy.types.Operator): + bl_idname = f'{PREFIX.lower()}.hide_version_panel' + bl_label = "x" + bl_description = "Dismiss this version" + + def execute(self, context): + objVersion = getVersionFile() + objVersion["hide_version"] = True + setVersionFile(objVersion) + return {'FINISHED'} + + +@prefix_name +class Hide_Message(bpy.types.Operator): + bl_idname = f'{PREFIX.lower()}.hide_message_panel' + bl_label = "x" + bl_description = "Dismiss this message" + + def execute(self, context): + objVersion = getVersionFile() + objVersion["hide_message"] = True + setVersionFile(objVersion) + return {'FINISHED'} + + +def getIntVersion(strVersion): + strVersion = strVersion.replace(",", ".").replace(")", "").replace("(", "") + return int(strVersion.replace(".", "").replace(" ", "")) + + +def getVersionFile(): + objVersion = None + try: + objFile = open(VERSION_FILE) + objVersion = json.load(objFile) + objFile.close() + for strKey in objDefault.keys(): + if strKey not in objVersion: + objVersion[strKey] = objDefault['strKey'] + except: + objVersion = objDefault + return objVersion + + +def setVersionFile(objVersionFile): + objFile = open(VERSION_FILE, 'w') + objFile.write(json.dumps(objVersionFile)) + objFile.close() + + +def check_version(bl_info): + objResponse = None + objVersion = None + objPreference = getVersionFile() + # some fail early modes + # throttle check frequency by setting + intTime = int(time.time()) + if 'checkIn' in objPreference: + if objPreference['checkIn'] > 0: + objPreference['checkIn'] = objPreference['checkIn']-1 + setVersionFile(objPreference) + return + + if 'lastCheck' in objPreference and objPreference['lastCheck'] + CHECK_FREQUENCY > intTime: + return + + try: + print('check url') + objResponse = requests.get(URL) + except: + pass + if objResponse and objResponse.status_code == 200: + try: + objVersion = json.loads(objResponse.text) + except: + pass + if objVersion is not None: + objVersion['lastCheck'] = intTime + objVersion['checkIn'] = CHECK_EVERY + if getIntVersion(objVersion["version"]) > getIntVersion(str(bl_info["version"])): + if getIntVersion(objVersion["version"]) > getIntVersion(objPreference["version"]): + objVersion["hide_version"] = False + else: + objVersion["hide_version"] = objPreference["hide_version"] + else: + objVersion["hide_version"] = True + if objVersion["message"] != objPreference["message"]: + objVersion["hide_message"] = False + else: + objVersion["hide_message"] = objPreference["hide_message"] + setVersionFile(objVersion) + + +def getTextArray(context, text): + intWidth = int(context.region.width)-30 + chars = intWidth / 7 # 7 pix on 1 character + wrapper = textwrap.TextWrapper(width=chars) + return wrapper.wrap(text=text) + + +def draw_version_box(objPanel, context): + # --------------- VERSION CHECK PANEL ----------------- + objVersion = getVersionFile() + layout = objPanel.layout + if objVersion is not None and "hide_version" in objVersion.keys() and objVersion["hide_version"] != True: + box = layout.box() + row = box.row(align=True) + row.alignment = 'EXPAND' + row.label(icon="INFO", text=objVersion["ver_message"]) + if ALLOW_DISMISS_VERSION: + row.operator(f'{PREFIX}.hide_version_panel', text="", icon="X") + if "bm_url" in objVersion.keys(): + row = box.row() + row.operator( + 'wm.url_open', text="Blender Market", icon="URL").url = objVersion["bm_url"] + if "gm_url" in objVersion.keys(): + row = box.row() + row.operator( + 'wm.url_open', text="GumRoad", icon="URL").url = objVersion["gm_url"] + + +def register(bl_info): + for i in arrClasses: + bpy.utils.register_class(i) + check_version(bl_info) + + +def unregister(): + for i in reversed(arrClasses): + bpy.utils.unregister_class(i) From f662ff18b59ac715456637fb2deb42cd3c8b8712 Mon Sep 17 00:00:00 2001 From: SuddenDevelopment Date: Mon, 27 Feb 2023 18:26:00 -0500 Subject: [PATCH 2/2] minor template name change --- preferences.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preferences.py b/preferences.py index cf9ede2..8a40b3d 100644 --- a/preferences.py +++ b/preferences.py @@ -5,7 +5,7 @@ def prefix_name(opClass): - opClass.__name__ = config.ADDON_PREFIX.upper() + opClass.__name__ + opClass.__name__ = config.ADDON_PREFIX.upper() + '_' + opClass.__name__ arrClasses.append(opClass) return opClass