Use randomnly colored pins for text marks

This commit is contained in:
Kovid Goyal 2022-04-15 10:30:14 +05:30
parent b027b253f4
commit d693cb46fe
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 220 additions and 4 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

177
resources/pin-template.svg Normal file
View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="128"
height="128"
viewBox="0 0 128 128"
id="svg2"
version="1.1"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
sodipodi:docname="marked.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs4">
<filter
style="color-interpolation-filters:sRGB"
id="filter3014"
inkscape:label="Ridged border"
inkscape:menu="Bevels"
inkscape:menu-tooltip="Ridged border with inner bevel"
x="-0.029185463"
y="-0.02918546"
width="1.0583709"
height="1.0583709">
<feMorphology
id="feMorphology3016"
radius="4.3"
in="SourceAlpha"
result="result91" />
<feComposite
id="feComposite3018"
in2="result91"
operator="out"
in="SourceGraphic" />
<feGaussianBlur
id="feGaussianBlur3020"
result="result0"
stdDeviation="1.2" />
<feDiffuseLighting
id="feDiffuseLighting3022"
diffuseConstant="1"
result="result92">
<feDistantLight
id="feDistantLight3024"
elevation="66"
azimuth="225" />
</feDiffuseLighting>
<feBlend
id="feBlend3026"
in2="SourceGraphic"
mode="multiply"
result="result93" />
<feComposite
id="feComposite3028"
in2="SourceAlpha"
operator="in" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3782"
id="linearGradient3813"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,-18.805519,996.21376)"
x1="58"
y1="91"
x2="73"
y2="91" />
<linearGradient
id="linearGradient3782">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3784" />
<stop
style="stop-color:#c3c3c0;stop-opacity:1;"
offset="1"
id="stop3786" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.2"
inkscape:cx="19.821429"
inkscape:cy="69.732143"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2101"
inkscape:window-x="0"
inkscape:window-y="59"
inkscape:window-maximized="0"
inkscape:pagecheckerboard="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>Kovid Goyal</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:rights>
<dc:publisher>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:publisher>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-924.36216)">
<g
transform="translate(7.9775182,9.4253197)"
id="g4149">
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
id="path3088"
d="m 1.9128912,974.70018 49.4974748,-49.49747 -7.071068,21.2132 31.819805,17.67767 24.433067,-3.85121 -63.639613,63.63963 3.851207,-24.43308 -17.677669,-31.81981 z"
style="fill:#f39509;fill-opacity:1;stroke:#7a6822;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3014)" />
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path3097"
d="M 63.925974,996.92087 120,1042.5389 74.532576,986.31427"
style="fill:url(#linearGradient3813);fill-opacity:1;stroke:none" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -93,6 +93,7 @@ class View:
'au_map': self.get_author_data, 'au_map': self.get_author_data,
'ondevice': self.get_ondevice, 'ondevice': self.get_ondevice,
'marked': self.get_marked, 'marked': self.get_marked,
'all_marked_labels': self.all_marked_labels,
'series_sort':self.get_series_sort, 'series_sort':self.get_series_sort,
}.get(col, self._get) }.get(col, self._get)
if isinstance(col, numbers.Integral): if isinstance(col, numbers.Integral):
@ -221,6 +222,9 @@ class View:
id_ = idx if index_is_id else self.index_to_id(idx) id_ = idx if index_is_id else self.index_to_id(idx)
return self.marked_ids.get(id_, default_value) return self.marked_ids.get(id_, default_value)
def all_marked_labels(self):
return set(self.marked_ids.values()) - {'true'}
def get_author_data(self, idx, index_is_id=True, default_value=None): def get_author_data(self, idx, index_is_id=True, default_value=None):
id_ = idx if index_is_id else self.index_to_id(idx) id_ = idx if index_is_id else self.index_to_id(idx)
with self.cache.safe_read_lock: with self.cache.safe_read_lock:

View File

@ -122,7 +122,7 @@ class MarkBooksAction(InterfaceAction):
self.menu = m = self.qaction.menu() self.menu = m = self.qaction.menu()
m.aboutToShow.connect(self.about_to_show_menu) m.aboutToShow.connect(self.about_to_show_menu)
ma = partial(self.create_menu_action, m) ma = partial(self.create_menu_action, m)
self.show_marked_action = a = ma('mark_with_text', _('Mark books with text label'), icon='marked-text.png') self.show_marked_action = a = ma('mark_with_text', _('Mark books with text label'), icon='marked.png')
a.triggered.connect(self.mark_with_text) a.triggered.connect(self.mark_with_text)
self.show_marked_action = a = ma('show-marked', _('Show marked books'), icon='search.png', shortcut='Shift+Ctrl+M') self.show_marked_action = a = ma('show-marked', _('Show marked books'), icon='search.png', shortcut='Shift+Ctrl+M')
a.triggered.connect(self.show_marked) a.triggered.connect(self.show_marked)

View File

@ -23,7 +23,7 @@ from calibre import (
fit_image, force_unicode, human_readable, isbytestring, prepare_string_for_xml, fit_image, force_unicode, human_readable, isbytestring, prepare_string_for_xml,
strftime strftime
) )
from calibre.constants import DEBUG, config_dir, filesystem_encoding from calibre.constants import DEBUG, config_dir, dark_link_color, filesystem_encoding
from calibre.db.search import CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH, _match from calibre.db.search import CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH, _match
from calibre.ebooks.metadata import authors_to_string, fmt_sidx, string_to_authors from calibre.ebooks.metadata import authors_to_string, fmt_sidx, string_to_authors
from calibre.ebooks.metadata.book.formatter import SafeFormat from calibre.ebooks.metadata.book.formatter import SafeFormat
@ -52,6 +52,17 @@ ALIGNMENT_MAP = {'left': Qt.AlignmentFlag.AlignLeft, 'right': Qt.AlignmentFlag.A
_default_image = None _default_image = None
def render_pin(color='green', save_to=None):
svg = P('pin-template.svg', data=True).replace(b'fill:#f39509', ('fill:' + color).encode('utf-8'))
pm = QPixmap()
dpr = QApplication.instance().devicePixelRatio()
pm.setDevicePixelRatio(dpr)
pm.loadFromData(svg, 'svg')
if save_to:
pm.save(save_to)
return pm
def default_image(): def default_image():
global _default_image global _default_image
if _default_image is None: if _default_image is None:
@ -232,7 +243,6 @@ class BooksModel(QAbstractTableModel): # {{{
# remember that the cover grid view needs a larger version of the icon, # remember that the cover grid view needs a larger version of the icon,
# anyway) # anyway)
self.marked_icon = QIcon(I('marked.png')) self.marked_icon = QIcon(I('marked.png'))
self.marked_text_icon = QIcon(I('marked-text.png'))
self.bool_blank_icon_as_icon = QIcon(self.bool_blank_icon) self.bool_blank_icon_as_icon = QIcon(self.bool_blank_icon)
self.row_decoration = None self.row_decoration = None
self.device_connected = False self.device_connected = False
@ -241,8 +251,33 @@ class BooksModel(QAbstractTableModel): # {{{
self.current_highlighted_idx = None self.current_highlighted_idx = None
self.highlight_only = False self.highlight_only = False
self.row_height = 0 self.row_height = 0
self.marked_text_icons = {}
self.read_config() self.read_config()
def marked_text_icon_for(self, label):
import random
ans = self.marked_text_icons.get(label)
if ans is not None:
return ans[1]
used_labels = self.db.data.all_marked_labels()
for qlabel in tuple(self.marked_text_icons):
if qlabel not in used_labels:
del self.marked_text_icons[qlabel]
used_colors = {x[0] for x in self.marked_text_icons.values()}
if QApplication.instance().is_dark_theme:
all_colors = {dark_link_color, 'lightgreen', 'red', 'maroon', 'cyan', 'pink'}
else:
all_colors = {'blue', 'green', 'red', 'maroon', 'cyan', 'pink'}
for c in all_colors - used_colors:
color = c
break
else:
color = random.choice(sorted(all_colors))
pm = render_pin(color)
ans = QIcon(pm)
self.marked_text_icons[label] = color, ans
return ans
def _clear_caches(self): def _clear_caches(self):
self.color_cache = defaultdict(dict) self.color_cache = defaultdict(dict)
self.icon_cache = defaultdict(dict) self.icon_cache = defaultdict(dict)
@ -1075,7 +1110,7 @@ class BooksModel(QAbstractTableModel): # {{{
try: try:
m = self.db.data.get_marked(self.db.data.index_to_id(section)) m = self.db.data.get_marked(self.db.data.index_to_id(section))
if m: if m:
i = self.marked_icon if m == 'true' else self.marked_text_icon i = self.marked_icon if m == 'true' else self.marked_text_icon_for(m)
else: else:
i = self.row_decoration i = self.row_decoration
return i return i