diff --git a/__init__.py b/__init__.py index fb7af8c..9fb5e1c 100644 --- a/__init__.py +++ b/__init__.py @@ -1,54 +1,26 @@ +# -*- coding: utf-8 -*- + bl_info = { 'name': 'Package Manager', 'description': 'Testing package management', 'category': 'System', 'support': 'TESTING', } -import bpy -from bpy.props import CollectionProperty -from bpy.types import PropertyGroup, Panel, UIList, AddonPreferences -class RepositoryProperty(PropertyGroup): - url = bpy.props.StringProperty(name="URL") +# from bpy.utils import register_class, unregister_class +import importlib +import sys +import logging -class PACKAGE_UL_repositories(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - split = layout.split(0.3) - split.prop(item, "name") - split.prop(item, "url") - - def invoke(self, onctext, event): - pass - -class PackagePreferences(AddonPreferences): - bl_idname = __package__ - - repositories = CollectionProperty(type=RepositoryProperty) - active_repository = bpy.props.IntProperty() - - def draw(self, context): - layout = self.layout - row = layout.row() - row.template_list("PACKAGE_UL_repositories", "", self, "repositories", self, "active_repository") - col = row.column(align=True) - col.operator("package.add_repository", icon="ZOOMIN", text="") - col.operator("package.remove_repository", icon="ZOOMOUT", text="") - -# class PackageManager: -# # For some reason accessing *Property objects stored in this class gives a TypeError: -# # UILayout.prop(): error with argument 1, "data" - Function.data expected a AnyType type, not tuple -# # But accessing them when not stored in a class or dict is fine? -# settings = bpy.props.PointerProperty(type=PackageSettings) -# last_response_code = bpy.props.PointerProperty(type=bpy.types.StringProperty) +log = logging.getLogger(__name__) def register(): # Reload support - import sys - if '%s.pkg_ops' % __name__ in sys.modules: - import importlib + if '%s.blender_common' % __name__ in sys.modules: + def reload_mod(name): - print("Reloading %s" % name) + log.debug("Reloading %s", name) modname = '%s.%s' % (__name__, name) try: old_module = sys.modules[modname] @@ -61,26 +33,13 @@ def register(): sys.modules[modname] = new_module return new_module - pkg_ops = reload_mod('pkg_ops') - pkg_ui = reload_mod('pkg_ui') + blender_common = reload_mod('blender_common') else: - from . import (pkg_ops, pkg_ui) + from . import blender_common - bpy.utils.register_class(RepositoryProperty) - bpy.utils.register_class(PACKAGE_UL_repositories) - bpy.utils.register_class(PackagePreferences) - pkg_ops.register() - pkg_ui.register() + blender_common.register() def unregister(): - from . import (pkg_ops, pkg_ui) - - pkg_ops.unregister() - pkg_ui.unregister(); - bpy.utils.unregister_class(RepositoryProperty) - bpy.utils.unregister_class(PACKAGE_UL_repositories) - bpy.utils.unregister_class(PackagePreferences) - del bpy.types.Scene.custom - del bpy.types.Scene.custom_idx + blender_common.unregister() diff --git a/pkg_ops.py b/blender_common.py similarity index 50% rename from pkg_ops.py rename to blender_common.py index dfbdb61..f5fe58a 100644 --- a/pkg_ops.py +++ b/blender_common.py @@ -1,54 +1,36 @@ +# -*- coding: utf-8 -*- +# This file is for code dealing specifically with blender + import bpy -import logging -from multiprocessing import Process, Pipe -from . import blenderpack +from bpy.props import CollectionProperty +from bpy.types import PropertyGroup, Panel, UIList, AddonPreferences, Operator +from .subprocess_adapter import SubprocessOperatorMixin -class SubprocessOperatorMixin: - timer = None - log = logging.getLogger("%s.SubprocessOperatorMixin" % __name__) +class RepositoryProperty(PropertyGroup): + url = bpy.props.StringProperty(name="URL") - # run once in invoke - def setup(self): - pass - # run on receipt of data from subprocess - def handle_response(self, resp): +class PACKAGE_UL_repositories(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + split = layout.split(0.3) + split.prop(item, "name") + split.prop(item, "url") + + def invoke(self, onctext, event): pass - def __init__(self): - self.pipe = Pipe() - self.subprocess = None +class PackagePreferences(AddonPreferences): + bl_idname = __package__ - def execute(self, context): - return self.invoke(context, None) + repositories = CollectionProperty(type=RepositoryProperty) + active_repository = bpy.props.IntProperty() - def invoke(self, context, event): - self.subprocess.start() - # we have to explicitly close the end of the pipe we are NOT using, - # otherwise no exception will be generated when the other process closes its end. - self.pipe[1].close() - - wm = context.window_manager - wm.modal_handler_add(self) - self.timer = wm.event_timer_add(.01, context.window) - - self.setup() - - return {'RUNNING_MODAL'} - - def modal(self, context, event): - self.log.debug("polling") - try: - if self.pipe[0].poll(): - newdata = self.pipe[0].recv() - else: - newdata = None - except EOFError: - self.log.debug("done polling") - return {'FINISHED'} - - if newdata is not None: - self.handle_response(newdata) - return {'PASS_THROUGH'} + def draw(self, context): + layout = self.layout + row = layout.row() + row.template_list("PACKAGE_UL_repositories", "", self, "repositories", self, "active_repository") + col = row.column(align=True) + col.operator("package.add_repository", icon="ZOOMIN", text="") + col.operator("package.remove_repository", icon="ZOOMOUT", text="") class PACKAGE_OT_fetch(SubprocessOperatorMixin, bpy.types.Operator): bl_idname = "package.fetch" @@ -88,11 +70,22 @@ class PACKAGE_OT_remove_repository(bpy.types.Operator): def register(): + bpy.utils.register_class(RepositoryProperty) + bpy.utils.register_class(PackagePreferences) + bpy.utils.register_class(PACKAGE_OT_fetch) + bpy.utils.register_class(PACKAGE_OT_add_repository) bpy.utils.register_class(PACKAGE_OT_remove_repository) + bpy.utils.register_class(PACKAGE_UL_repositories) def unregister(): - bpy.utils.unregister_class(PACKAGE_OT_fetch) - bpy.utils.unregister_class(PACKAGE_OT_remove_repository) + bpy.utils.unregister_class(RepositoryProperty) + bpy.utils.unregister_class(PackagePreferences) + + bpy.utils.unregister_class(PACKAGE_OT_fetch) + + bpy.utils.unregister_class(PACKAGE_OT_add_repository) + bpy.utils.unregister_class(PACKAGE_OT_remove_repository) + bpy.utils.unregister_class(PACKAGE_UL_repositories) diff --git a/pkg_ui.py b/pkg_ui.py deleted file mode 100644 index 2a7c347..0000000 --- a/pkg_ui.py +++ /dev/null @@ -1,33 +0,0 @@ -import bpy -from . import pkg_ops - -class USERPREF_PT_packages(bpy.types.Panel): - bl_label = "Package Management" - bl_space_type = 'USER_PREFERENCES' - bl_region_type = 'WINDOW' - bl_options = {'HIDE_HEADER'} - - @classmethod - def poll(cls, context): - userpref = context.user_preferences - return (userpref.active_section == 'PACKAGES') - - def draw(self, context): - wm = context.window_manager - # see comment in __init__.py - pm_settings = wm.package_manager_settings - layout = self.layout - - row = layout.row() - row.prop(pm_settings, "url") - row.operator(pkg_ops.PACKAGE_OT_fetch.bl_idname, text="Fetch") - - row = layout.row() - # just a demonstration - row.label(text="Last response: %s" % pkg_ops.PACKAGE_OT_fetch.last_response) - -def register(): - bpy.utils.register_class(USERPREF_PT_packages) - -def unregister(): - bpy.utils.unregister_class(USERPREF_PT_packages) diff --git a/subprocess_adapter.py b/subprocess_adapter.py new file mode 100644 index 0000000..2af0ba0 --- /dev/null +++ b/subprocess_adapter.py @@ -0,0 +1,50 @@ +import logging +from multiprocessing import Process, Pipe + +class SubprocessOperatorMixin: + timer = None + log = logging.getLogger("%s.SubprocessOperatorMixin" % __name__) + + # run once in invoke + def setup(self): + pass + # run on receipt of data from subprocess + def handle_response(self, resp): + pass + + def __init__(self): + self.pipe = Pipe() + self.subprocess = None + + def execute(self, context): + return self.invoke(context, None) + + def invoke(self, context, event): + self.subprocess.start() + # we have to explicitly close the end of the pipe we are NOT using, + # otherwise no exception will be generated when the other process closes its end. + self.pipe[1].close() + + wm = context.window_manager + wm.modal_handler_add(self) + self.timer = wm.event_timer_add(.01, context.window) + + self.setup() + + return {'RUNNING_MODAL'} + + def modal(self, context, event): + self.log.debug("polling") + try: + if self.pipe[0].poll(): + newdata = self.pipe[0].recv() + else: + newdata = None + except EOFError: + self.log.debug("done polling") + return {'FINISHED'} + + if newdata is not None: + self.handle_response(newdata) + return {'PASS_THROUGH'} +