From b395338d5d67268e4eaab3ed22effe27d9757b74 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 20 Dec 2012 13:41:47 +0100 Subject: [PATCH 1/3] Add customizable icons to tag browser --- src/calibre/gui2/tag_browser/model.py | 30 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index c39026859a..18d3739545 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -8,7 +8,7 @@ __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import traceback, cPickle, copy +import traceback, cPickle, copy, os from PyQt4.Qt import (QAbstractItemModel, QIcon, QVariant, QFont, Qt, QMimeData, QModelIndex, pyqtSignal, QObject) @@ -213,6 +213,7 @@ class TagsModel(QAbstractItemModel): # {{{ for key in category_icon_map: iconmap[key] = QIcon(I(category_icon_map[key])) self.category_icon_map = TagsIcons(iconmap) + self.category_custom_icons = dict() self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags'] self.icon_state_map = [None, QIcon(I('plus.png')), QIcon(I('plusplus.png')), QIcon(I('minus.png')), QIcon(I('minusminus.png'))] @@ -304,13 +305,24 @@ class TagsModel(QAbstractItemModel): # {{{ continue is_gst = False if key.startswith('@') and key[1:] in gst: - tt = _(u'The grouped search term name is "{0}"').format(key[1:]) + tt = _(u'The grouped search term name is "{0}"').format(key) is_gst = True elif key == 'news': tt = '' else: tt = _(u'The lookup/search name is "{0}"').format(key) + # Get any customized icons. Done here to account for columns + # coming and going. Should be done only once per model instantiation + if self.category_custom_icons.get(key, None) is None: + icon = QIcon(I('tbci_' + key)) + if len(icon.availableSizes()) > 0: + self.category_custom_icons[key] = icon + else: + self.category_custom_icons[key] = \ + self.category_icon_map['gst'] if is_gst else \ + self.category_icon_map[key] + if key.startswith('@'): path_parts = [p for p in key.split('.')] path = '' @@ -319,14 +331,12 @@ class TagsModel(QAbstractItemModel): # {{{ for i,p in enumerate(path_parts): path += p if path not in category_node_map: - icon = self.category_icon_map['gst'] if is_gst else \ - self.category_icon_map[key] node = self.create_node(parent=last_category_node, - data=p[1:] if i == 0 else p, - category_icon=icon, - tooltip=tt if path == key else path, - category_key=path, - icon_map=self.icon_state_map) + data=p[1:] if i == 0 else p, + category_icon=self.category_custom_icons[key], + tooltip=tt if path == key else path, + category_key=path, + icon_map=self.icon_state_map) last_category_node = node category_node_map[path] = node self.category_nodes.append(node) @@ -343,7 +353,7 @@ class TagsModel(QAbstractItemModel): # {{{ else: node = self.create_node(parent=self.root_item, data=self.categories[key], - category_icon=self.category_icon_map[key], + category_icon=self.category_custom_icons[key], tooltip=tt, category_key=key, icon_map=self.icon_state_map) node.is_gst = False From 7dd6fbd80e3758a50ac6261197c86f35acc7e695 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 20 Dec 2012 13:55:21 +0100 Subject: [PATCH 2/3] Make leaf nodes in tag browser have custom icons --- src/calibre/gui2/tag_browser/model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index 18d3739545..de82c76abb 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -514,6 +514,7 @@ class TagsModel(QAbstractItemModel): # {{{ if (not tag.is_hierarchical) and (in_uc or (fm['is_custom'] and fm['display'].get('is_names', False)) or not category_is_hierarchical or len(components) == 1): + tag.icon = self.category_custom_icons[key] n = self.create_node(parent=node_parent, data=tag, tooltip=tt, icon_map=self.icon_state_map) if tag.id_set is not None: @@ -550,6 +551,7 @@ class TagsModel(QAbstractItemModel): # {{{ t.is_hierarchical = \ '5state' if t.category != 'search' else '3state' t.name = comp + t.icon = self.category_custom_icons[key] node_parent = self.create_node(parent=node_parent, data=t, tooltip=tt, icon_map=self.icon_state_map) child_map[(comp,tag.category)] = node_parent From a3e9634e38ae8b91cb363905a7a0440dc2a6d4f7 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 20 Dec 2012 17:26:58 +0100 Subject: [PATCH 3/3] Change icon stuff to use the context menu --- src/calibre/gui2/__init__.py | 1 + src/calibre/gui2/tag_browser/model.py | 25 ++++++++++++++++--------- src/calibre/gui2/tag_browser/view.py | 26 +++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 7c2baad9fa..9dbf769511 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -106,6 +106,7 @@ gprefs.defaults['tag_browser_old_look'] = False gprefs.defaults['book_list_tooltips'] = True gprefs.defaults['bd_show_cover'] = True gprefs.defaults['bd_overlay_cover_size'] = False +gprefs.defaults['tags_browser_category_icons'] = {} # }}} NONE = QVariant() #: Null value to return from the data function of item models diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index de82c76abb..f7ec97e92a 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -214,6 +214,10 @@ class TagsModel(QAbstractItemModel): # {{{ iconmap[key] = QIcon(I(category_icon_map[key])) self.category_icon_map = TagsIcons(iconmap) self.category_custom_icons = dict() + for k,v in gprefs['tags_browser_category_icons'].items(): + icon = QIcon(v) + if len(icon.availableSizes()) > 0: + self.category_custom_icons[k] = icon self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags'] self.icon_state_map = [None, QIcon(I('plus.png')), QIcon(I('plusplus.png')), QIcon(I('minus.png')), QIcon(I('minusminus.png'))] @@ -232,6 +236,16 @@ class TagsModel(QAbstractItemModel): # {{{ def gui_parent(self): return QObject.parent(self) + def set_custom_category_icon(self, key, path): + d = gprefs['tags_browser_category_icons'] + if path: + d[key] = path + self.category_custom_icons[key] = QIcon(path) + else: + del d[key] + del self.category_custom_icons[key] + gprefs['tags_browser_category_icons'] = d + def reread_collapse_model(self, state_map, rebuild=True): if gprefs['tags_browser_collapse_at'] == 0: self.collapse_model = 'disable' @@ -312,16 +326,9 @@ class TagsModel(QAbstractItemModel): # {{{ else: tt = _(u'The lookup/search name is "{0}"').format(key) - # Get any customized icons. Done here to account for columns - # coming and going. Should be done only once per model instantiation if self.category_custom_icons.get(key, None) is None: - icon = QIcon(I('tbci_' + key)) - if len(icon.availableSizes()) > 0: - self.category_custom_icons[key] = icon - else: - self.category_custom_icons[key] = \ - self.category_icon_map['gst'] if is_gst else \ - self.category_icon_map[key] + self.category_custom_icons[key] = \ + self.category_icon_map['gst'] if is_gst else self.category_icon_map[key] if key.startswith('@'): path_parts = [p for p in key.split('.')] diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py index a9882a1a35..49670dca83 100644 --- a/src/calibre/gui2/tag_browser/view.py +++ b/src/calibre/gui2/tag_browser/view.py @@ -13,11 +13,12 @@ from itertools import izip from PyQt4.Qt import (QStyledItemDelegate, Qt, QTreeView, pyqtSignal, QSize, QIcon, QApplication, QMenu, QPoint, QModelIndex, QToolTip, QCursor, - QDrag) + QDrag, QFileDialog) from calibre.gui2.tag_browser.model import (TagTreeItem, TAG_SEARCH_STATES, TagsModel) from calibre.gui2 import config, gprefs +from calibre.utils.resources import get_image_path from calibre.utils.search_query_parser import saved_searches from calibre.utils.icu import sort_key @@ -296,6 +297,22 @@ class TagsView(QTreeView): # {{{ if not action: return try: + if action == 'set_icon': + try: + path = QFileDialog.getOpenFileName(self, 'New Icon', get_image_path(None), + filter='*.png *.jpg') + if path: + self._model.set_custom_category_icon(key, unicode(path)) + self.recount() + except: + import traceback + traceback.print_exc() + return + if action == 'clear_icon': + self._model.set_custom_category_icon(key, None) + self.recount() + return + if action == 'edit_item': self.edit(index) return @@ -533,6 +550,12 @@ class TagsView(QTreeView): # {{{ partial(self.context_menu_handler, action='manage_searches', category=tag.name if tag else None)) + self.context_menu.addSeparator() + self.context_menu.addAction(_('Change category icon'), + partial(self.context_menu_handler, action='set_icon', key=key)) + self.context_menu.addAction(_('Restore default icon'), + partial(self.context_menu_handler, action='clear_icon', key=key)) + # Always show the user categories editor self.context_menu.addSeparator() if key.startswith('@') and \ @@ -551,6 +574,7 @@ class TagsView(QTreeView): # {{{ self.context_menu.addAction(_('Show all categories'), partial(self.context_menu_handler, action='defaults')) + m = self.context_menu.addMenu(_('Change sub-categorization scheme')) da = m.addAction(_('Disable'), partial(self.context_menu_handler, action='categorization', category='disable'))