From 6752108930d427bdbb6184378f9b0af9f4b50b1d Mon Sep 17 00:00:00 2001 From: Ellwood Zwovic Date: Sat, 22 Jul 2017 18:44:44 -0700 Subject: [PATCH] Improve metadata display --- package_manager/__init__.py | 204 ++++++++++++++++++---------------- package_manager/bpkg/types.py | 93 ++++++++++++++-- 2 files changed, 193 insertions(+), 104 deletions(-) diff --git a/package_manager/__init__.py b/package_manager/__init__.py index 7342460..ff5d1fd 100644 --- a/package_manager/__init__.py +++ b/package_manager/__init__.py @@ -14,12 +14,13 @@ bl_info = { } import logging +log = logging.getLogger(__name__) if 'bpy' in locals(): import importlib + log.debug("Reloading") subproc = importlib.reload(subproc) - bpkg = importlib.reload(bpkg) Package = bpkg.Package else: @@ -30,6 +31,32 @@ else: import bpy from collections import OrderedDict +class ConsolidatedPackage: + """ + Stores a grouping of different versions of the same package + """ + + log = logging.getLogger(__name__ + ".ConsolidatedPackage") + + def __init__(self, pkg=None): + self.versions = [] + self.expanded = False + self.installed = False + + if pkg is not None: + self.add_version(pkg) + + def get_latest_version(self) -> bpkg.Package: + """Get package with highest version number""" + return self.versions[0] # this is always sorted with the highest on top + + def add_version(self, pkg: Package): + self.versions.append(pkg) + self.versions.sort(key=lambda v: v.version, reverse=True) + + def __iter__(self): + return (pkg for pkg in self.versions) + class SubprocMixin: """Mix-in class for things that need to be run in a subprocess.""" @@ -565,97 +592,72 @@ class USERPREF_PT_packages(bpy.types.Panel): return startswith + contains - def draw_package(pkg: ConsolidatedPackage, layout: bpy.types.UILayout):# {{{ + def draw_package(metapkg: ConsolidatedPackage, layout: bpy.types.UILayout):# {{{ """Draws the given package""" - pkgbox = layout.box() - spl = pkgbox.split(.8) - left = spl.row(align=True) - blinfo = pkg.versions[0].bl_info - - # for install/uninstall buttons - right = spl.row() - right.alignment = 'RIGHT' - right.scale_y = 1.5 - - # for collapse/expand button - left.operator( - WM_OT_package_toggle_expand.bl_idname, - icon='TRIA_DOWN' if pkg.expanded else 'TRIA_RIGHT', - emboss=False, - ).package_name=blinfo['name'] - - # for metadata - leftcol = left.column(align=True) def collapsed(): + """Draw collapsed version of package layout""" lr1 = leftcol.row() lr2 = leftcol.row() - lr1.label(text=blinfo.get('name', "")) - lr2.label(text=blinfo.get('description', "")) - lr2.enabled = False #Give name more visual weight + if pkg.name: + lr1.label(text=pkg.name) + if pkg.description: + lr2.label(text=pkg.description) + lr2.enabled = False #Give name more visual weight - latest_pkg = pkg.get_latest_version() - if latest_pkg.installed: - if latest_pkg.url: - right.operator(PACKAGE_OT_uninstall.bl_idname, - text="Uninstall").package_name=latest_pkg.name - else: - right.label("Installed") - else: - if latest_pkg.url: - right.operator(PACKAGE_OT_install.bl_idname, - text="Install").package_url=pkg.versions[0].url - else: - right.label("Not installed, but no URL?") def expanded(): - row1 = leftcol.row() - row1.label(blinfo.get('name'), "") - - def string_version(version_number) -> str: - """Take version number as an iterable and format it as a string""" + """Draw expanded version of package layout""" + def fmt_version(version_number: tuple) -> str: + """Take version number as a tuple and format it as a string""" vstr = str(version_number[0]) for component in version_number[1:]: vstr += "." + str(component) return vstr - if blinfo.get('description'): + row1 = leftcol.row() + row1.label(pkg.name) + + if pkg.description: row2 = leftcol.row() - row2.label(blinfo['description']) + row2.label(pkg.description) # row2.scale_y = 1.2 - if blinfo.get('version'): - spl = leftcol.row().split(.15) - spl.label("Version:") - spl.label(string_version(blinfo['version'])) - - def draw_metadatum(key: str, value: str, layout: bpy.types.UILayout): + def draw_metadatum(label: str, value: str, layout: bpy.types.UILayout): """Draw the given key value pair in a new row in given layout container""" row = layout.row() row.scale_y = .8 spl = row.split(.15) - spl.label("{}:".format(key)) + spl.label("{}:".format(label)) spl.label(value) - for prop in ( - # "description", - "author", - "category", - # "version", - # "blender", - "location", - "warning", - "support", - # "wiki_url", - # "tracker_url", - ): - if blinfo.get(prop): - row = leftcol.row() - row.scale_y = .8 - spl = row.split(.15) - spl.label("{}:".format(prop.title())) - spl.label(str(blinfo[prop])) + # don't compare against None here; we don't want to display empty arrays/strings either + if pkg.location: + draw_metadatum("Location", pkg.location, leftcol) + if pkg.version: + draw_metadatum("Version", fmt_version(pkg.version), leftcol) + # if pkg.blender: + # draw_metadatum("Compatible blender version", fmt_version(pkg.blender), leftcol) + if pkg.category: + draw_metadatum("Category", pkg.category, leftcol) + if pkg.author: + draw_metadatum("Author", pkg.author, leftcol) + if pkg.support: + draw_metadatum("Support level", pkg.support.title(), leftcol) + if pkg.warning: + draw_metadatum("Warning", pkg.warning, leftcol) + + if pkg.wiki_url or pkg.tracker_url: + padrow = leftcol.row() + padrow.label() + padrow.scale_y = .5 + urlrow = leftcol.row() + urlrow.alignment = 'LEFT' + if pkg.wiki_url: + urlrow.operator("wm.url_open", text="Documentation", icon='HELP').url=pkg.wiki_url + if pkg.tracker_url: + urlrow.operator("wm.url_open", text="Report a Bug", icon='URL').url=pkg.tracker_url def draw_version(layout: bpy.types.UILayout, pkg: Package): """Draw version of package""" @@ -664,7 +666,7 @@ class USERPREF_PT_packages(bpy.types.Panel): right = spl.column() right.alignment = 'RIGHT' - left.label(text=string_version(pkg.version)) + left.label(text=fmt_version(pkg.version)) if pkg.repository is not None: draw_metadatum("Repository", pkg.repository, left) @@ -674,16 +676,49 @@ class USERPREF_PT_packages(bpy.types.Panel): draw_metadatum("Installed to", str(pkg.installed_location), left) - if len(pkg.versions) > 1: + if len(metapkg.versions) > 1: row = pkgbox.row() row.label(text="There are multiple providers of this package:") - for version in pkg.versions: + for version in metapkg.versions: # row = pkgbox.row() subvbox = pkgbox.box() draw_version(subvbox, version) + pkg = metapkg.get_latest_version() - if pkg.expanded: + pkgbox = layout.box() + spl = pkgbox.split(.8) + left = spl.row(align=True) + + # for install/uninstall buttons + right = spl.row() + right.alignment = 'RIGHT' + right.scale_y = 1.5 + + # for collapse/expand button + left.operator( + WM_OT_package_toggle_expand.bl_idname, + icon='TRIA_DOWN' if metapkg.expanded else 'TRIA_RIGHT', + emboss=False, + ).package_name=pkg.name + + # for metadata + leftcol = left.column(align=True) + + if pkg.installed: + if pkg.url: + right.operator(PACKAGE_OT_uninstall.bl_idname, + text="Uninstall").package_name=pkg.name + else: + right.label("Installed") + else: + if pkg.url: + right.operator(PACKAGE_OT_install.bl_idname, + text="Install").package_url=pkg.url + else: + right.label("Not installed, but no URL?") + + if metapkg.expanded: expanded() else: collapsed()# }}} @@ -725,29 +760,6 @@ class USERPREF_PT_packages(bpy.types.Panel): row = pkgzone.row() draw_package(USERPREF_PT_packages.all_packages[pkgname], row) -class ConsolidatedPackage: - """ - Stores a grouping of different versions of the same packages, - and view-specific data used for drawing - """ - def __init__(self, pkg=None): - self.versions = [] - self.expanded = False - self.installed = False - - if pkg is not None: - self.add_version(pkg) - - def get_latest_version(self) -> Package: - """Get package with highest version number""" - return self.versions[0] # this is always sorted with the highest on top - - def add_version(self, pkg: Package): - self.versions.append(pkg) - self.versions.sort(key=lambda v: v.version, reverse=True) - - def __iter__(self): - return (pkg for pkg in self.versions) class WM_OT_package_toggle_expand(bpy.types.Operator): bl_idname = "wm.package_toggle_expand" diff --git a/package_manager/bpkg/types.py b/package_manager/bpkg/types.py index a04c233..da539b8 100644 --- a/package_manager/bpkg/types.py +++ b/package_manager/bpkg/types.py @@ -17,6 +17,7 @@ class Package: self.installed = False self.repository = None + self.installed_location = None def to_dict(self) -> dict: """ @@ -39,21 +40,96 @@ class Package: if package_dict.get(attr) is not None: setattr(self, attr, package_dict[attr]) - #bl_info convenience getters + # bl_info convenience getters + # required fields @property def name(self) -> str: """Get name from bl_info""" - return self.bl_info['name'] - - @property - def description(self) -> str: - """Get description from bl_info""" - return self.bl_info['description'] + try: + return self.bl_info['name'] + except KeyError: + return None @property def version(self) -> tuple: """Get version from bl_info""" - return tuple(self.bl_info['version']) + try: + return tuple(self.bl_info['version']) + except KeyError: + return None + + @property + def blender(self) -> tuple: + """Get blender from bl_info""" + try: + return self.bl_info['blender'] + except KeyError: + return None + + # optional fields + @property + def description(self) -> str: + """Get description from bl_info""" + try: + return self.bl_info['description'] + except KeyError: + return None + + @property + def author(self) -> str: + """Get author from bl_info""" + try: + return self.bl_info['author'] + except KeyError: + return None + + @property + def category(self) -> str: + """Get category from bl_info""" + try: + return self.bl_info['category'] + except KeyError: + return None + + @property + def location(self) -> str: + """Get location from bl_info""" + try: + return self.bl_info['location'] + except KeyError: + return None + + @property + def support(self) -> str: + """Get support from bl_info""" + try: + return self.bl_info['support'] + except KeyError: + return None + + @property + def warning(self) -> str: + """Get warning from bl_info""" + try: + return self.bl_info['warning'] + except KeyError: + return None + + @property + def wiki_url(self) -> str: + """Get wiki_url from bl_info""" + try: + return self.bl_info['wiki_url'] + except KeyError: + return None + + @property + def tracker_url(self) -> str: + """Get tracker_url from bl_info""" + try: + return self.bl_info['tracker_url'] + except KeyError: + return None # @classmethod # def from_dict(cls, package_dict: dict): @@ -83,6 +159,7 @@ class Package: pkg = cls() pkg.files = [filepath.name] pkg.installed_location = str(filepath) + try: pkg.bl_info = module.bl_info except AttributeError as err: