Skip to content

Commit

Permalink
Improve Cards tab (layout and card icons)
Browse files Browse the repository at this point in the history
  • Loading branch information
ping committed Aug 2, 2023
1 parent 9dbf69b commit 29ea6ec
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 157 deletions.
39 changes: 8 additions & 31 deletions calibre-plugin/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,18 @@
# See https://github.com/ping/libby-calibre-plugin for more
# information
#
from typing import Optional

from calibre.gui2.actions import InterfaceAction
from qt.core import (
QColor,
QDesktopServices,
QIcon,
QPainter,
QPixmap,
QSize,
QSvgRenderer,
QToolButton,
QUrl,
QXmlStreamReader,
)

from . import PLUGIN_ICON, PLUGIN_NAME, logger
from .compat import (
QColor_fromString,
QPainter_CompositionMode_CompositionMode_SourceIn,
Qt_GlobalColor_transparent,
_c,
)
from .compat import QColor_fromString, _c
from .config import PREFS, PreferenceKeys
from .dialog import (
BaseDialogMixin,
Expand All @@ -38,7 +27,7 @@
LoansDialogMixin,
MagazinesDialogMixin,
)
from .utils import ICON_MAP, PluginIcons
from .utils import CARD_ICON, ICON_MAP, PluginIcons, svg_to_qicon

# noinspection PyUnreachableCode
if False:
Expand All @@ -61,36 +50,24 @@ class OverdriveLibbyAction(InterfaceAction):
action_menu_clone_qaction = _("Libby")
dont_add_to = frozenset(["context-menu-device"])

@staticmethod
def svg_to_qicon(data: bytes, color: Optional[QColor] = None, size=(64, 64)):
renderer = QSvgRenderer(QXmlStreamReader(data))
pixmap = QPixmap(*size)
pixmap.fill(Qt_GlobalColor_transparent)
painter = QPainter(pixmap)
renderer.render(painter)
painter.setCompositionMode(QPainter_CompositionMode_CompositionMode_SourceIn)
if color:
painter.fillRect(pixmap.rect(), color)
painter.end()
return QIcon(pixmap)

def genesis(self):
# This method is called once per plugin, do initial setup here

