Show ratings in the book details panel as stars. Also allow the user to change the alignment of the ratings column in the main books list. No longer display the stars in blue.

This commit is contained in:
Kovid Goyal 2012-02-27 15:52:24 +05:30
parent f090abb46b
commit 9423839eff
13 changed files with 256 additions and 86 deletions

152
imgsrc/calibreSymbols.spd Normal file
View File

@ -0,0 +1,152 @@
SplineFontDB: 3.0
FontName: calibreSymbols
FullName: calibre Symbols
FamilyName: calibre Symbols
Weight: Medium
Copyright: Created by Kovid Goyal with FontForge 2.0 (http://fontforge.sf.net)
UComments: "2012-2-27: Created."
Version: 001.000
ItalicAngle: 0
UnderlinePosition: -100
UnderlineWidth: 50
Ascent: 800
Descent: 200
LayerCount: 2
Layer: 0 0 "Back" 1
Layer: 1 0 "Fore" 0
NeedsXUIDChange: 1
XUID: [1021 913 325894820 11538708]
FSType: 0
OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 1
CreationTime: 1330331997
ModificationTime: 1330337167
OS2TypoAscent: 0
OS2TypoAOffset: 1
OS2TypoDescent: 0
OS2TypoDOffset: 1
OS2TypoLinegap: 90
OS2WinAscent: 0
OS2WinAOffset: 1
OS2WinDescent: 0
OS2WinDOffset: 1
HheadAscent: 0
HheadAOffset: 1
HheadDescent: 0
HheadDOffset: 1
MarkAttachClasses: 1
DEI: 91125
Encoding: UnicodeFull
UnicodeInterp: none
NameList: Adobe Glyph List
DisplaySize: -24
AntiAlias: 1
FitToEm: 1
WidthSeparation: 150
WinInfo: 0 75 22
BeginPrivate: 0
EndPrivate
BeginChars: 1114112 3
StartChar: uni2605
Encoding: 9733 9733 0
Width: 933
VWidth: 0
Flags: W
LayerCount: 2
Fore
SplineSet
544.1 344.853 m 1
723.713 360.062 l 2
774.129 364.181 799.969 366.241 801.229 366.241 c 0
816.984 366.241 824.862 359.429 824.862 345.803 c 0
824.862 340.416 823.287 336.218 820.136 333.207 c 0
816.984 330.197 792.878 314.274 747.817 285.438 c 2
596.566 188 l 1
693.461 -56.3096 l 2
694.722 -58.8447 695.353 -62.6465 695.353 -67.7168 c 0
695.353 -72.4697 693.619 -76.5898 690.152 -80.0742 c 0
686.687 -83.5605 682.905 -85.3027 678.81 -85.3027 c 0
675.028 -85.3027 671.089 -83.9561 666.991 -81.2637 c 0
662.896 -78.5693 640.681 -59.7949 600.348 -24.9385 c 2
466.11 91.9873 l 1
333.765 -23.0381 l 2
292.172 -59.1621 269.405 -78.5693 265.467 -81.2637 c 0
261.527 -83.9561 257.667 -85.3027 253.887 -85.3027 c 0
249.475 -85.3027 245.457 -83.4814 241.833 -79.8369 c 0
238.209 -76.1934 236.397 -72.1523 236.397 -67.7168 c 0
236.397 -64.8652 245.379 -40.7832 263.34 4.53027 c 2
335.184 188 l 1
181.096 287.34 l 2
137.61 315.225 114.372 330.593 111.379 333.445 c 0
108.385 336.297 106.888 340.416 106.888 345.803 c 0
106.888 359.745 114.924 366.717 130.994 366.717 c 0
132.255 366.717 154.312 364.815 197.167 361.013 c 2
387.648 344.853 l 1
430.661 528.798 l 2
441.69 576.646 448.544 602.945 451.222 607.699 c 0
453.9 612.452 458.863 614.828 466.11 614.828 c 0
473.674 614.828 478.716 612.215 481.236 606.986 c 0
483.757 601.758 491.005 573.317 502.979 521.667 c 2
544.1 344.853 l 1
EndSplineSet
Validated: 524289
EndChar
StartChar: zero
Encoding: 48 48 1
Width: 1303
VWidth: 2048
Flags: W
HStem: -43.3789 76.7998<582.097 721.09> 623.341 76.7998<582.097 721.091>
VStem: 403.82 97.4395<148.044 508.66> 802.221 96.959<148.044 508.659>
LayerCount: 2
Fore
SplineSet
651.5 623.341 m 0
601.58 623.341 564.061 598.78 538.939 549.66 c 0
513.82 500.541 501.26 426.7 501.26 328.141 c 0
501.26 229.9 513.82 156.221 538.939 107.101 c 0
564.061 57.9805 601.58 33.4209 651.5 33.4209 c 0
701.74 33.4209 739.42 57.9805 764.54 107.101 c 0
789.66 156.221 802.221 229.9 802.221 328.141 c 0
802.221 426.7 789.66 500.541 764.54 549.66 c 0
739.42 598.78 701.74 623.341 651.5 623.341 c 0
651.5 700.141 m 0
731.82 700.141 793.18 668.38 835.58 604.859 c 0
877.979 541.341 899.18 449.101 899.18 328.141 c 0
899.18 207.5 877.979 115.421 835.58 51.9004 c 0
793.18 -11.6201 731.819 -43.3789 651.5 -43.3789 c 0
571.18 -43.3789 509.82 -11.6201 467.42 51.9004 c 0
425.021 115.421 403.82 207.5 403.82 328.141 c 0
403.82 449.101 425.021 541.341 467.42 604.859 c 0
509.82 668.38 571.18 700.141 651.5 700.141 c 0
EndSplineSet
Validated: 524289
EndChar
StartChar: period
Encoding: 46 46 2
Width: 516
VWidth: 2048
Flags: W
HStem: 53.4004 166.199<203.263 309.297>
VStem: 174.6 163.801<82.9501 190.955>
LayerCount: 2
Fore
SplineSet
338.4 142.8 m 0
338.4 119.2 330.5 98.4004 314.7 80.4004 c 0
298.9 62.4004 277 53.4004 249 53.4004 c 0
225.4 53.4004 207.1 61.2002 194.1 76.7998 c 0
181.1 92.4004 174.6 111 174.6 132.6 c 0
174.6 155.8 182.6 176.1 198.6 193.5 c 0
214.6 210.9 236.8 219.6 265.2 219.6 c 0
288.8 219.6 306.9 212.2 319.5 197.4 c 0
332.1 182.6 338.4 164.4 338.4 142.8 c 0
EndSplineSet
Validated: 524289
EndChar
EndChars
EndSplineFont

Binary file not shown.

View File

@ -806,6 +806,23 @@ def is_gui_thread():
global gui_thread global gui_thread
return gui_thread is QThread.currentThread() return gui_thread is QThread.currentThread()
_rating_font = None
def rating_font():
global _rating_font
if _rating_font is None:
from PyQt4.Qt import QFontDatabase
_rating_font = 'Arial Unicode MS' if iswindows else 'sans-serif'
fontid = QFontDatabase.addApplicationFont(
#P('fonts/liberation/LiberationSerif-Regular.ttf')
P('fonts/calibreSymbols.otf')
)
if fontid > -1:
try:
_rating_font = unicode(list(
QFontDatabase.applicationFontFamilies(fontid))[0])
except:
pass
return _rating_font
def find_forms(srcdir): def find_forms(srcdir):
base = os.path.join(srcdir, 'calibre', 'gui2') base = os.path.join(srcdir, 'calibre', 'gui2')

View File

@ -20,7 +20,7 @@ from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
from calibre.constants import filesystem_encoding from calibre.constants import filesystem_encoding
from calibre.library.comments import comments_to_html from calibre.library.comments import comments_to_html
from calibre.gui2 import (config, open_local_file, open_url, pixmap_to_data, from calibre.gui2 import (config, open_local_file, open_url, pixmap_to_data,
gprefs) gprefs, rating_font)
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
from calibre.utils.formatter import EvalFormatter from calibre.utils.formatter import EvalFormatter
from calibre.utils.date import is_date_undefined from calibre.utils.date import is_date_undefined
@ -116,6 +116,14 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
val = force_unicode(val) val = force_unicode(val)
ans.append((field, ans.append((field,
u'<td class="comments" colspan="2">%s</td>'%comments_to_html(val))) u'<td class="comments" colspan="2">%s</td>'%comments_to_html(val)))
elif metadata['datatype'] == 'rating':
val = getattr(mi, field)
if val:
val = val/2.0
ans.append((field,
u'<td class="title">%s</td><td class="rating" '
'style=\'font-family:"%s"\'>%s</td>'%(
name, rating_font(), u'\u2605'*int(val))))
elif metadata['datatype'] == 'composite' and \ elif metadata['datatype'] == 'composite' and \
metadata['display'].get('contains_html', False): metadata['display'].get('contains_html', False):
val = getattr(mi, field) val = getattr(mi, field)

View File

@ -10,10 +10,11 @@ Module to implement the Cover Flow feature
import sys, os, time import sys, os, time
from PyQt4.Qt import (QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, QAction, from PyQt4.Qt import (QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, QAction,
QStackedLayout, QLabel, QByteArray, pyqtSignal, QKeySequence) QStackedLayout, QLabel, QByteArray, pyqtSignal, QKeySequence, QFont)
from calibre import plugins from calibre import plugins
from calibre.gui2 import config, available_height, available_width, gprefs from calibre.gui2 import (config, available_height, available_width, gprefs,
rating_font)
pictureflow, pictureflowerror = plugins['pictureflow'] pictureflow, pictureflowerror = plugins['pictureflow']
@ -102,6 +103,8 @@ if pictureflow is not None:
type=Qt.QueuedConnection) type=Qt.QueuedConnection)
self.context_menu = None self.context_menu = None
self.setContextMenuPolicy(Qt.DefaultContextMenu) self.setContextMenuPolicy(Qt.DefaultContextMenu)
if hasattr(self, 'setSubtitleFont'):
self.setSubtitleFont(QFont(rating_font()))
def set_context_menu(self, cm): def set_context_menu(self, cm):
self.context_menu = cm self.context_menu = cm

View File

@ -5,16 +5,12 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from math import cos, sin, pi
from PyQt4.Qt import (QColor, Qt, QModelIndex, QSize, QApplication, from PyQt4.Qt import (Qt, QApplication, QStyle, QIcon, QDoubleSpinBox,
QPainterPath, QLinearGradient, QBrush, QVariant, QSpinBox, QStyledItemDelegate, QComboBox, QTextDocument,
QPen, QStyle, QPainter, QStyleOptionViewItemV4, QAbstractTextDocumentLayout, QFont)
QIcon, QDoubleSpinBox, QVariant, QSpinBox,
QStyledItemDelegate, QComboBox, QTextDocument,
QAbstractTextDocumentLayout)
from calibre.gui2 import UNDEFINED_QDATETIME, error_dialog from calibre.gui2 import UNDEFINED_QDATETIME, error_dialog, rating_font
from calibre.gui2.widgets import EnLineEdit from calibre.gui2.widgets import EnLineEdit
from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox
from calibre.utils.date import now, format_date, qt_to_dt from calibre.utils.date import now, format_date, qt_to_dt
@ -27,81 +23,35 @@ from calibre.gui2.languages import LanguagesEdit
class RatingDelegate(QStyledItemDelegate): # {{{ class RatingDelegate(QStyledItemDelegate): # {{{
COLOR = QColor("blue")
SIZE = 16
def __init__(self, parent): def __init__(self, *args, **kwargs):
QStyledItemDelegate.__init__(self, parent) QStyledItemDelegate.__init__(self, *args, **kwargs)
self._parent = parent self.rf = QFont(rating_font())
self.dummy = QModelIndex() self.em = Qt.ElideMiddle
self.star_path = QPainterPath()
self.star_path.moveTo(90, 50)
for i in range(1, 5):
self.star_path.lineTo(50 + 40 * cos(0.8 * i * pi), \
50 + 40 * sin(0.8 * i * pi))
self.star_path.closeSubpath()
self.star_path.setFillRule(Qt.WindingFill)
self.gradient = QLinearGradient(0, 0, 0, 100)
self.factor = self.SIZE/100.
def sizeHint(self, option, index):
#num = index.model().data(index, Qt.DisplayRole).toInt()[0]
return QSize(5*(self.SIZE), self.SIZE+4)
def paint(self, painter, option, index):
style = self._parent.style()
option = QStyleOptionViewItemV4(option)
self.initStyleOption(option, index)
option.text = u''
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
def draw_star():
painter.save()
painter.scale(self.factor, self.factor)
painter.translate(50.0, 50.0)
painter.rotate(-20)
painter.translate(-50.0, -50.0)
painter.drawPath(self.star_path)
painter.restore()
painter.save()
if hasattr(QStyle, 'CE_ItemViewItem'):
style.drawControl(QStyle.CE_ItemViewItem, option,
painter, self._parent)
elif option.state & QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
else:
painter.fillRect(option.rect, option.backgroundBrush)
try:
painter.setRenderHint(QPainter.Antialiasing)
painter.setClipRect(option.rect)
y = option.rect.center().y()-self.SIZE/2.
x = option.rect.left()
color = index.data(Qt.ForegroundRole)
if color.isNull() or not color.isValid():
color = self.COLOR
else:
color = QColor(color)
painter.setPen(QPen(color, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
self.gradient.setColorAt(0.0, color)
self.gradient.setColorAt(1.0, color)
painter.setBrush(QBrush(self.gradient))
painter.translate(x, y)
i = 0
while i < num:
draw_star()
painter.translate(self.SIZE, 0)
i += 1
except:
import traceback
traceback.print_exc()
painter.restore()
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
sb = QStyledItemDelegate.createEditor(self, parent, option, index) sb = QStyledItemDelegate.createEditor(self, parent, option, index)
sb.setMinimum(0) sb.setMinimum(0)
sb.setMaximum(5) sb.setMaximum(5)
sb.setSuffix(' ' + _('stars'))
return sb return sb
def displayText(self, value, locale):
r = value.toInt()[0]
if r < 0 or r > 5:
r = 0
return u'\u2605'*r
def sizeHint(self, option, index):
option.font = self.rf
option.textElideMode = self.em
return QStyledItemDelegate.sizeHint(self, option, index)
def paint(self, painter, option, index):
option.font = self.rf
option.textElideMode = self.em
return QStyledItemDelegate.paint(self, painter, option, index)
# }}} # }}}
class DateDelegate(QStyledItemDelegate): # {{{ class DateDelegate(QStyledItemDelegate): # {{{

View File

@ -588,8 +588,8 @@ class BooksModel(QAbstractTableModel): # {{{
def rating_type(r, idx=-1): def rating_type(r, idx=-1):
r = self.db.data[r][idx] r = self.db.data[r][idx]
r = r/2 if r else 0 r = r/2.0 if r else 0
return QVariant(r) return QVariant(int(r))
def datetime_type(r, idx=-1): def datetime_type(r, idx=-1):
val = self.db.data[r][idx] val = self.db.data[r][idx]

View File

@ -200,10 +200,10 @@ class BooksView(QTableView): # {{{
ac = a if self._model.sorted_on[1] else d ac = a if self._model.sorted_on[1] else d
ac.setCheckable(True) ac.setCheckable(True)
ac.setChecked(True) ac.setChecked(True)
if col not in ('ondevice', 'rating', 'inlibrary') and \ if col not in ('ondevice', 'inlibrary') and \
(not self.model().is_custom_column(col) or \ (not self.model().is_custom_column(col) or \
self.model().custom_columns[col]['datatype'] not in ('bool', self.model().custom_columns[col]['datatype'] not in ('bool',
'rating')): )):
m = self.column_header_context_menu.addMenu( m = self.column_header_context_menu.addMenu(
_('Change text alignment for %s') % name) _('Change text alignment for %s') % name)
al = self._model.alignment_map.get(col, 'left') al = self._model.alignment_map.get(col, 'left')

View File

@ -27,7 +27,7 @@ from calibre.utils.logging import GUILog as Log
from calibre.ebooks.metadata.sources.identify import (identify, from calibre.ebooks.metadata.sources.identify import (identify,
urls_from_identifiers) urls_from_identifiers)
from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.book.base import Metadata
from calibre.gui2 import error_dialog, NONE from calibre.gui2 import error_dialog, NONE, rating_font
from calibre.utils.date import (utcnow, fromordinal, format_date, from calibre.utils.date import (utcnow, fromordinal, format_date,
UNDEFINED_DATE, as_utc) UNDEFINED_DATE, as_utc)
from calibre.library.comments import comments_to_html from calibre.library.comments import comments_to_html
@ -254,6 +254,7 @@ class ResultsView(QTableView): # {{{
return ret return ret
def show_details(self, index): def show_details(self, index):
f = rating_font()
book = self.model().data(index, Qt.UserRole) book = self.model().data(index, Qt.UserRole)
parts = [ parts = [
'<center>', '<center>',
@ -265,7 +266,8 @@ class ResultsView(QTableView): # {{{
if series[1]: if series[1]:
parts.append('<div>%s: %s</div>'%series) parts.append('<div>%s: %s</div>'%series)
if not book.is_null('rating'): if not book.is_null('rating'):
parts.append('<div>%s</div>'%('\u2605'*int(book.rating))) style = 'style=\'font-family:"%s"\''%f
parts.append('<div %s>%s</div>'%(style, '\u2605'*int(book.rating)))
parts.append('</center>') parts.append('</center>')
if book.identifiers: if book.identifiers:
urls = urls_from_identifiers(book.identifiers) urls = urls_from_identifiers(book.identifiers)

View File

@ -364,6 +364,8 @@ public:
QTime previousPosTimestamp; QTime previousPosTimestamp;
int pixelDistanceMoved; int pixelDistanceMoved;
int pixelsToMovePerSlide; int pixelsToMovePerSlide;
QFont subtitleFont;
void setImages(FlowImages *images); void setImages(FlowImages *images);
void dataChanged(); void dataChanged();
@ -422,6 +424,7 @@ PictureFlowPrivate::PictureFlowPrivate(PictureFlow* w, int queueLength_)
step = 0; step = 0;
target = 0; target = 0;
fade = 256; fade = 256;
subtitleFont = QFont();
triggerTimer.setSingleShot(true); triggerTimer.setSingleShot(true);
triggerTimer.setInterval(0); triggerTimer.setInterval(0);
@ -674,9 +677,13 @@ void PictureFlowPrivate::render_text(QPainter *painter, int index) {
caption = slideImages->caption(index); caption = slideImages->caption(index);
subtitle = slideImages->subtitle(index); subtitle = slideImages->subtitle(index);
buffer_width = buffer.width(); buffer_height = buffer.height(); buffer_width = buffer.width(); buffer_height = buffer.height();
subtitleFont.setPixelSize(fontSize);
brect = painter->boundingRect(QRect(0, 0, buffer_width, fontSize), TEXT_FLAGS, caption); brect = painter->boundingRect(QRect(0, 0, buffer_width, fontSize), TEXT_FLAGS, caption);
painter->save();
painter->setFont(subtitleFont);
brect2 = painter->boundingRect(QRect(0, 0, buffer_width, fontSize), TEXT_FLAGS, subtitle); brect2 = painter->boundingRect(QRect(0, 0, buffer_width, fontSize), TEXT_FLAGS, subtitle);
painter->restore();
// So that if there is no subtitle, the caption is not flush with the bottom // So that if there is no subtitle, the caption is not flush with the bottom
if (brect2.height() < fontSize) brect2.setHeight(fontSize); if (brect2.height() < fontSize) brect2.setHeight(fontSize);
@ -691,7 +698,11 @@ void PictureFlowPrivate::render_text(QPainter *painter, int index) {
painter->drawText(brect, TEXT_FLAGS, caption); painter->drawText(brect, TEXT_FLAGS, caption);
brect2.moveTop(buffer_height - brect2.height()); brect2.moveTop(buffer_height - brect2.height());
painter->save();
painter->setFont(subtitleFont);
painter->drawText(brect2, TEXT_FLAGS, slideImages->subtitle(index)); painter->drawText(brect2, TEXT_FLAGS, slideImages->subtitle(index));
painter->restore();
} }
// Render the slides. Updates only the offscreen buffer. // Render the slides. Updates only the offscreen buffer.
@ -1168,6 +1179,17 @@ void PictureFlow::setSlideSize(QSize size)
d->setSlideSize(size); d->setSlideSize(size);
} }
void PictureFlow::setSubtitleFont(QFont font)
{
d->subtitleFont = font;
}
QFont PictureFlow::subtitleFont() const
{
return d->subtitleFont;
}
QImage PictureFlow::slide(int index) const QImage PictureFlow::slide(int index) const
{ {
return d->slide(index); return d->slide(index);

View File

@ -92,6 +92,7 @@ Q_OBJECT
Q_PROPERTY(int currentSlide READ currentSlide WRITE setCurrentSlide) Q_PROPERTY(int currentSlide READ currentSlide WRITE setCurrentSlide)
Q_PROPERTY(QSize slideSize READ slideSize WRITE setSlideSize) Q_PROPERTY(QSize slideSize READ slideSize WRITE setSlideSize)
Q_PROPERTY(QFont subtitleFont READ subtitleFont WRITE setSubtitleFont)
public: public:
/*! /*!
@ -120,6 +121,17 @@ public:
*/ */
void setSlideSize(QSize size); void setSlideSize(QSize size);
/*!
Returns the font used to render subtitles
*/
QFont subtitleFont() const;
/*!
Sets the font used to render subtitles
*/
void setSubtitleFont(QFont font);
/*! /*!
Clears any caches held to free up memory Clears any caches held to free up memory
*/ */

View File

@ -41,6 +41,10 @@ public :
void setSlideSize(QSize size); void setSlideSize(QSize size);
QFont subtitleFont() const;
void setSubtitleFont(QFont font);
void clearCaches(); void clearCaches();
virtual QImage slide(int index) const; virtual QImage slide(int index) const;

View File

@ -173,7 +173,7 @@ class FieldMetadata(dict):
'datatype':'rating', 'datatype':'rating',
'is_multiple':{}, 'is_multiple':{},
'kind':'field', 'kind':'field',
'name':_('Ratings'), 'name':_('Rating'),
'search_terms':['rating'], 'search_terms':['rating'],
'is_custom':False, 'is_custom':False,
'is_category':True, 'is_category':True,