From 4cc865cd62debc5f263362b27710e859af33999c Mon Sep 17 00:00:00 2001 From: Ellwood Zwovic Date: Mon, 24 Jul 2017 16:44:35 -0700 Subject: [PATCH] Add repository list to package panel Actually adding multiple repositories is disallowed for now --- package_manager/__init__.py | 119 +++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/package_manager/__init__.py b/package_manager/__init__.py index b961ab7..e9ae92a 100644 --- a/package_manager/__init__.py +++ b/package_manager/__init__.py @@ -75,7 +75,7 @@ class SubprocMixin: def quit(self): """Signals the state machine to stop this operator from running.""" - + self.cancel(bpy.context) self._state = 'QUIT' def invoke(self, context, event): @@ -87,6 +87,7 @@ class SubprocMixin: # The subprocess should just be terminated when Blender quits. Without this, # Blender would hang while closing, until the subprocess terminates itself. + # TODO: Perhaps it would be better to fork when blender exits? self.process = self.create_subprocess() self.process.daemon = True self.process.start() @@ -364,7 +365,19 @@ class PACKAGE_OT_refresh_repositories(SubprocMixin, bpy.types.Operator): bl_description = 'Check repositories for new and updated packages' bl_options = {'REGISTER'} - log = logging.getLogger(__name__ + ".PACKAGE_OT_refresh") + log = logging.getLogger(__name__ + ".PACKAGE_OT_refresh_repositories") + _running = False + + def invoke(self, context, event): + PACKAGE_OT_refresh_repositories._running = True + return super().invoke(context, event) + + @classmethod + def poll(cls, context): + return not cls._running + + def cancel(self, context): + PACKAGE_OT_refresh_repositories._running = False def create_subprocess(self): """Starts the download process. @@ -389,17 +402,7 @@ class PACKAGE_OT_refresh_repositories(SubprocMixin, bpy.types.Operator): import pathlib storage_path = pathlib.Path(bpy.utils.user_resource('CONFIG', 'packages', create=True)) - repository_url = bpy.context.user_preferences.addons[__package__].preferences.repository_url - - from urllib.parse import urlsplit, urlunsplit - - parsed_url = urlsplit(repository_url) - if not parsed_url.path.endswith("repo.json"): - if parsed_url.path.endswith('/'): - new_path = parsed_url.path + "repo.json" - else: - new_path = parsed_url.path + "/repo.json" - repository_url = urlunsplit((parsed_url.scheme, parsed_url.netloc, new_path, parsed_url.query, parsed_url.fragment)) + repository_url = bpy.context.user_preferences.addons[__package__].preferences.repositories[0].url proc = multiprocessing.Process(target=subproc.refresh, args=(self.pipe_subproc, storage_path, repository_url)) @@ -518,6 +521,63 @@ class PACKAGE_OT_load_repositories(SubprocMixin, bpy.types.Operator): self.log.error('Process died without telling us! Exit code was 0 though') self.report({'WARNING'}, 'Error downloading package, but process finished OK. This is weird.') +class RepositoryProperty(bpy.types.PropertyGroup): + url = bpy.props.StringProperty(name="URL") + status = bpy.props.EnumProperty(name="Status", items=[ + ("OK", "Okay", "FILE_TICK"), + ("NOTFOUND", "Not found", "ERROR"), + ("NOCONNECT", "Could not connect", "QUESTION"), + ]) + +class PACKAGE_UL_repositories(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + layout.alignment='LEFT' + if len(item.name) == 0: + layout.label(item['url']) + else: + layout.label(item.name) + +def parse_repository_url(url: str) -> str: + """Sanitize repository url""" + from urllib.parse import urlsplit, urlunsplit + parsed_url = urlsplit(url) + new_path = parsed_url.path.rstrip("repo.json") + return urlunsplit((parsed_url.scheme, parsed_url.netloc, new_path, parsed_url.query, parsed_url.fragment)) + +class PACKAGE_OT_add_repository(bpy.types.Operator): + bl_idname = "package.add_repository" + bl_label = "Add Repository" + + url = bpy.props.StringProperty(name="Repository URL") + + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_props_dialog(self) + + def execute(self, context): + prefs = context.user_preferences.addons[__package__].preferences + if len(prefs.repositories) > 0: + self.report({'ERROR'}, "Only one repository at a time is currently supported") + return {'CANCELLED'} + + if len(self.url) == 0: + self.report({'ERROR'}, "Repository URL not specified") + return {'CANCELLED'} + + repo = prefs.repositories.add() + repo.url = parse_repository_url(self.url) + + context.area.tag_redraw() + return {'FINISHED'} + +class PACKAGE_OT_remove_repository(bpy.types.Operator): + bl_idname = "package.remove_repository" + bl_label = "Remove Repository" + + def execute(self, context): + prefs = context.user_preferences.addons[__package__].preferences + prefs.repositories.remove(prefs.active_repository) + return {'FINISHED'} class USERPREF_PT_packages(bpy.types.Panel): bl_label = "Package Management" @@ -539,12 +599,25 @@ class USERPREF_PT_packages(bpy.types.Panel): def draw(self, context): layout = self.layout wm = context.window_manager + prefs = context.user_preferences.addons[__package__].preferences main = layout.row() - spl = main.split(.12) + spl = main.split(.22) sidebar = spl.column(align=True) pkgzone = spl.column() + sidebar.label("Repositories") + + row = sidebar.row() + row.template_list("PACKAGE_UL_repositories", "", prefs, "repositories", prefs, "active_repository") + col = row.column(align=True) + col.operator(PACKAGE_OT_add_repository.bl_idname, text="", icon='ZOOMIN') + col.operator(PACKAGE_OT_remove_repository.bl_idname, text="", icon='ZOOMOUT') + + sidebar.separator() + sidebar.operator(PACKAGE_OT_refresh_repositories.bl_idname) + + sidebar.separator() sidebar.label(text="Category") sidebar.prop(wm, "addon_filter", text="") @@ -800,6 +873,12 @@ class PackageManagerPreferences(bpy.types.AddonPreferences): name='Repository URL', description='Temporary repository URL') + repositories = bpy.props.CollectionProperty( + type=RepositoryProperty, + name="Repositories", + ) + active_repository = bpy.props.IntProperty() + def draw(self, context): layout = self.layout @@ -869,6 +948,12 @@ def register(): name="Install filter", default='AVAILABLE', ) + + bpy.utils.register_class(RepositoryProperty) + bpy.utils.register_class(PACKAGE_OT_add_repository) + bpy.utils.register_class(PACKAGE_OT_remove_repository) + bpy.utils.register_class(PACKAGE_UL_repositories) + bpy.utils.register_class(PackageManagerPreferences) @@ -883,4 +968,10 @@ def unregister(): bpy.utils.unregister_class(WM_OT_package_toggle_expand) del bpy.types.WindowManager.package_search del bpy.types.WindowManager.package_install_filter + + bpy.utils.unregister_class(RepositoryProperty) + bpy.utils.unregister_class(PACKAGE_OT_add_repository) + bpy.utils.unregister_class(PACKAGE_OT_remove_repository) + bpy.utils.unregister_class(PACKAGE_UL_repositories) + bpy.utils.unregister_class(PackageManagerPreferences)