# extract icons
image_resources = get_resources(
[v.file for v in ICON_MAP.values()] + [PLUGIN_ICON]
[v.file for v in ICON_MAP.values()] + [PLUGIN_ICON, CARD_ICON]
)
self.icons = {}
for k, v in ICON_MAP.items():
self.icons[k] = self.svg_to_qicon(
self.icons[k] = svg_to_qicon(
image_resources.pop(v.file), QColor_fromString(v.color)
)

# card icon
self.icons[PluginIcons.Card] = image_resources.pop(CARD_ICON)

# action icon
plugin_icon = self.svg_to_qicon(
image_resources.pop(PLUGIN_ICON), size=(300, 300)
)
plugin_icon = svg_to_qicon(image_resources.pop(PLUGIN_ICON), size=(300, 300))
self.qaction.setIcon(plugin_icon)
# set the cloned menu icon
mini_plugin_icon = QIcon(
Expand Down
91 changes: 76 additions & 15 deletions calibre-plugin/dialog/cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from calibre.utils.config import tweaks
from calibre.utils.date import dt_as_local, format_date
from lxml import etree
from qt.core import (
QCursor,
QDesktopServices,
Expand All @@ -20,7 +21,9 @@
QMenu,
QMouseEvent,
QPalette,
QPixmapCache,
QProgressBar,
QPushButton,
QScrollArea,
QSizePolicy,
QUrl,
Expand All @@ -32,9 +35,16 @@
from .base import BaseDialogMixin
from .. import DEMO_MODE
from ..borrow_book import LibbyBorrowHold
from ..compat import _c
from ..libby import LibbyClient
from ..models import LibbyCardsModel
from ..utils import PluginIcons, obfuscate_date, obfuscate_int, obfuscate_name
from ..utils import (
PluginIcons,
obfuscate_date,
obfuscate_int,
obfuscate_name,
svg_to_pixmap,
)

# noinspection PyUnreachableCode
if False:
Expand Down Expand Up @@ -68,13 +78,40 @@ def __init__(self, card, library, tab, *args, **kwargs):
self.tab = tab
self.icons = self.tab.icons
layout = QGridLayout()
layout.setColumnStretch(0, 1)
layout.setColumnStretch(1, 1)
self.setLayout(layout)
widget_row_pos = 0

# library name
# Library Card Icon
library_card_lbl = QLabel(self)
# render pixmap
card_pixmap_cache_id = f'card_website_{library["websiteId"]}'
card_pixmap = QPixmapCache.find(card_pixmap_cache_id)
if not QPixmapCache.find(card_pixmap_cache_id):
svg_root = etree.fromstring(self.icons[PluginIcons.Card])
if not DEMO_MODE:
stop1 = svg_root.find('.//stop[@class="stop1"]', svg_root.nsmap)
stop1.attrib["stop-color"] = library["settings"]["primaryColor"]["hex"]
stop2 = svg_root.find('.//stop[@class="stop2"]', svg_root.nsmap)
stop2.attrib["stop-color"] = library["settings"]["secondaryColor"][
"hex"
]
card_pixmap = svg_to_pixmap(
etree.tostring(svg_root), size=(40, 30)
) # original size=(80, 60)
QPixmapCache.insert(card_pixmap_cache_id, card_pixmap)
library_card_lbl.setPixmap(card_pixmap)
layout.addWidget(library_card_lbl, widget_row_pos, 0)

# Library Name
library_lbl = ClickableQLabel(
library["name"] if not DEMO_MODE else obfuscate_name(library["name"])
library["name"]
if not DEMO_MODE
else (
"Random "
f'#{obfuscate_int(library["websiteId"], offset=int(library["websiteId"]/2), min_value=1, max_val=999)}'
" Library"
)
)
curr_font = library_lbl.font()
curr_font.setPointSizeF(curr_font.pointSizeF() * 1.2)
Expand All @@ -88,9 +125,10 @@ def __init__(self, card, library, tab, *args, **kwargs):
self.library_lbl_context_menu_requested
)
library_lbl.setToolTip(_("Right-click for shortcuts"))
layout.addWidget(library_lbl, widget_row_pos, 0, 1, 2)
layout.addWidget(library_lbl, widget_row_pos, 1, 1, 3)
widget_row_pos += 1

# Card Name
card_name = (
card["cardName"] if not DEMO_MODE else obfuscate_name(card["cardName"])
)
Expand All @@ -99,8 +137,9 @@ def __init__(self, card, library, tab, *args, **kwargs):
card_lbl.doubleClicked.connect(
lambda: self.tab.display_debug("Card", self.card)
)
layout.addWidget(card_lbl, widget_row_pos, 0, 1, 2)

layout.addWidget(card_lbl, widget_row_pos, 0)
# Card Number
if card.get("username"):
card_username = (
card["username"] if not DEMO_MODE else obfuscate_name(card["username"])
Expand All @@ -109,9 +148,10 @@ def __init__(self, card, library, tab, *args, **kwargs):
card_user_lbl.setTextInteractionFlags(
Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse
)
layout.addWidget(card_user_lbl, widget_row_pos, 1)
layout.addWidget(card_user_lbl, widget_row_pos, 2, 1, 2)
widget_row_pos += 1

# Card Created Date
if card.get("createDate"):
dt_value = dt_as_local(LibbyClient.parse_datetime(card["createDate"]))
card_create_lbl = QLabel(
Expand All @@ -126,7 +166,9 @@ def __init__(self, card, library, tab, *args, **kwargs):
)
)
card_create_lbl.setTextFormat(Qt.RichText)
layout.addWidget(card_create_lbl, widget_row_pos, 0)
layout.addWidget(card_create_lbl, widget_row_pos, 0, 1, 2)

# Verified Date
if card.get("authorizeDate"):
dt_value = dt_as_local(
LibbyClient.parse_datetime(card["authorizeDate"])
Expand All @@ -143,7 +185,7 @@ def __init__(self, card, library, tab, *args, **kwargs):
)
)
card_auth_lbl.setTextFormat(Qt.RichText)
layout.addWidget(card_auth_lbl, widget_row_pos, 1)
layout.addWidget(card_auth_lbl, widget_row_pos, 2, 1, 2)
widget_row_pos += 1

# loans limits
Expand All @@ -165,7 +207,7 @@ def __init__(self, card, library, tab, *args, **kwargs):
self.loans_progressbar_context_menu_requested
)
loans_progressbar.setToolTip(_("Right-click for shortcuts"))
layout.addWidget(loans_progressbar, widget_row_pos, 0, 1, 2)
layout.addWidget(loans_progressbar, widget_row_pos, 0, 1, 4)
widget_row_pos += 1

