mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-03-18 15:37:52 -04:00
Bookshelf: Allow specifying the font to use for spines
This commit is contained in:
parent
781ed74dc9
commit
cd24e5d9b9
@ -523,7 +523,7 @@ def create_defs():
|
||||
defs['bookshelf_min_font_multiplier'] = 0.75
|
||||
defs['bookshelf_max_font_multiplier'] = 1.3
|
||||
defs['bookshelf_outline_width'] = 0
|
||||
defs['bookshelf_bold_font'] = True
|
||||
defs['bookshelf_font'] = {'family': None, 'style': None}
|
||||
defs['bookshelf_use_custom_colors'] = False
|
||||
defs['bookshelf_custom_colors'] = {
|
||||
'light': {}, 'dark': {},
|
||||
|
||||
@ -27,6 +27,7 @@ from qt.core import (
|
||||
QEasingCurve,
|
||||
QEvent,
|
||||
QFont,
|
||||
QFontDatabase,
|
||||
QFontInfo,
|
||||
QFontMetricsF,
|
||||
QIcon,
|
||||
@ -1482,7 +1483,10 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
self.theme: ColorTheme = None
|
||||
self.palette_changed()
|
||||
|
||||
self.base_font_size_pts = QFontInfo(self.font()).pointSizeF()
|
||||
self.spine_font = self.default_spine_font = QFont(self.font())
|
||||
self.spine_font.setBold(True)
|
||||
self.divider_font = QFont(self.spine_font)
|
||||
self.base_font_size_pts = QFontInfo(self.spine_font).pointSizeF()
|
||||
self.outline_width = 0
|
||||
self.min_line_height = self.base_font_size_pts * 1.2
|
||||
|
||||
@ -1632,9 +1636,17 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
def refresh_settings(self):
|
||||
'''Refresh the gui and render settings.'''
|
||||
self.template_inited = False
|
||||
s = gprefs['bookshelf_font']
|
||||
if s and s.get('family'):
|
||||
self.spine_font = QFontDatabase.font(s['family'], s['style'], int(self.base_font_size_pts))
|
||||
self.spine_font.setPointSizeF(self.base_font_size_pts)
|
||||
else:
|
||||
self.spine_font = self.default_spine_font
|
||||
self.get_sized_font.cache_clear()
|
||||
self.get_text_metrics.cache_clear()
|
||||
self.min_font_size = max(0.1, min(gprefs['bookshelf_min_font_multiplier'], 1)) * self.base_font_size_pts
|
||||
self.max_font_size = max(1, min(gprefs['bookshelf_max_font_multiplier'], 3)) * self.base_font_size_pts
|
||||
_, fm, _ = self.get_sized_font(self.min_font_size, bold=gprefs['bookshelf_bold_font'])
|
||||
_, fm, _ = self.get_sized_font(self.min_font_size)
|
||||
self.outline_width = float(max(0, min(gprefs['bookshelf_outline_width'], 5)))
|
||||
self.min_line_height = math.ceil(fm.height() + self.outline_width * 2)
|
||||
self.calculate_shelf_geometry()
|
||||
@ -1973,25 +1985,24 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
painter.restore()
|
||||
|
||||
@lru_cache(maxsize=128)
|
||||
def get_sized_font(self, sz: float = 9, bold: bool = False) -> tuple[QFont, QFontMetricsF, QFontInfo]:
|
||||
font = QFont(self.font())
|
||||
def get_sized_font(self, sz: float = 9, for_divider: bool = False) -> tuple[QFont, QFontMetricsF, QFontInfo]:
|
||||
font = QFont(self.divider_font if for_divider else self.spine_font)
|
||||
font.setPointSizeF(sz)
|
||||
font.setBold(bold)
|
||||
return font, QFontMetricsF(font), QFontInfo(font)
|
||||
|
||||
@lru_cache(maxsize=4096)
|
||||
def get_text_metrics(
|
||||
self, first_line: str, second_line: str = '', sz: QSize = QSize(), bold: bool = False, allow_wrap: bool = False,
|
||||
outline_width: float = 0,
|
||||
self, first_line: str, second_line: str = '', sz: QSize = QSize(), allow_wrap: bool = False,
|
||||
outline_width: float = 0, for_divider: bool = False,
|
||||
) -> tuple[str, str, QFont, QFontMetricsF, bool]:
|
||||
width, height = sz.width(), sz.height()
|
||||
font, fm, fi = self.get_sized_font(self.base_font_size_pts, bold)
|
||||
font, fm, fi = self.get_sized_font(self.base_font_size_pts, for_divider=for_divider)
|
||||
extra_height = outline_width * 2 # stroke width above and below
|
||||
if allow_wrap and not second_line and first_line and fm.boundingRect(first_line).width() > width and height >= 2 * self.min_line_height:
|
||||
# rather than reducing font size if there is available space, wrap to two lines
|
||||
font2, fm2, fi2 = font, fm, fi
|
||||
while math.ceil(2 * (fm2.height() + extra_height)) > height:
|
||||
font2, fm2, fi2 = self.get_sized_font(font2.pointSizeF() - 0.5, bold)
|
||||
font2, fm2, fi2 = self.get_sized_font(font2.pointSizeF() - 0.5)
|
||||
if fm2.boundingRect(first_line).width() >= width: # two line font size is larger than one line font size
|
||||
font, fm, fi = font2, fm2, fi2
|
||||
has_third_line = False
|
||||
@ -2015,7 +2026,7 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
# larger font sizes
|
||||
if math.ceil(fm.height() + extra_height) < height:
|
||||
while font.pointSizeF() < self.max_font_size:
|
||||
q, qm, qi = self.get_sized_font(font.pointSizeF() + 1, bold)
|
||||
q, qm, qi = self.get_sized_font(font.pointSizeF() + 1)
|
||||
if math.ceil(qm.height() + extra_height) < height:
|
||||
font, fm = q, qm
|
||||
else:
|
||||
@ -2025,14 +2036,14 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
nsz = font.pointSizeF()
|
||||
if nsz < self.min_font_size and second_line:
|
||||
return '', '', font, fm, False
|
||||
font, fm, fi = self.get_sized_font(nsz - 0.5, bold)
|
||||
font, fm, fi = self.get_sized_font(nsz - 0.5)
|
||||
|
||||
# Now reduce the font size as much as needed to fit within width
|
||||
text = first_line
|
||||
if second_line and fm.boundingRect(first_line).width() < fm.boundingRect(second_line).width():
|
||||
text = second_line
|
||||
while fi.pointSizeF() > self.min_font_size and fm.boundingRect(text).width() > width:
|
||||
font, fm, fi = self.get_sized_font(font.pointSizeF() - 1, bold)
|
||||
font, fm, fi = self.get_sized_font(font.pointSizeF() - 1)
|
||||
if fi.pointSizeF() <= self.min_font_size:
|
||||
first_line = fm.elidedText(first_line, Qt.TextElideMode.ElideRight, width)
|
||||
if second_line:
|
||||
@ -2069,7 +2080,7 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
-self.TEXT_MARGIN if text_right else 0,
|
||||
0,
|
||||
)
|
||||
elided_text, _, font, _, _ = self.get_text_metrics(divider.group_name, '', text_rect.size(), bold=True)
|
||||
elided_text, _, font, _, _ = self.get_text_metrics(divider.group_name, '', text_rect.size(), for_divider=True)
|
||||
painter.save()
|
||||
painter.setFont(font)
|
||||
painter.setPen(self.theme.divider_text_color)
|
||||
@ -2200,13 +2211,12 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
|
||||
nfl, nsl, font, fm, was_wrapped = self.get_text_metrics(
|
||||
first_line, second_line, first_rect.transposed().size(), allow_wrap=True,
|
||||
bold=gprefs['bookshelf_bold_font'], outline_width=self.outline_width)
|
||||
outline_width=self.outline_width)
|
||||
if not nfl and not nsl: # two lines dont fit
|
||||
second_line = ''
|
||||
first_rect = QRect(rect.left(), first_rect.top(), rect.width(), first_rect.height())
|
||||
nfl, nsl, font, fm, _ = self.get_text_metrics(
|
||||
first_line, second_line, first_rect.transposed().size(), bold=gprefs['bookshelf_bold_font'],
|
||||
outline_width=self.outline_width)
|
||||
first_line, second_line, first_rect.transposed().size(), outline_width=self.outline_width)
|
||||
elif was_wrapped:
|
||||
first_rect, second_rect = calculate_rects(True)
|
||||
first_line, second_line, = nfl, nsl
|
||||
|
||||
@ -9,7 +9,7 @@ import os
|
||||
from contextlib import suppress
|
||||
from functools import lru_cache, partial
|
||||
|
||||
from qt.core import QApplication, QDialog, QDialogButtonBox, QIcon, QInputDialog, QLabel, Qt, QTabWidget, QTextBrowser, QTimer, QVBoxLayout, pyqtSignal
|
||||
from qt.core import QDialog, QDialogButtonBox, QIcon, QInputDialog, QLabel, Qt, QTabWidget, QTextBrowser, QTimer, QVBoxLayout, pyqtSignal
|
||||
|
||||
from calibre.gui2 import gprefs
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
@ -56,8 +56,29 @@ class BookshelfTab(QTabWidget, LazyConfigWidgetBase, Ui_Form):
|
||||
recount_updated = pyqtSignal(object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
self.current_font_choice = {}
|
||||
super().__init__(parent)
|
||||
|
||||
def restore_defaults(self):
|
||||
super().restore_defaults()
|
||||
self.current_font_choice = gprefs.defaults['bookshelf_font'].copy()
|
||||
self.update_font_display()
|
||||
self.populate_custom_color_theme(use_defaults=True)
|
||||
|
||||
def update_font_display(self):
|
||||
text = ''
|
||||
s = self.current_font_choice
|
||||
if s.get('family'):
|
||||
text = s['family'] + ' - ' + (s.get('style') or '')
|
||||
self.bookshelf_font_display.setText(text)
|
||||
|
||||
def initialize(self):
|
||||
super().initialize()
|
||||
s = gprefs['bookshelf_font'] or {}
|
||||
self.current_font_choice = s.copy()
|
||||
self.update_font_display()
|
||||
self.populate_custom_color_theme()
|
||||
|
||||
def commit(self, *args):
|
||||
import re
|
||||
tp = self.opt_bookshelf_spine_size_template.text()
|
||||
@ -72,6 +93,7 @@ class BookshelfTab(QTabWidget, LazyConfigWidgetBase, Ui_Form):
|
||||
for k,b in v.items():
|
||||
d[k] = b.color
|
||||
gprefs['bookshelf_custom_colors'] = newval
|
||||
gprefs['bookshelf_font'] = self.current_font_choice.copy()
|
||||
return super().commit(*args)
|
||||
|
||||
def genesis(self, gui):
|
||||
@ -87,7 +109,6 @@ class BookshelfTab(QTabWidget, LazyConfigWidgetBase, Ui_Form):
|
||||
r('bookshelf_make_space_for_second_line', gprefs)
|
||||
r('bookshelf_min_font_multiplier', gprefs)
|
||||
r('bookshelf_max_font_multiplier', gprefs)
|
||||
r('bookshelf_bold_font', gprefs)
|
||||
r('bookshelf_outline_width', gprefs)
|
||||
|
||||
r('bookshelf_divider_text_right', gprefs)
|
||||
@ -178,8 +199,18 @@ different calibre library you use.''').format('{size}', '{random}', '{pages}'))
|
||||
l.setBuddy(b)
|
||||
layout.insertRow(r, b, l)
|
||||
b.color_changed.connect(self.changed_signal)
|
||||
QApplication.instance().palette_changed.connect(self.populate_custom_color_theme)
|
||||
self.populate_custom_color_theme()
|
||||
self.change_font_button.clicked.connect(self.change_font)
|
||||
|
||||
def change_font(self):
|
||||
from calibre.gui2.preferences.look_feel_tabs.font_selection_dialog import FontSelectionDialog
|
||||
s = self.current_font_choice
|
||||
|
||||
d = FontSelectionDialog(family=s.get('family') or '', style=s.get('style') or '', parent=self)
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
family, style = d.selected_font()
|
||||
self.current_font_choice = {'family': family, 'style': style}
|
||||
self.update_font_display()
|
||||
self.changed_signal.emit()
|
||||
|
||||
def lazy_initialize(self):
|
||||
self.recount_timer.start()
|
||||
@ -273,13 +304,13 @@ def evaluate(book, context):
|
||||
'current_selected_color': _('&The current and selected book highlight'),
|
||||
}
|
||||
|
||||
def populate_custom_color_theme(self):
|
||||
def populate_custom_color_theme(self, use_defaults=False):
|
||||
from calibre.gui2.library.bookshelf_view import ColorTheme
|
||||
default = {
|
||||
'light': ColorTheme.light_theme()._asdict(),
|
||||
'dark': ColorTheme.dark_theme()._asdict(),
|
||||
}
|
||||
configs = gprefs['bookshelf_custom_colors']
|
||||
configs = (gprefs.defaults if use_defaults else gprefs)['bookshelf_custom_colors']
|
||||
for theme in default:
|
||||
for k in self.color_label_map():
|
||||
b = self.color_buttons[theme][k]
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>626</width>
|
||||
<height>520</height>
|
||||
<height>521</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
@ -377,6 +377,26 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>&Font:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>bookshelf_font_display</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>&Font size:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_bookshelf_min_font_multiplier</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
@ -434,6 +454,38 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>&Outline</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_bookshelf_outline_width</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="opt_bookshelf_outline_width">
|
||||
<property name="toolTip">
|
||||
<string><p>Set the thickness, in pixels, of the outline (border) around text on the spine. A value of zero means no outline. An outline helps the text standout against the spine, especially when the spine has a colorful cover.</string>
|
||||
</property>
|
||||
<property name="specialValueText">
|
||||
<string>none</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>3.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
@ -458,16 +510,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="opt_bookshelf_bold_font">
|
||||
<property name="toolTip">
|
||||
<string><p>Use a bold font to draw text on the book spine</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use &bold font</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
@ -483,47 +525,26 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>&Font size:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_bookshelf_min_font_multiplier</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Text &outline:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_bookshelf_outline_width</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QDoubleSpinBox" name="opt_bookshelf_outline_width">
|
||||
<property name="toolTip">
|
||||
<string><p>Set the thickness, in pixels, of the outline (border) around text on the spine. A value of zero means no outline. An outline helps the text standout against the spine, especially when the spine has a colorful cover.</string>
|
||||
</property>
|
||||
<property name="specialValueText">
|
||||
<string>none</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>3.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="bookshelf_font_display">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Use default font</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="change_font_button">
|
||||
<property name="text">
|
||||
<string>Change &font</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
@ -0,0 +1,212 @@
|
||||
from qt.core import QDialog, QDialogButtonBox, QFontDatabase, QFontInfo, QHBoxLayout, QLabel, QListWidget, QScrollArea, Qt, QVBoxLayout, QWidget, pyqtSignal
|
||||
|
||||
|
||||
class FontSelectionDialog(QDialog):
|
||||
fontSelected = pyqtSignal(str, str) # family, style
|
||||
|
||||
def __init__(self, family: str = '', style: str = '', min_size=8, medium_size=12, max_size=24, parent=None):
|
||||
super().__init__(parent)
|
||||
if family:
|
||||
self.initial_family, self.initial_style = family, style
|
||||
else:
|
||||
font = self.font()
|
||||
fi = QFontInfo(font)
|
||||
self.initial_family = fi.family()
|
||||
self.initial_style = fi.styleName()
|
||||
self.min_size = min_size
|
||||
self.medium_size = medium_size
|
||||
self.max_size = max_size
|
||||
|
||||
self.setWindowTitle(_('Select font'))
|
||||
self.setMinimumSize(600, 500)
|
||||
|
||||
self._setup_ui()
|
||||
self._populate_families()
|
||||
|
||||
def _setup_ui(self):
|
||||
main_layout = QVBoxLayout(self)
|
||||
|
||||
# Top section: Font families and styles side by side
|
||||
lists_layout = QHBoxLayout()
|
||||
|
||||
# Font families list
|
||||
families_layout = QVBoxLayout()
|
||||
families_label = QLabel(_('&Family:'))
|
||||
self.families_list = QListWidget()
|
||||
families_label.setBuddy(self.families_list)
|
||||
self.families_list.currentItemChanged.connect(self._on_family_changed)
|
||||
families_layout.addWidget(families_label)
|
||||
families_layout.addWidget(self.families_list)
|
||||
|
||||
# Styles list
|
||||
styles_layout = QVBoxLayout()
|
||||
styles_label = QLabel(_('&Style:'))
|
||||
self.styles_list = QListWidget()
|
||||
styles_label.setBuddy(self.styles_list)
|
||||
self.styles_list.currentItemChanged.connect(self._on_style_changed)
|
||||
styles_layout.addWidget(styles_label)
|
||||
styles_layout.addWidget(self.styles_list)
|
||||
|
||||
lists_layout.addLayout(families_layout, 2)
|
||||
lists_layout.addLayout(styles_layout, 1)
|
||||
|
||||
main_layout.addLayout(lists_layout, stretch=2)
|
||||
|
||||
# Preview area
|
||||
preview_group = QWidget()
|
||||
preview_layout = QVBoxLayout(preview_group)
|
||||
preview_layout.setContentsMargins(0, 10, 0, 10)
|
||||
|
||||
# Scrollable preview area
|
||||
scroll_area = QScrollArea()
|
||||
scroll_area.setWidgetResizable(True)
|
||||
scroll_area.setMinimumHeight(200)
|
||||
|
||||
preview_container = QWidget()
|
||||
self.preview_layout = QVBoxLayout(preview_container)
|
||||
self.preview_layout.setSpacing(10)
|
||||
self.preview_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
# Preview labels for different sizes
|
||||
self.preview_small = QLabel('The quick brown fox jumps over the lazy dog')
|
||||
self.preview_medium = QLabel(self.preview_small)
|
||||
self.preview_large = QLabel(self.preview_small)
|
||||
self.preview_layout.addWidget(self.preview_small)
|
||||
self.preview_layout.addWidget(self.preview_medium)
|
||||
self.preview_layout.addWidget(self.preview_large)
|
||||
|
||||
scroll_area.setWidget(preview_container)
|
||||
preview_layout.addWidget(scroll_area)
|
||||
|
||||
main_layout.addWidget(preview_group, stretch=1)
|
||||
|
||||
# OK/Cancel buttons
|
||||
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
|
||||
button_box.accepted.connect(self.accept)
|
||||
button_box.rejected.connect(self.reject)
|
||||
|
||||
main_layout.addWidget(button_box)
|
||||
|
||||
def _populate_families(self):
|
||||
'''Populate the families list with smoothly scalable fonts only'''
|
||||
self.families_list.clear()
|
||||
|
||||
# Get all font families
|
||||
all_families = QFontDatabase.families()
|
||||
|
||||
# Filter for smoothly scalable fonts
|
||||
scalable_families = []
|
||||
idx = i = 0
|
||||
for family in all_families:
|
||||
if QFontDatabase.isSmoothlyScalable(family):
|
||||
scalable_families.append(family)
|
||||
if family == self.initial_family:
|
||||
idx = i
|
||||
i += 1
|
||||
|
||||
scalable_families.sort()
|
||||
self.families_list.addItems(scalable_families)
|
||||
|
||||
# Select the initial item if available
|
||||
if self.families_list.count() > 0:
|
||||
self.families_list.setCurrentRow(idx)
|
||||
self._on_family_changed(self.families_list.currentItem(), None)
|
||||
|
||||
def _on_family_changed(self, current, previous):
|
||||
'''When a family is selected, populate the styles list'''
|
||||
if not current:
|
||||
self.styles_list.clear()
|
||||
return
|
||||
|
||||
family = current.text()
|
||||
self.styles_list.clear()
|
||||
|
||||
# Get all styles for this family
|
||||
styles = QFontDatabase.styles(family)
|
||||
idx = 0
|
||||
if family == self.initial_family and self.initial_style in styles:
|
||||
idx = styles.index(self.initial_style)
|
||||
self.styles_list.addItems(styles)
|
||||
|
||||
# Select first style if available
|
||||
if self.styles_list.count() > 0:
|
||||
self.styles_list.setCurrentRow(idx)
|
||||
self._update_preview()
|
||||
|
||||
def _on_style_changed(self, current, previous):
|
||||
'''Update the preview when style changes'''
|
||||
self._update_preview()
|
||||
|
||||
def _update_preview(self):
|
||||
'''Update the preview labels with the selected font'''
|
||||
family_item = self.families_list.currentItem()
|
||||
style_item = self.styles_list.currentItem()
|
||||
|
||||
if not family_item or not style_item:
|
||||
return
|
||||
|
||||
family = family_item.text()
|
||||
style = style_item.text()
|
||||
systems = tuple(QFontDatabase.writingSystems(family))
|
||||
text = ''
|
||||
for s in systems:
|
||||
if (t := QFontDatabase.writingSystemSample(s)):
|
||||
text = t
|
||||
break
|
||||
|
||||
def s(label, sz):
|
||||
font = QFontDatabase.font(family, style, int(sz))
|
||||
font.setPointSizeF(sz)
|
||||
label.setFont(font)
|
||||
label.setText('')
|
||||
if label is self.preview_small:
|
||||
prefix = _('Minimum size:')
|
||||
elif label is self.preview_medium:
|
||||
prefix = _('Base size:')
|
||||
else:
|
||||
prefix = _('Maximum size:')
|
||||
label.setText(prefix + ' ' + text)
|
||||
s(self.preview_small, self.min_size)
|
||||
s(self.preview_medium, self.medium_size)
|
||||
s(self.preview_large, self.max_size)
|
||||
|
||||
def selected_font(self):
|
||||
'''Returns the selected font family and style as a tuple'''
|
||||
family_item = self.families_list.currentItem()
|
||||
style_item = self.styles_list. currentItem()
|
||||
|
||||
if family_item and style_item:
|
||||
return family_item.text(), style_item.text()
|
||||
return None, None
|
||||
|
||||
def get_font(self, size=None):
|
||||
'''Returns a QFont object for the selected family and style'''
|
||||
family, style = self.selected_font()
|
||||
if family and style:
|
||||
if size is None:
|
||||
size = self.medium_size
|
||||
return QFontDatabase.font(family, style, size)
|
||||
return None
|
||||
|
||||
def accept(self):
|
||||
'''Override accept to emit signal with selected font'''
|
||||
family, style = self.selected_font()
|
||||
if family and style:
|
||||
self.fontSelected.emit(family, style)
|
||||
super().accept()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from calibre.gui2 import Application
|
||||
|
||||
app = Application()
|
||||
|
||||
def show_dialog():
|
||||
dialog = FontSelectionDialog(min_size=10, medium_size=14, max_size=28)
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
family, style = dialog.selected_font()
|
||||
selected_font = dialog.get_font(16)
|
||||
print(f'Selected: {family} - {style}')
|
||||
print(f'Font: {selected_font. family()} {selected_font.styleName()} {selected_font.pointSize()}pt')
|
||||
|
||||
show_dialog()
|
||||
Loading…
x
Reference in New Issue
Block a user