Fix #2573 (Crash when "Click to browse books by Tags")

This commit is contained in:
Kovid Goyal 2009-06-11 10:25:22 -07:00
commit 859fd352f1
4 changed files with 155 additions and 175 deletions

View File

@ -696,7 +696,7 @@
<customwidget> <customwidget>
<class>TagsView</class> <class>TagsView</class>
<extends>QTreeView</extends> <extends>QTreeView</extends>
<header>tags.h</header> <header>calibre/gui2/tag_view.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources>

View File

@ -0,0 +1,151 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
Browsing book collection by tags.
'''
from PyQt4.Qt import QStandardItemModel, Qt, QTreeView, QStandardItem, \
QFont, SIGNAL, QSize, QIcon, QPoint, QPixmap
from calibre.gui2 import config
class TagsView(QTreeView):
def __init__(self, *args):
QTreeView.__init__(self, *args)
self.setUniformRowHeights(True)
self.setCursor(Qt.PointingHandCursor)
self.setIconSize(QSize(30, 30))
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())
def recount(self, *args):
ci = self.currentIndex()
if not ci.isValid():
ci = self.indexAt(QPoint(10, 10))
self.model().refresh()
if ci.isValid():
self.scrollTo(ci, QTreeView.PositionAtTop)
class CategoryItem(QStandardItem):
def __init__(self, category, display_text, tags, icon, font, icon_map):
self.category = category
self.tags = tags
QStandardItem.__init__(self, icon, display_text)
self.setFont(font)
self.setSelectable(False)
self.setSizeHint(QSize(100, 40))
self.setEditable(False)
for tag in tags:
self.appendRow(TagItem(tag, icon_map))
class TagItem(QStandardItem):
def __init__(self, tag, icon_map):
self.icon_map = icon_map
self.tag = tag
QStandardItem.__init__(self, tag.as_string())
self.set_icon()
self.setEditable(False)
self.setSelectable(False)
def toggle(self):
self.tag.state = (self.tag.state + 1)%3
self.set_icon()
def set_icon(self):
self.setIcon(self.icon_map[self.tag.state])
class TagsModel(QStandardItemModel):
categories = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('Tags')]
row_map = ['author', 'series', 'format', 'publisher', 'news', 'tag']
def __init__(self, db):
self.cmap = tuple(map(QIcon, [':/images/user_profile.svg',
':/images/series.svg', ':/images/book.svg', ':/images/publisher.png',
':/images/news.svg', ':/images/tags.svg']))
p = QPixmap(30, 30)
p.fill(Qt.transparent)
self.icon_map = [QIcon(p), QIcon(':/images/plus.svg'),
QIcon(':/images/minus.svg')]
QStandardItemModel.__init__(self)
self.db = db
self.ignore_next_search = False
self._data = {}
self.bold_font = QFont()
self.bold_font.setBold(True)
self.refresh()
self.db.add_listener(self.database_changed)
def database_changed(self, event, ids):
self.refresh()
def refresh(self):
old_data = self._data
self._data = self.db.get_categories(config['sort_by_popularity'])
for key in old_data.keys():
for tag in old_data[key]:
try:
index = self._data[key].index(tag)
if index > -1:
self._data[key][index].state = tag.state
except:
continue
self.clear()
root = self.invisibleRootItem()
for r, category in enumerate(self.row_map):
tags = self._data.get(category, [])
root.appendRow(CategoryItem(category, self.categories[r],
self._data[category], self.cmap[r], self.bold_font, self.icon_map))
#self.reset()
def reinit(self, *args, **kwargs):
if not self.ignore_next_search:
for category in self._data.values():
for tag in category:
tag.state = 0
self.reset()
self.ignore_next_search = False
def toggle(self, index):
if index.parent().isValid():
category = self.row_map[index.parent().row()]
tag = self._data[category][index.row()]
self.invisibleRootItem().child(index.parent().row()).child(index.row()).toggle()
self.ignore_next_search = True
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index)
return True
return False
def tokens(self):
ans = []
for key in self.row_map:
for tag in self._data[key]:
category = key if key != 'news' else 'tag'
if tag.state > 0:
prefix = ' not ' if tag.state == 2 else ''
ans.append('%s%s:"%s"'%(prefix, category, tag))
return ans

View File

@ -1,173 +0,0 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
Browsing book collection by tags.
'''
from PyQt4.Qt import QAbstractItemModel, Qt, QVariant, QTreeView, QModelIndex, \
QFont, SIGNAL, QSize, QColor, QIcon, QPoint
from calibre.gui2 import config
NONE = QVariant()
class TagsView(QTreeView):
def __init__(self, *args):
QTreeView.__init__(self, *args)
self.setUniformRowHeights(True)
self.setCursor(Qt.PointingHandCursor)
self.setIconSize(QSize(30, 30))
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())
def recount(self, *args):
ci = self.currentIndex()
if not ci.isValid():
ci = self.indexAt(QPoint(10, 10))
self.model().refresh()
if ci.isValid():
self.scrollTo(ci, QTreeView.PositionAtTop)
class TagsModel(QAbstractItemModel):
categories = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('Tags')]
row_map = {0: 'author', 1:'series', 2:'format', 3:'publisher', 4:'news', 5:'tag'}
def __init__(self, db):
QAbstractItemModel.__init__(self)
self.db = db
self.ignore_next_search = False
self._data = {}
self.refresh()
self.bold_font = QFont()
self.bold_font.setBold(True)
self.bold_font = QVariant(self.bold_font)
self.status_map = [QColor(200,200,200, 0), QIcon(':/images/plus.svg'), QIcon(':/images/minus.svg')]
self.status_map = list(map(QVariant, self.status_map))
self.cmap = [QIcon(':/images/user_profile.svg'), QIcon(':/images/series.svg'), QIcon(':/images/book.svg'), QIcon(':/images/publisher.png'), QIcon(':/images/news.svg'), QIcon(':/images/tags.svg')]
self.cmap = list(map(QVariant, self.cmap))
self.db.add_listener(self.database_changed)
def database_changed(self, event, ids):
self.refresh()
def refresh(self):
old_data = self._data
self._data = self.db.get_categories(config['sort_by_popularity'])
for key in old_data.keys():
for tag in old_data[key]:
try:
index = self._data[key].index(tag)
if index > -1:
self._data[key][index].state = tag.state
except:
continue
self.reset()
def reinit(self, *args, **kwargs):
if not self.ignore_next_search:
for category in self._data.values():
for tag in category:
tag.state = 0
self.reset()
self.ignore_next_search = False
def toggle(self, index):
if index.parent().isValid():
category = self.row_map[index.parent().row()]
tag = self._data[category][index.row()]
tag.state = (tag.state + 1)%3
self.ignore_next_search = True
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index)
return True
return False
def tokens(self):
ans = []
for key in self.row_map.values():
for tag in self._data[key]:
category = key if key != 'news' else 'tag'
if tag.state > 0:
prefix = ' not ' if tag.state == 2 else ''
ans.append('%s%s:"%s"'%(prefix, category, tag))
return ans
def index(self, row, col, parent=QModelIndex()):
if parent.isValid():
if parent.parent().isValid(): # parent is a tag
return QModelIndex()
try:
category = self.row_map[parent.row()]
except KeyError:
return QModelIndex()
if col == 0 and row < len(self._data[category]):
return self.createIndex(row, col, parent.row())
return QModelIndex()
if col == 0 and row < len(self.categories):
return self.createIndex(row, col, -1)
return QModelIndex()
def parent(self, index):
if not index.isValid() or index.internalId() < 0:
return QModelIndex()
return self.createIndex(index.internalId(), 0, -1)
def rowCount(self, parent):
if not parent or not parent.isValid():
return len(self.categories)
if not parent.parent().isValid():
return len(self._data[self.row_map[parent.row()]])
return 0
def columnCount(self, parent):
return 1
def flags(self, index):
if not index.isValid():
return Qt.NoItemFlags
return Qt.ItemIsEnabled
def category_data(self, index, role):
if role == Qt.DisplayRole:
row = index.row()
return QVariant(self.categories[row])
if role == Qt.FontRole:
return self.bold_font
if role == Qt.SizeHintRole:
return QVariant(QSize(100, 40))
if role == Qt.DecorationRole:
return self.cmap[index.row()]
return NONE
def tag_data(self, index, role):
category = self.row_map[index.parent().row()]
if role == Qt.DisplayRole:
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
def data(self, index, role):
if not index.parent().isValid():
return self.category_data(index, role)
if not index.parent().parent().isValid():
return self.tag_data(index, role)
return NONE

View File

@ -334,7 +334,9 @@ def post_install():
os.chdir(config_dir) os.chdir(config_dir)
for f in os.listdir('.'): for f in os.listdir('.'):
if os.stat(f).st_uid == 0: if os.stat(f).st_uid == 0:
os.unlink(f) os.rmdir(f) if os.path.isdir(f) else os.unlink(f)
if os.stat(config_dir).st_uid == 0:
os.rmdir(config_dir)
def binary_install(): def binary_install():
manifest = os.path.join(getattr(sys, 'frozen_path'), 'manifest') manifest = os.path.join(getattr(sys, 'frozen_path'), 'manifest')