# holds limits
Expand All @@ -187,7 +229,7 @@ def __init__(self, card, library, tab, *args, **kwargs):
self.holds_progressbar_context_menu_requested
)
holds_progressbar.setToolTip(_("Right-click for shortcuts"))
layout.addWidget(holds_progressbar, widget_row_pos, 0, 1, 2)
layout.addWidget(holds_progressbar, widget_row_pos, 0, 1, 4)

def library_lbl_context_menu_requested(self):
menu = QMenu(self)
Expand Down Expand Up @@ -262,7 +304,7 @@ def __init__(self, gui, icon, do_user_config, icons):
self.cards_tab_widget = QWidget()
self.cards_tab_widget.layout = QGridLayout()
self.cards_tab_widget.setSizePolicy(
QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding
QSizePolicy.MinimumExpanding, QSizePolicy.Maximum
)
self.cards_tab_widget.setLayout(self.cards_tab_widget.layout)
self.cards_scroll_area = QScrollArea()
Expand All @@ -271,20 +313,37 @@ def __init__(self, gui, icon, do_user_config, icons):
self.cards_scroll_area.setFrameShape(QFrame.NoFrame)
self.cards_scroll_area.setWidgetResizable(True)
self.cards_scroll_area.setWidget(self.cards_tab_widget)
widget_row_pos = 0

# Refresh button
self.cards_refresh_btn = QPushButton(_c("Refresh"), self)
self.cards_refresh_btn.setIcon(self.icons[PluginIcons.Refresh])
self.cards_refresh_btn.setAutoDefault(False)
self.cards_refresh_btn.setToolTip(_("Get latest loans"))
self.cards_refresh_btn.clicked.connect(self.cards_refresh_btn_clicked)
btn_size = self.cards_refresh_btn.size()
self.cards_refresh_btn.setMaximumSize(self.min_button_width, btn_size.height())
self.cards_tab_widget.layout.addWidget(self.cards_refresh_btn, 0, 0, 1, 3)
self.refresh_buttons.append(self.cards_refresh_btn)
widget_row_pos += 1

self.libby_cards_model = LibbyCardsModel(None, [], self.db) # model
self.models.append(self.libby_cards_model)

self.libby_cards_model.modelReset.connect(self.libby_cards_model_reset)
self.libby_cards_model.modelReset.connect(
lambda: self.libby_cards_model_reset(widget_row_pos)
)
self.cards_tab_index = self.add_tab(self.cards_scroll_area, _("Cards"))

def libby_cards_model_reset(self):
def cards_refresh_btn_clicked(self):
self.sync()

def libby_cards_model_reset(self, widget_row_pos):
for card_widget in self.card_widgets:
self.cards_tab_widget.layout.removeWidget(card_widget)
card_widget.setParent(None)
del card_widget
self.card_widgets = []
widget_row_pos = 0
for i in range(self.libby_cards_model.rowCount()):
card = self.libby_cards_model.data(
self.libby_cards_model.index(i, 0), Qt.UserRole
Expand All @@ -296,3 +355,5 @@ def libby_cards_model_reset(self):
self.card_widgets.append(card_widget)
self.cards_tab_widget.layout.addWidget(card_widget, widget_row_pos, 0)
widget_row_pos += 1
if DEMO_MODE:
break
31 changes: 31 additions & 0 deletions calibre-plugin/images/card.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 29ea6ec

Please sign in to comment.