diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 58b78cc00a..c976c3428f 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -37,6 +37,8 @@ def _config(): help=_('Notify when a new version is available')) c.add_opt('use_roman_numerals_for_series_number', default=True, help=_('Use Roman numerals for series number')) + c.add_opt('sort_by_popularity', default=False, + help=_('Sort tags list by popularity')) c.add_opt('cover_flow_queue_length', default=6, help=_('Number of covers to show in the cover browsing mode')) c.add_opt('LRF_conversion_defaults', default=[], diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index c698b16286..31f6b891a7 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -250,7 +250,8 @@ class Main(MainWindow, Ui_MainWindow): self.tags_view.setVisible(False) self.match_all.setVisible(False) self.match_any.setVisible(False) - self.tags_view.set_database(db, self.match_all) + self.popularity.setVisible(False) + self.tags_view.set_database(db, self.match_all, self.popularity) self.connect(self.tags_view, SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'), self.search.search_from_tokens) self.connect(self.status_bar.tag_view_button, SIGNAL('toggled(bool)'), self.toggle_tags_view) @@ -300,11 +301,13 @@ class Main(MainWindow, Ui_MainWindow): self.tags_view.setVisible(True) self.match_all.setVisible(True) self.match_any.setVisible(True) + self.popularity.setVisible(True) self.tags_view.setFocus(Qt.OtherFocusReason) else: self.tags_view.setVisible(False) self.match_all.setVisible(False) self.match_any.setVisible(False) + self.popularity.setVisible(False) def sync_cf_to_listview(self, index, *args): if not hasattr(index, 'row') and self.library_view.currentIndex().row() != index: diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index 59ce80884a..065b322b04 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -262,6 +262,13 @@ + + + + Sort by &popularity + + + diff --git a/src/calibre/gui2/tags.py b/src/calibre/gui2/tags.py index 2dba15b95d..dd8b3e076b 100644 --- a/src/calibre/gui2/tags.py +++ b/src/calibre/gui2/tags.py @@ -8,7 +8,7 @@ Browsing book collection by tags. ''' from PyQt4.Qt import QAbstractItemModel, Qt, QVariant, QTreeView, QModelIndex, \ QFont, SIGNAL, QSize, QColor, QIcon - +from calibre.gui2 import config NONE = QVariant() class TagsView(QTreeView): @@ -19,23 +19,24 @@ class TagsView(QTreeView): self.setCursor(Qt.PointingHandCursor) self.setIconSize(QSize(30, 30)) - def set_database(self, db, match_all): + def set_database(self, db, match_all, popularity): self._model = TagsModel(db) + self.popularity = popularity self.match_all = match_all self.setModel(self._model) self.connect(self, SIGNAL('clicked(QModelIndex)'), self.toggle) + self.popularity.setChecked(config['sort_by_popularity']) + self.connect(self.popularity, SIGNAL('stateChanged(int)'), self.sort_changed) + + def sort_changed(self, state): + config.set('sort_by_popularity', state == Qt.Checked) + self.model().refresh() def toggle(self, index): if self._model.toggle(index): self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'), self._model.tokens(), self.match_all.isChecked()) -class Tag(unicode): - - def __init__(self, name): - unicode.__init__(self, name) - self.state = 0 - class TagsModel(QAbstractItemModel): categories = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('Tags')] @@ -61,9 +62,9 @@ class TagsModel(QAbstractItemModel): def refresh(self): old_data = self._data - self._data = self.db.get_categories() + self._data = self.db.get_categories(config['sort_by_popularity']) for key in self._data: - self._data[key] = list(map(Tag, self._data[key])) + self._data[key] = self._data[key] for key in old_data.keys(): for tag in old_data[key]: try: @@ -152,7 +153,7 @@ class TagsModel(QAbstractItemModel): def tag_data(self, index, role): category = self.row_map[index.parent().row()] if role == Qt.DisplayRole: - return QVariant(self._data[category][index.row()]) + return QVariant(self._data[category][index.row()].as_string()) if role == Qt.DecorationRole: return self.status_map[self._data[category][index.row()].state] return NONE diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index a5b0c83a41..b8ad998b3a 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -326,6 +326,16 @@ class ResultCache(object): ans = (no_tags + ans) if order == 'ASC' else (ans + no_tags) return ans +class Tag(unicode): + + def __init__(self, name): + unicode.__init__(self, name) + self.count = 0 + self.state = 0 + + def as_string(self): + return u'[%d] %s'%(self.count, self) + class LibraryDatabase2(LibraryDatabase): ''' An ebook metadata database that stores references to ebook files on disk. @@ -683,7 +693,7 @@ class LibraryDatabase2(LibraryDatabase): self.conn.execute(st%dict(ltable='series', table='series', ltable_col='series')) self.conn.commit() - def get_categories(self): + def get_categories(self, sort_on_count=False): categories = {} def get(name, category, field='name'): ans = self.conn.execute('SELECT DISTINCT %s FROM %s'%(field, name)).fetchall() @@ -691,9 +701,23 @@ class LibraryDatabase2(LibraryDatabase): try: ans.remove('') except ValueError: pass - ans.sort() - categories[category] = ans - for x in (('authors', 'author'), ('tags', 'tag'), ('publishers', 'publisher'), ('series', 'series')): + categories[category] = list(map(Tag, ans)) + tags = categories[category] + if name != 'data': + for tag in tags: + id = self.conn.execute('SELECT id FROM %s WHERE %s=?'%(name, field), (tag,)).fetchone() + if id: + id = id[0] + tag.id = id + for tag in tags: + if tag.id is not None: + tag.count = self.conn.execute('SELECT COUNT(id) FROM books_%s_link WHERE %s=?'%(name, category), (tag.id,)).fetchone()[0] + else: + for tag in tags: + tag.count = self.conn.execute('SELECT COUNT(format) FROM data WHERE format=?', (tag,)).fetchone()[0] + tags.sort(reverse=sort_on_count, cmp=(lambda x,y:cmp(x.count,y.count)) if sort_on_count else cmp) + for x in (('authors', 'author'), ('tags', 'tag'), ('publishers', 'publisher'), + ('series', 'series')): get(*x) get('data', 'format', 'format') return categories