From 8a33f488747d63c97cf7df33967df8a14d851391 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 11 Jan 2022 20:14:54 +0530 Subject: [PATCH] Support themeing for plugin icons --- manual/creating_plugins.rst | 24 +++++++++----- src/calibre/customize/zipplugin.py | 50 ++++++++++++++++++------------ 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/manual/creating_plugins.rst b/manual/creating_plugins.rst index bf1bd1b43b..4b0a944431 100644 --- a/manual/creating_plugins.rst +++ b/manual/creating_plugins.rst @@ -138,15 +138,23 @@ Getting resources from the plugin ZIP file calibre's plugin loading system defines a couple of built-in functions that allow you to conveniently get files from the plugin ZIP file. **get_resources(name_or_list_of_names)** - This function should be called with a list of paths to files inside the ZIP file. For example to access the file icon.png in - the folder images in the ZIP file, you would use: ``images/icon.png``. Always use a forward slash as the path separator, - even on Windows. When you pass in a single name, the function will return the raw bytes of that file or None if the name - was not found in the ZIP file. If you pass in more than one name then it returns a dict mapping the names to bytes. - If a name is not found, it will not be present in the returned dict. + This function should be called with a list of paths to files inside the + ZIP file. For example to access the file :file:`icon.png` in the folder + images in the ZIP file, you would use: ``images/icon.png``. Always use + a forward slash as the path separator, even on Windows. When you pass + in a single name, the function will return the raw bytes of that file + or None if the name was not found in the ZIP file. If you pass in more + than one name then it returns a dict mapping the names to bytes. If a + name is not found, it will not be present in the returned dict. + + **get_icons(name_or_list_of_names, plugin_name='')** + A wrapper for get_resources() that creates QIcon objects + from the raw bytes returned by get_resources. If a name is not found + in the ZIP file the corresponding QIcon will be null. In order to + support icon theme-ing, pass in the human friendly name of your plugin + as ``plugin_name``. If the user is using an icon theme with icons for + you plugin, they will be loaded preferentially. - **get_icons(name_or_list_of_names)** - A convenience wrapper for get_resources() that creates QIcon objects from the raw bytes returned by get_resources. - If a name is not found in the ZIP file the corresponding QIcon will be null. Enabling user configuration of your plugin ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/calibre/customize/zipplugin.py b/src/calibre/customize/zipplugin.py index 26bbe63001..5e53071bc3 100644 --- a/src/calibre/customize/zipplugin.py +++ b/src/calibre/customize/zipplugin.py @@ -57,38 +57,50 @@ def get_resources(zfp, name_or_list_of_names): return ans -def get_icons(zfp, name_or_list_of_names): +def get_icons(zfp, name_or_list_of_names, plugin_name=''): ''' Load icons from the plugin zip file :param name_or_list_of_names: List of paths to resources in the zip file using / as separator, or a single path + :param plugin_name: The human friendly name of the plugin, used to load icons from + the current theme, if present. + :return: A dictionary of the form ``{name : QIcon}``. Any names that were not found in the zip file will be null QIcons. If a single path is passed in the return value will be A QIcon. ''' from qt.core import QIcon, QPixmap - names = name_or_list_of_names - ans = get_resources(zfp, names) - if isinstance(names, string_or_bytes): - names = [names] - if ans is None: - ans = {} - if isinstance(ans, string_or_bytes): - ans = dict([(names[0], ans)]) + ans = {} + namelist = [name_or_list_of_names] if isinstance(name_or_list_of_names, string_or_bytes) else name_or_list_of_names + failed = set() + if plugin_name: + for name in namelist: + q = QIcon.ic(f'{plugin_name}/{name}') + if q.isNull(): + failed.add(name) + else: + ans[name] = q + else: + failed = set(namelist) + if failed: + from_zfp = get_resources(zfp, list(failed)) + if from_zfp is None: + from_zfp = {} + elif isinstance(from_zfp, string_or_bytes): + from_zfp = {namelist[0]: from_zfp} - ians = {} - for name in names: - p = QPixmap() - raw = ans.get(name, None) - if raw: - p.loadFromData(raw) - ians[name] = QIcon(p) - if len(names) == 1: - ians = ians.pop(names[0]) - return ians + for name in failed: + p = QPixmap() + raw = from_zfp.get(name) + if raw: + p.loadFromData(raw) + ans[name] = QIcon(p) + if len(namelist) == 1 and ans: + ans = ans.pop(namelist[0]) + return ans _translations_cache = {}