From d121dde02a901c0a1d81dcb24373777e56e8e9d0 Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Fri, 31 Jan 2025 13:17:08 +0100 Subject: [PATCH 1/8] various cleanup --- src/calibre/gui2/preferences/look_feel.py | 104 ------------------ src/calibre/gui2/preferences/look_feel.ui | 33 +++--- .../preferences/look_feel_tabs/__init__.py | 4 +- .../preferences/look_feel_tabs/cover_view.py | 10 +- .../preferences/look_feel_tabs/cover_view.ui | 7 -- .../preferences/look_feel_tabs/quickview.py | 13 +-- .../preferences/look_feel_tabs/quickview.ui | 4 - .../preferences/look_feel_tabs/tb_display.py | 12 +- .../preferences/look_feel_tabs/tb_display.ui | 11 -- .../look_feel_tabs/tb_hierarchy.py | 4 +- .../look_feel_tabs/tb_hierarchy.ui | 11 -- .../look_feel_tabs/tb_icon_rules.ui | 11 -- .../look_feel_tabs/tb_partitioning.py | 10 +- .../look_feel_tabs/tb_partitioning.ui | 11 -- src/calibre/gui2/preferences/main.py | 2 +- 15 files changed, 37 insertions(+), 210 deletions(-) diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index f870ecfdf7..d3aa675d14 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -46,7 +46,6 @@ from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK from calibre.ebooks.metadata.sources.prefs import msprefs from calibre.gui2 import config, default_author_link, error_dialog, gprefs, icon_resource_manager, open_local_file, qt_app, question_dialog from calibre.gui2.custom_column_widgets import get_field_list as em_get_field_list -from calibre.gui2.dialogs.quickview import get_qv_field_list from calibre.gui2.library.alternate_views import CM_TO_INCH, auto_height from calibre.gui2.preferences import ConfigWidgetBase, Setting, set_help_tips, test_widget from calibre.gui2.preferences.coloring import EditRules @@ -255,106 +254,6 @@ class EMDisplayedFields(DisplayedFields): # {{{ # }}} -class QVDisplayedFields(DisplayedFields): # {{{ - - def __init__(self, db, parent=None): - DisplayedFields.__init__(self, db, parent) - - def initialize(self, use_defaults=False): - self.beginResetModel() - self.fields = [[x[0], x[1]] for x in - get_qv_field_list(self.db.field_metadata, use_defaults=use_defaults)] - self.endResetModel() - self.changed = True - - def commit(self): - if self.changed: - self.db.new_api.set_pref('qv_display_fields', self.fields) -# }}} - - -class TBDisplayedFields(DisplayedFields): # {{{ - # The code in this class depends on the fact that the tag browser is - # initialized before this class is instantiated. - - def __init__(self, db, parent=None, category_icons=None): - DisplayedFields.__init__(self, db, parent, category_icons=category_icons) - from calibre.gui2.ui import get_gui - self.gui = get_gui() - - def initialize(self, use_defaults=False, pref_data_override=None): - tv = self.gui.tags_view - cat_ord = tv.model().get_ordered_categories(use_defaults=use_defaults, - pref_data_override=pref_data_override) - if use_defaults: - hc = [] - self.changed = True - elif pref_data_override: - hc = [k for k,v in pref_data_override if not v] - self.changed = True - else: - hc = tv.hidden_categories - - self.beginResetModel() - self.fields = [[x, x not in hc] for x in cat_ord] - self.endResetModel() - - def commit(self): - if self.changed: - self.db.prefs.set('tag_browser_hidden_categories', [k for k,v in self.fields if not v]) - self.db.prefs.set('tag_browser_category_order', [k for k,v in self.fields]) -# }}} - - -class TBPartitionedFields(DisplayedFields): # {{{ - # The code in this class depends on the fact that the tag browser is - # initialized before this class is instantiated. - - def __init__(self, db, parent=None, category_icons=None): - DisplayedFields.__init__(self, db, parent, category_icons=category_icons) - from calibre.gui2.ui import get_gui - self.gui = get_gui() - - def filter_user_categories(self, tv): - cats = tv.model().categories - answer = {} - filtered = set() - for key,name in cats.items(): - if key.startswith('@'): - key = key.partition('.')[0] - name = key[1:] - if key not in filtered: - answer[key] = name - filtered.add(key) - return answer - - def initialize(self, use_defaults=False, pref_data_override=None): - tv = self.gui.tags_view - cats = self.filter_user_categories(tv) - ans = [] - if use_defaults: - ans = [[k, True] for k in cats.keys()] - self.changed = True - elif pref_data_override: - po = dict(pref_data_override) - ans = [[k, po.get(k, True)] for k in cats.keys()] - self.changed = True - else: - # Check if setting not migrated yet - cats_to_partition = frozenset(self.db.prefs.get('tag_browser_dont_collapse', gprefs.get('tag_browser_dont_collapse')) or ()) - for key in cats: - ans.append([key, key not in cats_to_partition]) - self.beginResetModel() - self.fields = ans - self.endResetModel() - - def commit(self): - if self.changed: - # Migrate to a per-library setting - self.db.prefs.set('tag_browser_dont_collapse', [k for k,v in self.fields if not v]) -# }}} - - class BDVerticalCats(DisplayedFields): # {{{ def __init__(self, db, parent=None, category_icons=None): @@ -768,7 +667,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.display_model.restore_defaults() self.em_display_model.restore_defaults() self.bd_vertical_cats_model.restore_defaults() - gprefs.set('tb_search_order', gprefs.defaults['tb_search_order']) self.edit_rules.clear() self.icon_rules.clear() self.grid_rules.clear() @@ -864,8 +762,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): m.update_db_prefs_cache() m.beginResetModel(), m.endResetModel() self.update_font_display() - gui.tags_view.set_look_and_feel() - gui.tags_view.reread_collapse_parameters() gui.tags_view.model().reset_tag_browser() gui.library_view.refresh_book_details(force=True) gui.library_view.refresh_grid() diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index 52d0d1cbd2..69784aab70 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -1366,11 +1366,6 @@ columns". Editing with mouse clicks and the Tab key will be disabled.</p QListView
calibre/gui2/preferences.h
- - ListWidgetWithMoveByKeyPress - QListWidget -
calibre/gui2/preferences.h
-
ScrollingTabWidget QTabWidget @@ -1378,39 +1373,39 @@ columns". Editing with mouse clicks and the Tab key will be disabled.</p 1 - ConfigWidget + ConfigWidgetBase QWidget -
calibre/gui2/preferences/look_feel.h
+
calibre/gui2/preferences/look_feel_tabs.h
TbDisplayTab - ConfigWidget -
calibre/gui2/preferences/look_feel_tabs.tb_display.h
+ ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/tb_display.h
TbPartitioningTab - ConfigWidget -
calibre/gui2/preferences/look_feel_tabs.tb_partitioning.h
+ ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/tb_partitioning.h
TbIconRulesTab - ConfigWidget -
calibre/gui2/preferences/look_feel_tabs.tb_icon_rules.h
+ ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.h
TbHierarchyTab - ConfigWidget -
calibre/gui2/preferences/look_feel_tabs.tb_hierarchy.h
+ ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.h
CoverView - ConfigWidget -
calibre/gui2/preferences/look_feel_tabs.cover_view.h
+ ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/cover_view.h
QuickviewTab - ConfigWidget -
calibre/gui2/preferences/look_feel_tabs.quickview.h
+ ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/quickview.h
diff --git a/src/calibre/gui2/preferences/look_feel_tabs/__init__.py b/src/calibre/gui2/preferences/look_feel_tabs/__init__.py index a50b12f6a7..9252a5b93f 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/__init__.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/__init__.py @@ -13,7 +13,7 @@ from calibre.gui2 import choose_files, choose_save_file, error_dialog from calibre.gui2.book_details import get_field_list -class DisplayedFields(QAbstractListModel): # {{{ +class DisplayedFields(QAbstractListModel): def __init__(self, db, parent=None, pref_name=None, category_icons=None): self.pref_name = pref_name or 'book_display_fields' @@ -154,5 +154,3 @@ def move_field_down(widget, model): sm = widget.selectionModel() sm.select(idx, QItemSelectionModel.SelectionFlag.ClearAndSelect) widget.setCurrentIndex(idx) - -# }}} diff --git a/src/calibre/gui2/preferences/look_feel_tabs/cover_view.py b/src/calibre/gui2/preferences/look_feel_tabs/cover_view.py index 5d946b87a0..ec934384ad 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/cover_view.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/cover_view.py @@ -10,7 +10,7 @@ from qt.core import QKeySequence from calibre.gui2 import config, gprefs from calibre.gui2.dialogs.template_dialog import TemplateDialog -from calibre.gui2.preferences import ConfigWidgetBase, LazyConfigWidgetBase, set_help_tips +from calibre.gui2.preferences import LazyConfigWidgetBase, set_help_tips from calibre.gui2.preferences.look_feel_tabs.cover_view_ui import Ui_Form @@ -41,8 +41,6 @@ class CoverView(LazyConfigWidgetBase, Ui_Form): self.fs_help_msg.setText(self.fs_help_msg.text()%( QKeySequence(QKeySequence.StandardKey.FullScreen).toString(QKeySequence.SequenceFormat.NativeText))) - def lazy_initialize(self): - ConfigWidgetBase.initialize(self) set_help_tips(self.opt_cover_browser_narrow_view_position, _( 'This option controls the position of the cover browser when using the Narrow user ' 'interface layout. "Automatic" will place the cover browser on top or on the right ' @@ -64,12 +62,6 @@ class CoverView(LazyConfigWidgetBase, Ui_Form): if t.exec(): self.opt_cover_browser_title_template.setText(t.rule[1]) - def commit(self): - return ConfigWidgetBase.commit(self) - - def restore_defaults(self): - ConfigWidgetBase.restore_defaults(self) - def refresh_gui(self, gui): gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections']) gui.cover_flow.setPreserveAspectRatio(gprefs['cb_preserve_aspect_ratio']) diff --git a/src/calibre/gui2/preferences/look_feel_tabs/cover_view.ui b/src/calibre/gui2/preferences/look_feel_tabs/cover_view.ui index 545ccc4954..4e3cfaad46 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/cover_view.ui +++ b/src/calibre/gui2/preferences/look_feel_tabs/cover_view.ui @@ -2,13 +2,6 @@ Form - - - :/images/cover_flow.png:/images/cover_flow.png - - - Cover &browser - 0 diff --git a/src/calibre/gui2/preferences/look_feel_tabs/quickview.py b/src/calibre/gui2/preferences/look_feel_tabs/quickview.py index a8bf913466..5fafe4530d 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/quickview.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/quickview.py @@ -10,12 +10,12 @@ from functools import partial from calibre.gui2 import gprefs from calibre.gui2.actions.show_quickview import get_quickview_action_plugin from calibre.gui2.dialogs.quickview import get_qv_field_list -from calibre.gui2.preferences import ConfigWidgetBase, LazyConfigWidgetBase +from calibre.gui2.preferences import LazyConfigWidgetBase from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, move_field_down, move_field_up from calibre.gui2.preferences.look_feel_tabs.quickview_ui import Ui_Form -class QVDisplayedFields(DisplayedFields): # {{{ +class QVDisplayedFields(DisplayedFields): def __init__(self, db, parent=None): DisplayedFields.__init__(self, db, parent) @@ -31,8 +31,6 @@ class QVDisplayedFields(DisplayedFields): # {{{ if self.changed: self.db.new_api.set_pref('qv_display_fields', self.fields) -# }}} - class QuickviewTab(LazyConfigWidgetBase, Ui_Form): @@ -59,7 +57,7 @@ class QuickviewTab(LazyConfigWidgetBase, Ui_Form): self.qv_display_model.initialize() def restore_defaults(self): - ConfigWidgetBase.restore_defaults(self) + LazyConfigWidgetBase.restore_defaults(self) self.qv_display_model.restore_defaults() def refresh_gui(self, gui): @@ -67,7 +65,6 @@ class QuickviewTab(LazyConfigWidgetBase, Ui_Form): if qv: qv.refill_quickview() - def commit(self, *args): - rr = ConfigWidgetBase.commit(self, *args) + def commit(self): self.qv_display_model.commit() - return rr + return LazyConfigWidgetBase.commit(self) diff --git a/src/calibre/gui2/preferences/look_feel_tabs/quickview.ui b/src/calibre/gui2/preferences/look_feel_tabs/quickview.ui index cf4a3174ec..e3a8d62323 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/quickview.ui +++ b/src/calibre/gui2/preferences/look_feel_tabs/quickview.ui @@ -2,10 +2,6 @@ Form - - - :/images/cover_flow.png:/images/cover_flow.png - diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_display.py b/src/calibre/gui2/preferences/look_feel_tabs/tb_display.py index 99646fce8d..a29b390d06 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/tb_display.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_display.py @@ -9,12 +9,12 @@ from functools import partial from calibre.db.categories import is_standard_category from calibre.gui2 import config, gprefs -from calibre.gui2.preferences import ConfigWidgetBase, LazyConfigWidgetBase +from calibre.gui2.preferences import LazyConfigWidgetBase from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, export_layout, import_layout, move_field_down, move_field_up, reset_layout from calibre.gui2.preferences.look_feel_tabs.tb_display_ui import Ui_Form -class TBDisplayedFields(DisplayedFields): # {{{ +class TBDisplayedFields(DisplayedFields): # The code in this class depends on the fact that the tag browser is # initialized before this class is instantiated. @@ -44,7 +44,6 @@ class TBDisplayedFields(DisplayedFields): # {{{ if self.changed: self.db.prefs.set('tag_browser_hidden_categories', [k for k,v in self.fields if not v]) self.db.prefs.set('tag_browser_category_order', [k for k,v in self.fields]) -# }}} class TbDisplayTab(LazyConfigWidgetBase, Ui_Form): @@ -104,9 +103,12 @@ class TbDisplayTab(LazyConfigWidgetBase, Ui_Form): move_field_up(self.tb_display_order, model) def restore_defaults(self): - ConfigWidgetBase.restore_defaults(self) + LazyConfigWidgetBase.restore_defaults(self) self.tb_display_model.restore_defaults() def commit(self): self.tb_display_model.commit() - return ConfigWidgetBase.commit(self) + return LazyConfigWidgetBase.commit(self) + + def refresh_gui(self, gui): + gui.tags_view.set_look_and_feel() diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_display.ui b/src/calibre/gui2/preferences/look_feel_tabs/tb_display.ui index 893b1f93bb..a6df02e4aa 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/tb_display.ui +++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_display.ui @@ -2,17 +2,6 @@ Form - - - 0 - 0 - 1096 - 791 - - - - Form - diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.py b/src/calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.py index ff1b40d22f..d14ec2c63d 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.py @@ -16,7 +16,7 @@ from calibre.gui2.preferences.look_feel_tabs import DisplayedFields from calibre.gui2.preferences.look_feel_tabs.tb_hierarchy_ui import Ui_Form -class TBHierarchicalFields(DisplayedFields): # {{{ +class TBHierarchicalFields(DisplayedFields): # The code in this class depends on the fact that the tag browser is # initialized before this class is instantiated. @@ -51,7 +51,6 @@ class TBHierarchicalFields(DisplayedFields): # {{{ def commit(self): if self.changed: self.db.prefs.set('categories_using_hierarchy', [k for k,v in self.fields if v]) -# }}} class TbHierarchyTab(LazyConfigWidgetBase, Ui_Form): @@ -175,3 +174,4 @@ class TbHierarchyTab(LazyConfigWidgetBase, Ui_Form): def commit(self): self.tb_search_order_commit() self.tb_hierarchical_cats_model.commit() + return LazyConfigWidgetBase.commit(self) diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.ui b/src/calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.ui index eaec795640..f51114c98e 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.ui +++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_hierarchy.ui @@ -2,17 +2,6 @@ Form - - - 0 - 0 - 1035 - 547 - - - - Form - diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.ui b/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.ui index 7abf8e7f20..ee0bc3271d 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.ui +++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.ui @@ -2,17 +2,6 @@ Form - - - 0 - 0 - 1035 - 547 - - - - Form - diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_partitioning.py b/src/calibre/gui2/preferences/look_feel_tabs/tb_partitioning.py index d09f36d8d2..9b453c04d1 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/tb_partitioning.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_partitioning.py @@ -9,12 +9,12 @@ __docformat__ = 'restructuredtext en' from functools import partial from calibre.gui2 import gprefs -from calibre.gui2.preferences import ConfigWidgetBase, LazyConfigWidgetBase +from calibre.gui2.preferences import LazyConfigWidgetBase from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, export_layout, import_layout, reset_layout from calibre.gui2.preferences.look_feel_tabs.tb_partitioning_ui import Ui_Form -class TBPartitionedFields(DisplayedFields): # {{{ +class TBPartitionedFields(DisplayedFields): # The code in this class depends on the fact that the tag browser is # initialized before this class is instantiated. @@ -60,7 +60,6 @@ class TBPartitionedFields(DisplayedFields): # {{{ if self.changed: # Migrate to a per-library setting self.db.prefs.set('tag_browser_dont_collapse', [k for k,v in self.fields if not v]) -# }}} class TbPartitioningTab(LazyConfigWidgetBase, Ui_Form): @@ -92,4 +91,7 @@ class TbPartitioningTab(LazyConfigWidgetBase, Ui_Form): def commit(self): self.tb_categories_to_part_model.commit() - return ConfigWidgetBase.commit(self) + return LazyConfigWidgetBase.commit(self) + + def refresh_gui(self, gui): + gui.tags_view.reread_collapse_parameters() diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_partitioning.ui b/src/calibre/gui2/preferences/look_feel_tabs/tb_partitioning.ui index e93be6e29c..fe16266be2 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/tb_partitioning.ui +++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_partitioning.ui @@ -2,17 +2,6 @@ Form - - - 0 - 0 - 1035 - 547 - - - - Form - diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index 91ee4407a8..306eb200f5 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -397,7 +397,7 @@ class Preferences(QDialog): def commit(self, *args): # Commit the child widgets first in case the main widget uses the information - must_restart = bool(self.showing_widget.do_on_child_tabs('commit')) | self.showing_widget.commit() + must_restart = bool(self.showing_widget.do_on_child_tabs('commit')) | bool(self.showing_widget.commit()) rc = self.showing_widget.restart_critical self.committed = True do_restart = False From 210415fd4a23edd402dd6f1b17f98025f64265bb Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Fri, 31 Jan 2025 16:12:55 +0100 Subject: [PATCH 2/8] move "Look & Feel/Main interface" to its own widget --- src/calibre/gui2/preferences/look_feel.py | 166 +------- src/calibre/gui2/preferences/look_feel.ui | 354 +---------------- .../look_feel_tabs/main_interface.py | 193 ++++++++++ .../look_feel_tabs/main_interface.ui | 356 ++++++++++++++++++ 4 files changed, 557 insertions(+), 512 deletions(-) create mode 100644 src/calibre/gui2/preferences/look_feel_tabs/main_interface.py create mode 100644 src/calibre/gui2/preferences/look_feel_tabs/main_interface.ui diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index d3aa675d14..172320f9a4 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -10,16 +10,12 @@ from functools import partial from threading import Thread from qt.core import ( - QApplication, QBrush, QColor, QColorDialog, QComboBox, QDialog, QDialogButtonBox, - QFont, - QFontDialog, - QFontInfo, QFormLayout, QHeaderView, QIcon, @@ -41,22 +37,19 @@ from qt.core import ( ) from calibre import human_readable -from calibre.constants import ismacos, iswindows from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK from calibre.ebooks.metadata.sources.prefs import msprefs -from calibre.gui2 import config, default_author_link, error_dialog, gprefs, icon_resource_manager, open_local_file, qt_app, question_dialog +from calibre.gui2 import config, default_author_link, error_dialog, gprefs, open_local_file, question_dialog from calibre.gui2.custom_column_widgets import get_field_list as em_get_field_list from calibre.gui2.library.alternate_views import CM_TO_INCH, auto_height -from calibre.gui2.preferences import ConfigWidgetBase, Setting, set_help_tips, test_widget +from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.coloring import EditRules from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, export_layout, import_layout, move_field_down, move_field_up, reset_layout from calibre.gui2.preferences.look_feel_ui import Ui_Form from calibre.gui2.widgets import BusyCursor from calibre.gui2.widgets2 import Dialog from calibre.startup import connect_lambda -from calibre.utils.config import prefs from calibre.utils.icu import sort_key -from calibre.utils.localization import available_translations, get_lang, get_language from calibre.utils.resources import get_path as P from calibre.utils.resources import set_data from polyglot.builtins import iteritems @@ -322,53 +315,21 @@ class Background(QWidget): # {{{ # }}} -class LanguageSetting(Setting): - - def commit(self): - val = self.get_gui_val() - oldval = self.get_config_val() - if val != oldval: - gprefs.set('last_used_language', oldval) - return super().commit() - - class ConfigWidget(ConfigWidgetBase, Ui_Form): size_calculated = pyqtSignal(object) def genesis(self, gui): self.gui = gui - self.ui_style_available = True - if not ismacos and not iswindows: - self.label_widget_style.setVisible(False) - self.opt_ui_style.setVisible(False) - self.ui_style_available = False db = gui.library_view.model().db r = self.register - try: - self.icon_theme_title = icon_resource_manager.user_theme_title - except Exception: - self.icon_theme_title = _('Default icons') - self.icon_theme.setText(_('Icon theme: %s') % self.icon_theme_title) - self.commit_icon_theme = None - self.icon_theme_button.clicked.connect(self.choose_icon_theme) self.default_author_link = DefaultAuthorLink(self.default_author_link_container) self.default_author_link.changed_signal.connect(self.changed_signal) - r('ui_style', gprefs, restart_required=True, choices=[(_('System default'), 'system'), (_('calibre style'), 'calibre')]) - r('book_list_tooltips', gprefs) - r('dnd_merge', gprefs) - r('wrap_toolbar_text', gprefs, restart_required=True) - r('show_layout_buttons', gprefs) - r('show_sb_all_actions_button', gprefs) - # r('show_sb_preference_button', gprefs) - r('row_numbers_in_book_list', gprefs) r('bd_show_cover', gprefs) r('bd_overlay_cover_size', gprefs) - r('cover_corner_radius', gprefs) - r('cover_corner_radius_unit', gprefs, choices=[(_('Pixels'), 'px'), (_('Percentage'), '%')]) r('cover_grid_width', gprefs) r('cover_grid_height', gprefs) r('cover_grid_cache_size_multiple', gprefs) @@ -379,48 +340,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('emblem_size', gprefs) r('emblem_position', gprefs, choices=[ (_('Left'), 'left'), (_('Top'), 'top'), (_('Right'), 'right'), (_('Bottom'), 'bottom')]) - r('book_list_extra_row_spacing', gprefs) - r('booklist_grid', gprefs) r('book_details_comments_heading_pos', gprefs, choices=[ (_('Never'), 'hide'), (_('Above text'), 'above'), (_('Beside text'), 'side')]) r('book_details_note_link_icon_width', gprefs) self.id_links_button.clicked.connect(self.edit_id_link_rules) - def get_esc_lang(l): - if l == 'en': - return 'English' - return get_language(l) - - lang = get_lang() - if lang is None or lang not in available_translations(): - lang = 'en' - items = [(l, get_esc_lang(l)) for l in available_translations() - if l != lang] - if lang != 'en': - items.append(('en', get_esc_lang('en'))) - items.sort(key=lambda x: x[1].lower()) - choices = [(y, x) for x, y in items] - # Default language is the autodetected one - choices = [(get_language(lang), lang)] + choices - lul = gprefs.get('last_used_language') - if lul and (lul in available_translations() or lul in ('en', 'eng')): - choices.insert(1, ((get_language(lul), lul))) - r('language', prefs, choices=choices, restart_required=True, setting=LanguageSetting) - - r('disable_animations', config) - r('systray_icon', config, restart_required=True) - r('show_splash_screen', gprefs) - r('disable_tray_notification', config) r('use_roman_numerals_for_series_number', config) - choices = [(_('Off'), 'off'), (_('Small'), 'small'), - (_('Medium-small'), 'mid-small'), (_('Medium'), 'medium'), (_('Large'), 'large')] - r('toolbar_icon_size', gprefs, choices=choices) - - choices = [(_('If there is enough room'), 'auto'), (_('Always'), 'always'), - (_('Never'), 'never')] - r('toolbar_text', gprefs, choices=choices) - fm = db.field_metadata choices = sorted(((fm[k]['name'], k) for k in fm.displayable_field_keys() if fm[k]['name']), key=lambda x:sort_key(x[0])) @@ -441,9 +367,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('edit_metadata_single_cc_label_length', gprefs) r('edit_metadata_templates_only_F2_on_booklist', gprefs) - self.current_font = self.initial_font = None - self.change_font_button.clicked.connect(self.change_font) - self.display_model = DisplayedFields(self.gui.current_db, self.field_display_order) self.display_model.dataChanged.connect(self.changed_signal) self.field_display_order.setModel(self.display_model) @@ -527,37 +450,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.sections_view.setSpacing(4) self.sections_view.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.tabWidget.currentWidget().setFocus(Qt.FocusReason.OtherFocusReason) - self.opt_ui_style.currentIndexChanged.connect(self.update_color_palette_state) - self.opt_gui_layout.addItem(_('Wide'), 'wide') - self.opt_gui_layout.addItem(_('Narrow'), 'narrow') - self.opt_gui_layout.currentIndexChanged.connect(self.changed_signal) - set_help_tips(self.opt_gui_layout, config.help('gui_layout')) - self.button_adjust_colors.clicked.connect(self.adjust_colors) - - def adjust_colors(self): - from calibre.gui2.dialogs.palette import PaletteConfig - d = PaletteConfig(self) - if d.exec() == QDialog.DialogCode.Accepted: - d.apply_settings() - self.changed_signal.emit() def initial_tab_changed(self): self.sections_view.setCurrentRow(self.tabWidget.currentIndex()) - def update_color_palette_state(self): - if self.ui_style_available: - enabled = self.opt_ui_style.currentData() == 'calibre' - self.button_adjust_colors.setEnabled(enabled) - - def choose_icon_theme(self): - from calibre.gui2.icon_theme import ChooseTheme - d = ChooseTheme(self) - if d.exec() == QDialog.DialogCode.Accepted: - self.commit_icon_theme = d.commit_changes - self.icon_theme_title = d.new_theme_title or _('Default icons') - self.icon_theme.setText(_('Icon theme: %s') % self.icon_theme_title) - self.changed_signal.emit() - def edit_id_link_rules(self): if IdLinksEditor(self).exec() == QDialog.DialogCode.Accepted: self.changed_signal.emit() @@ -595,12 +491,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): ConfigWidgetBase.initialize(self) self.default_author_link.value = default_author_link() - font = gprefs['font'] - if font is not None: - font = list(font) - font.append(gprefs.get('font_stretch', QFont.Stretch.Unstretched)) - self.current_font = self.initial_font = font - self.update_font_display() self.display_model.initialize() self.em_display_model.initialize() self.bd_vertical_cats_model.initialize() @@ -622,8 +512,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.opt_book_details_css.blockSignals(True) self.opt_book_details_css.setPlainText(P('templates/book_details.css', data=True).decode('utf-8')) self.opt_book_details_css.blockSignals(False) - self.update_color_palette_state() - self.opt_gui_layout.setCurrentIndex(0 if self.gui.layout_container.is_wide else 1) def open_cg_cache(self): open_local_file(self.gui.grid_view.thumbnail_cache.location) @@ -659,11 +547,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def restore_defaults(self): ConfigWidgetBase.restore_defaults(self) self.default_author_link.value = DEFAULT_AUTHOR_LINK - ofont = self.current_font - self.current_font = None - if ofont is not None: - self.changed_signal.emit() - self.update_font_display() self.display_model.restore_defaults() self.em_display_model.restore_defaults() self.bd_vertical_cats_model.restore_defaults() @@ -674,7 +557,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.set_cg_color(gprefs.defaults['cover_grid_color']) self.set_cg_texture(gprefs.defaults['cover_grid_texture']) self.opt_book_details_css.setPlainText(P('templates/book_details.css', allow_user_override=False, data=True).decode('utf-8')) - self.opt_gui_layout.setCurrentIndex(0) def change_cover_grid_color(self): col = QColorDialog.getColor(self.cg_bg_widget.bcol, @@ -702,40 +584,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.set_cg_texture(gprefs.defaults['cover_grid_texture']) self.changed_signal.emit() - def build_font_obj(self): - font_info = qt_app.original_font if self.current_font is None else self.current_font - font = QFont(*(font_info[:4])) - font.setStretch(font_info[4]) - return font - - def update_font_display(self): - font = self.build_font_obj() - fi = QFontInfo(font) - name = str(fi.family()) - - self.font_display.setFont(font) - self.font_display.setText(name + f' [{fi.pointSize()}pt]') - - def change_font(self, *args): - fd = QFontDialog(self.build_font_obj(), self) - if fd.exec() == QDialog.DialogCode.Accepted: - font = fd.selectedFont() - fi = QFontInfo(font) - self.current_font = [str(fi.family()), fi.pointSize(), - fi.weight(), fi.italic(), font.stretch()] - self.update_font_display() - self.changed_signal.emit() - def commit(self, *args): with BusyCursor(): - rr = ConfigWidgetBase.commit(self, *args) - if self.current_font != self.initial_font: - gprefs['font'] = (self.current_font[:4] if self.current_font else - None) - gprefs['font_stretch'] = (self.current_font[4] if self.current_font - is not None else QFont.Stretch.Unstretched) - QApplication.setFont(self.font_display.font()) - rr = True self.display_model.commit() self.em_display_model.commit() self.bd_vertical_cats_model.commit() @@ -744,36 +594,24 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.grid_rules.commit(self.gui.current_db.prefs) gprefs['cover_grid_color'] = tuple(self.cg_bg_widget.bcol.getRgb())[:3] gprefs['cover_grid_texture'] = self.cg_bg_widget.btex - if self.commit_icon_theme is not None: - self.commit_icon_theme() gprefs['default_author_link'] = self.default_author_link.value bcss = self.opt_book_details_css.toPlainText().encode('utf-8') defcss = P('templates/book_details.css', data=True, allow_user_override=False) if defcss == bcss: bcss = None set_data('templates/book_details.css', bcss) - self.gui.layout_container.change_layout(self.gui, self.opt_gui_layout.currentIndex() == 0) - return rr def refresh_gui(self, gui): gui.book_details.book_info.refresh_css() - gui.place_layout_buttons() m = gui.library_view.model() m.update_db_prefs_cache() m.beginResetModel(), m.endResetModel() - self.update_font_display() gui.tags_view.model().reset_tag_browser() gui.library_view.refresh_book_details(force=True) gui.library_view.refresh_grid() gui.library_view.refresh_composite_edit() - gui.library_view.set_row_header_visibility() - for view in 'library memory card_a card_b'.split(): - getattr(gui, view + '_view').set_row_header_visibility() - gui.library_view.refresh_row_sizing() gui.grid_view.refresh_settings() gui.update_auto_scroll_timeout() - gui.sb_all_gui_actions_button.setVisible(gprefs['show_sb_all_actions_button']) - # gui.sb_preferences_button.setVisible(gprefs['show_sb_preference_button']) if __name__ == '__main__': diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index 69784aab70..c8eb4a4e92 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -19,7 +19,7 @@ 0 - + :/images/lt.png:/images/lt.png @@ -27,353 +27,6 @@ &Main interface - - - - - User interface style (&needs restart): - - - opt_ui_style - - - - - - - - - - - - Qt::Horizontal - - - - 20 - 1 - - - - - - - - - - - - - - - - - Change &icon theme - - - - - - - User interface &colors: - - - button_adjust_colors - - - - - - - Adjust &colors - - - - - - - &User interface layout: - - - opt_gui_layout - - - - - - - - 250 - 16777215 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - 20 - - - - - - - Choose &language (needs restart): - - - opt_language - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - 20 - - - - - - - &Round the corners of covers: - - - opt_cover_corner_radius - - - - - - - - - Round the corners of covers in many places they are displayed such as the Cover grid, Book details panel, etc. Adjust the amount of rounding with this setting. A value of between 5 and 10 looks good with most covers sizes. Zero disables rounding and is the default. - - - no rounding - - - - - - - The unit for the cover corner rounding. Either pixel values or as a percentage of the cover size. - - - - - - - - - Enable s&ystem tray icon (needs restart) - - - - - - - Disable all animations. Useful if you have a slow/old computer. - - - Disable &animations - - - - - - - Disable popup notifications when calibre completes jobs such a conversion, sending to device etc. The notifications are sent via the operating system notification facility, if available. - - - Disable n&otifications on job completion - - - - - - - Show the &splash screen at startup - - - - - - - Show &tooltips in the book list - - - - - - - - - &Interface font: - - - font_display - - - - - - - true - - - - - - - - - Change &font (needs restart) - - - - - - - Toolbar - - - - - - - - - Icon si&ze: - - - opt_toolbar_icon_size - - - - - - - - - - Show &text under icons: - - - opt_toolbar_text - - - - - - - Use t&wo lines for the text under the icons (needs restart) - - - - - - - - - - Status bar buttons - - - - - - Show individual buttons to control the layout of the interface rather than a single button with a popup. - - - Show &layout buttons - - - - - - - Show a button that gives quick access to all available actions with a single click. - - - S&how actions button - - - - - - - Qt::Vertical - - - - 10 - 20 - - - - - - - - - - - E&xtra spacing to add between rows in the book list (can be negative): - - - opt_book_list_extra_row_spacing - - - - - - - px - - - -20 - - - - - - - Show &row numbers in the book list - - - - - - - Draw a &grid in the book list - - - - - - - Allow using &drag and drop to merge books - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - @@ -1377,6 +1030,11 @@ columns". Editing with mouse clicks and the Tab key will be disabled.</p QWidget
calibre/gui2/preferences/look_feel_tabs.h
+ + MainInterfaceTab + ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/main_interface.h
+
TbDisplayTab ConfigWidgetBase diff --git a/src/calibre/gui2/preferences/look_feel_tabs/main_interface.py b/src/calibre/gui2/preferences/look_feel_tabs/main_interface.py new file mode 100644 index 0000000000..763bef7122 --- /dev/null +++ b/src/calibre/gui2/preferences/look_feel_tabs/main_interface.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2025, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +from qt.core import QApplication, QDialog, QFont, QFontDialog, QFontInfo + +from calibre.constants import ismacos, iswindows +from calibre.gui2 import config, gprefs, icon_resource_manager, qt_app +from calibre.gui2.preferences import LazyConfigWidgetBase, Setting, set_help_tips +from calibre.gui2.preferences.look_feel_tabs.main_interface_ui import Ui_Form +from calibre.gui2.widgets import BusyCursor +from calibre.utils.config import prefs +from calibre.utils.localization import available_translations, get_lang, get_language + + +class LanguageSetting(Setting): + + def commit(self): + val = self.get_gui_val() + oldval = self.get_config_val() + if val != oldval: + gprefs.set('last_used_language', oldval) + return super().commit() + + +class MainInterfaceTab(LazyConfigWidgetBase, Ui_Form): + + def genesis(self, gui): + self.gui = gui + self.ui_style_available = True + if not ismacos and not iswindows: + self.label_widget_style.setVisible(False) + self.opt_ui_style.setVisible(False) + self.ui_style_available = False + + r = self.register + + try: + self.icon_theme_title = icon_resource_manager.user_theme_title + except Exception: + self.icon_theme_title = _('Default icons') + self.icon_theme.setText(_('Icon theme: %s') % self.icon_theme_title) + self.commit_icon_theme = None + self.icon_theme_button.clicked.connect(self.choose_icon_theme) + + r('ui_style', gprefs, restart_required=True, choices=[(_('System default'), 'system'), (_('calibre style'), 'calibre')]) + r('book_list_tooltips', gprefs) + r('dnd_merge', gprefs) + r('wrap_toolbar_text', gprefs, restart_required=True) + r('show_layout_buttons', gprefs) + r('show_sb_all_actions_button', gprefs) + # r('show_sb_preference_button', gprefs) + r('row_numbers_in_book_list', gprefs) + r('cover_corner_radius', gprefs) + r('cover_corner_radius_unit', gprefs, choices=[(_('Pixels'), 'px'), (_('Percentage'), '%')]) + r('book_list_extra_row_spacing', gprefs) + r('booklist_grid', gprefs) + + def get_esc_lang(l): + if l == 'en': + return 'English' + return get_language(l) + + lang = get_lang() + if lang is None or lang not in available_translations(): + lang = 'en' + items = [(l, get_esc_lang(l)) for l in available_translations() + if l != lang] + if lang != 'en': + items.append(('en', get_esc_lang('en'))) + items.sort(key=lambda x: x[1].lower()) + choices = [(y, x) for x, y in items] + # Default language is the autodetected one + choices = [(get_language(lang), lang)] + choices + lul = gprefs.get('last_used_language') + if lul and (lul in available_translations() or lul in ('en', 'eng')): + choices.insert(1, ((get_language(lul), lul))) + r('language', prefs, choices=choices, restart_required=True, setting=LanguageSetting) + + r('disable_animations', config) + r('systray_icon', config, restart_required=True) + r('show_splash_screen', gprefs) + r('disable_tray_notification', config) + + choices = [(_('Off'), 'off'), (_('Small'), 'small'), + (_('Medium-small'), 'mid-small'), (_('Medium'), 'medium'), (_('Large'), 'large')] + r('toolbar_icon_size', gprefs, choices=choices) + + choices = [(_('If there is enough room'), 'auto'), (_('Always'), 'always'), + (_('Never'), 'never')] + r('toolbar_text', gprefs, choices=choices) + + self.current_font = self.initial_font = None + self.change_font_button.clicked.connect(self.change_font) + + self.opt_ui_style.currentIndexChanged.connect(self.update_color_palette_state) + self.opt_gui_layout.addItem(_('Wide'), 'wide') + self.opt_gui_layout.addItem(_('Narrow'), 'narrow') + self.opt_gui_layout.currentIndexChanged.connect(self.changed_signal) + set_help_tips(self.opt_gui_layout, config.help('gui_layout')) + self.button_adjust_colors.clicked.connect(self.adjust_colors) + + def lazy_initialize(self): + font = gprefs['font'] + if font is not None: + font = list(font) + font.append(gprefs.get('font_stretch', QFont.Stretch.Unstretched)) + self.current_font = self.initial_font = font + self.update_font_display() + self.update_color_palette_state() + self.opt_gui_layout.setCurrentIndex(0 if self.gui.layout_container.is_wide else 1) + + def adjust_colors(self): + from calibre.gui2.dialogs.palette import PaletteConfig + d = PaletteConfig(self) + if d.exec() == QDialog.DialogCode.Accepted: + d.apply_settings() + self.changed_signal.emit() + + def update_color_palette_state(self): + if self.ui_style_available: + enabled = self.opt_ui_style.currentData() == 'calibre' + self.button_adjust_colors.setEnabled(enabled) + + def choose_icon_theme(self): + from calibre.gui2.icon_theme import ChooseTheme + d = ChooseTheme(self) + if d.exec() == QDialog.DialogCode.Accepted: + self.commit_icon_theme = d.commit_changes + self.icon_theme_title = d.new_theme_title or _('Default icons') + self.icon_theme.setText(_('Icon theme: %s') % self.icon_theme_title) + self.changed_signal.emit() + + def build_font_obj(self): + font_info = qt_app.original_font if self.current_font is None else self.current_font + font = QFont(*(font_info[:4])) + font.setStretch(font_info[4]) + return font + + def change_font(self, *args): + fd = QFontDialog(self.build_font_obj(), self) + if fd.exec() == QDialog.DialogCode.Accepted: + font = fd.selectedFont() + fi = QFontInfo(font) + self.current_font = [str(fi.family()), fi.pointSize(), + fi.weight(), fi.italic(), font.stretch()] + self.update_font_display() + self.changed_signal.emit() + + def update_font_display(self): + font = self.build_font_obj() + fi = QFontInfo(font) + name = str(fi.family()) + + self.font_display.setFont(font) + self.font_display.setText(name + f' [{fi.pointSize()}pt]') + + def commit(self): + rr = LazyConfigWidgetBase.commit(self) + with BusyCursor(): + if self.current_font != self.initial_font: + gprefs['font'] = (self.current_font[:4] if self.current_font else None) + gprefs['font_stretch'] = (self.current_font[4] if self.current_font + is not None else QFont.Stretch.Unstretched) + QApplication.setFont(self.font_display.font()) + rr = True + if self.commit_icon_theme is not None: + self.commit_icon_theme() + self.gui.layout_container.change_layout(self.gui, self.opt_gui_layout.currentIndex() == 0) + return rr + + def restore_defaults(self): + LazyConfigWidgetBase.restore_defaults(self) + ofont = self.current_font + self.current_font = None + if ofont is not None: + self.changed_signal.emit() + self.main_interface_tab.update_font_display() + self.opt_gui_layout.setCurrentIndex(0) + self.changed_signal.emit() + + def refresh_gui(self, gui): + gui.place_layout_buttons() + self.update_font_display() + gui.library_view.set_row_header_visibility() + for view in 'library memory card_a card_b'.split(): + getattr(gui, view + '_view').set_row_header_visibility() + gui.library_view.refresh_row_sizing() + gui.sb_all_gui_actions_button.setVisible(gprefs['show_sb_all_actions_button']) + # gui.sb_preferences_button.setVisible(gprefs['show_sb_preference_button']) diff --git a/src/calibre/gui2/preferences/look_feel_tabs/main_interface.ui b/src/calibre/gui2/preferences/look_feel_tabs/main_interface.ui new file mode 100644 index 0000000000..99a5a5592b --- /dev/null +++ b/src/calibre/gui2/preferences/look_feel_tabs/main_interface.ui @@ -0,0 +1,356 @@ + + + Form + + + + + + User interface style (&needs restart): + + + opt_ui_style + + + + + + + + + + + + Qt::Horizontal + + + + 20 + 1 + + + + + + + + + + + + + + + + + Change &icon theme + + + + + + + User interface &colors: + + + button_adjust_colors + + + + + + + Adjust &colors + + + + + + + &User interface layout: + + + opt_gui_layout + + + + + + + + 250 + 16777215 + + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + 20 + + + + + + + Choose &language (needs restart): + + + opt_language + + + + + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + 20 + + + + + + + &Round the corners of covers: + + + opt_cover_corner_radius + + + + + + + + + Round the corners of covers in many places they are displayed such as the Cover grid, Book details panel, etc. Adjust the amount of rounding with this setting. A value of between 5 and 10 looks good with most covers sizes. Zero disables rounding and is the default. + + + no rounding + + + + + + + The unit for the cover corner rounding. Either pixel values or as a percentage of the cover size. + + + + + + + + + Enable s&ystem tray icon (needs restart) + + + + + + + Disable all animations. Useful if you have a slow/old computer. + + + Disable &animations + + + + + + + Disable popup notifications when calibre completes jobs such a conversion, sending to device etc. The notifications are sent via the operating system notification facility, if available. + + + Disable n&otifications on job completion + + + + + + + Show the &splash screen at startup + + + + + + + Show &tooltips in the book list + + + + + + + + + &Interface font: + + + font_display + + + + + + + true + + + + + + + + + Change &font (needs restart) + + + + + + + Toolbar + + + + + + + + + Icon si&ze: + + + opt_toolbar_icon_size + + + + + + + + + + Show &text under icons: + + + opt_toolbar_text + + + + + + + Use t&wo lines for the text under the icons (needs restart) + + + + + + + + + + Status bar buttons + + + + + + Show individual buttons to control the layout of the interface rather than a single button with a popup. + + + Show &layout buttons + + + + + + + Show a button that gives quick access to all available actions with a single click. + + + S&how actions button + + + + + + + Qt::Vertical + + + + 10 + 20 + + + + + + + + + + + E&xtra spacing to add between rows in the book list (can be negative): + + + opt_book_list_extra_row_spacing + + + + + + + px + + + -20 + + + + + + + Show &row numbers in the book list + + + + + + + Draw a &grid in the book list + + + + + + + Allow using &drag and drop to merge books + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + From 762207488793d7e4ddc3b80bad2d776ee99a3ecd Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Fri, 31 Jan 2025 20:29:00 +0100 Subject: [PATCH 3/8] move "Look & Feel/Cover grid" to its own widget --- src/calibre/gui2/preferences/look_feel.py | 211 +-------- src/calibre/gui2/preferences/look_feel.ui | 434 +---------------- .../preferences/look_feel_tabs/__init__.py | 14 + .../preferences/look_feel_tabs/cover_grid.py | 223 +++++++++ .../preferences/look_feel_tabs/cover_grid.ui | 442 ++++++++++++++++++ 5 files changed, 696 insertions(+), 628 deletions(-) create mode 100644 src/calibre/gui2/preferences/look_feel_tabs/cover_grid.py create mode 100644 src/calibre/gui2/preferences/look_feel_tabs/cover_grid.ui diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 172320f9a4..7e8f4b912b 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -7,12 +7,8 @@ __docformat__ = 'restructuredtext en' from collections import defaultdict from functools import partial -from threading import Thread from qt.core import ( - QBrush, - QColor, - QColorDialog, QComboBox, QDialog, QDialogButtonBox, @@ -23,11 +19,8 @@ from qt.core import ( QLabel, QLineEdit, QListWidgetItem, - QPainter, - QPixmap, QPushButton, QSize, - QSizePolicy, Qt, QTableWidget, QTableWidgetItem, @@ -36,15 +29,21 @@ from qt.core import ( pyqtSignal, ) -from calibre import human_readable from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK from calibre.ebooks.metadata.sources.prefs import msprefs -from calibre.gui2 import config, default_author_link, error_dialog, gprefs, open_local_file, question_dialog +from calibre.gui2 import config, default_author_link, error_dialog, gprefs from calibre.gui2.custom_column_widgets import get_field_list as em_get_field_list -from calibre.gui2.library.alternate_views import CM_TO_INCH, auto_height from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.coloring import EditRules -from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, export_layout, import_layout, move_field_down, move_field_up, reset_layout +from calibre.gui2.preferences.look_feel_tabs import ( + DisplayedFields, + export_layout, + import_layout, + move_field_down, + move_field_up, + reset_layout, + selected_rows_metadatas, +) from calibre.gui2.preferences.look_feel_ui import Ui_Form from calibre.gui2.widgets import BusyCursor from calibre.gui2.widgets2 import Dialog @@ -281,44 +280,8 @@ class BDVerticalCats(DisplayedFields): # {{{ # }}} -class Background(QWidget): # {{{ - - def __init__(self, parent): - QWidget.__init__(self, parent) - self.bcol = QColor(*gprefs['cover_grid_color']) - self.btex = gprefs['cover_grid_texture'] - self.update_brush() - self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) - - def update_brush(self): - self.brush = QBrush(self.bcol) - if self.btex: - from calibre.gui2.preferences.texture_chooser import texture_path - path = texture_path(self.btex) - if path: - p = QPixmap(path) - try: - dpr = self.devicePixelRatioF() - except AttributeError: - dpr = self.devicePixelRatio() - p.setDevicePixelRatio(dpr) - self.brush.setTexture(p) - self.update() - - def sizeHint(self): - return QSize(200, 120) - - def paintEvent(self, ev): - painter = QPainter(self) - painter.fillRect(ev.rect(), self.brush) - painter.end() -# }}} - - class ConfigWidget(ConfigWidgetBase, Ui_Form): - size_calculated = pyqtSignal(object) - def genesis(self, gui): self.gui = gui @@ -330,16 +293,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.default_author_link.changed_signal.connect(self.changed_signal) r('bd_show_cover', gprefs) r('bd_overlay_cover_size', gprefs) - r('cover_grid_width', gprefs) - r('cover_grid_height', gprefs) - r('cover_grid_cache_size_multiple', gprefs) - r('cover_grid_disk_cache_size', gprefs) - r('cover_grid_spacing', gprefs) - r('cover_grid_show_title', gprefs) - - r('emblem_size', gprefs) - r('emblem_position', gprefs, choices=[ - (_('Left'), 'left'), (_('Top'), 'top'), (_('Right'), 'right'), (_('Bottom'), 'bottom')]) r('book_details_comments_heading_pos', gprefs, choices=[ (_('Never'), 'hide'), (_('Above text'), 'above'), (_('Beside text'), 'side')]) r('book_details_note_link_icon_width', gprefs) @@ -347,11 +300,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('use_roman_numerals_for_series_number', config) - fm = db.field_metadata - choices = sorted(((fm[k]['name'], k) for k in fm.displayable_field_keys() if fm[k]['name']), - key=lambda x:sort_key(x[0])) - r('field_under_covers_in_grid', db.prefs, choices=choices) - choices = [(_('Default'), 'default'), (_('Compact metadata'), 'alt1'), (_('All on 1 tab'), 'alt2')] r('edit_metadata_single_layout', gprefs, @@ -400,43 +348,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.icon_rules.changed.connect(self.changed_signal) self.tabWidget.addTab(self.icon_rules, QIcon.ic('icon_choose.png'), _('Column &icons')) - self.grid_rules = EditRules(self.emblems_tab) - self.grid_rules.changed.connect(self.changed_signal) - self.emblems_tab.setLayout(QVBoxLayout()) - self.emblems_tab.layout().addWidget(self.grid_rules) - self.tabWidget.setCurrentIndex(0) self.tabWidget.tabBar().setVisible(False) keys = [QKeySequence('F11', QKeySequence.SequenceFormat.PortableText), QKeySequence( 'Ctrl+Shift+F', QKeySequence.SequenceFormat.PortableText)] keys = [str(x.toString(QKeySequence.SequenceFormat.NativeText)) for x in keys] - self.size_calculated.connect(self.update_cg_cache_size, type=Qt.ConnectionType.QueuedConnection) - self.tabWidget.currentChanged.connect(self.tab_changed) - l = self.cg_background_box.layout() - self.cg_bg_widget = w = Background(self) - l.addWidget(w, 0, 0, 3, 1) - self.cover_grid_color_button = b = QPushButton(_('Change &color'), self) - b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) - l.addWidget(b, 0, 1) - b.clicked.connect(self.change_cover_grid_color) - self.cover_grid_texture_button = b = QPushButton(_('Change &background image'), self) - b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) - l.addWidget(b, 1, 1) - b.clicked.connect(self.change_cover_grid_texture) - self.cover_grid_default_appearance_button = b = QPushButton(_('Restore default &appearance'), self) - b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) - l.addWidget(b, 2, 1) - b.clicked.connect(self.restore_cover_grid_appearance) - self.cover_grid_empty_cache.clicked.connect(self.empty_cache) - self.cover_grid_open_cache.clicked.connect(self.open_cg_cache) - connect_lambda(self.cover_grid_smaller_cover.clicked, self, lambda self: self.resize_cover(True)) - connect_lambda(self.cover_grid_larger_cover.clicked, self, lambda self: self.resize_cover(False)) - self.cover_grid_reset_size.clicked.connect(self.cg_reset_size) - self.opt_cover_grid_disk_cache_size.setMinimum(self.gui.grid_view.thumbnail_cache.min_disk_cache) - self.opt_cover_grid_disk_cache_size.setMaximum(self.gui.grid_view.thumbnail_cache.min_disk_cache * 100) - self.opt_cover_grid_width.valueChanged.connect(self.update_aspect_ratio) - self.opt_cover_grid_height.valueChanged.connect(self.update_aspect_ratio) self.opt_book_details_css.textChanged.connect(self.changed_signal) from calibre.gui2.tweak_book.editor.text import get_highlighter, get_theme self.css_highlighter = get_highlighter('css')() @@ -458,35 +375,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): if IdLinksEditor(self).exec() == QDialog.DialogCode.Accepted: self.changed_signal.emit() - @property - def current_cover_size(self): - cval = self.opt_cover_grid_height.value() - wval = self.opt_cover_grid_width.value() - if cval < 0.1: - dpi = self.opt_cover_grid_height.logicalDpiY() - cval = auto_height(self.opt_cover_grid_height) / dpi / CM_TO_INCH - if wval < 0.1: - wval = 0.75 * cval - return wval, cval - - def update_aspect_ratio(self, *args): - width, height = self.current_cover_size - ar = width / height - self.cover_grid_aspect_ratio.setText(_('Current aspect ratio (width/height): %.2g') % ar) - - def resize_cover(self, smaller): - wval, cval = self.current_cover_size - ar = wval / cval - delta = 0.2 * (-1 if smaller else 1) - cval += delta - cval = max(0, cval) - self.opt_cover_grid_height.setValue(cval) - self.opt_cover_grid_width.setValue(cval * ar) - - def cg_reset_size(self): - self.opt_cover_grid_width.setValue(0) - self.opt_cover_grid_height.setValue(0) - def initialize(self): ConfigWidgetBase.initialize(self) @@ -495,55 +383,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.em_display_model.initialize() self.bd_vertical_cats_model.initialize() db = self.gui.current_db - mi = [] - try: - rows = self.gui.current_view().selectionModel().selectedRows() - for row in rows: - if row.isValid(): - mi.append(db.new_api.get_proxy_metadata(db.data.index_to_id(row.row()))) - except: - pass + mi = selected_rows_metadatas() self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules') self.icon_rules.initialize(db.field_metadata, db.prefs, mi, 'column_icon_rules') - self.grid_rules.initialize(db.field_metadata, db.prefs, mi, 'cover_grid_icon_rules') - self.set_cg_color(gprefs['cover_grid_color']) - self.set_cg_texture(gprefs['cover_grid_texture']) - self.update_aspect_ratio() self.opt_book_details_css.blockSignals(True) self.opt_book_details_css.setPlainText(P('templates/book_details.css', data=True).decode('utf-8')) self.opt_book_details_css.blockSignals(False) - def open_cg_cache(self): - open_local_file(self.gui.grid_view.thumbnail_cache.location) - - def update_cg_cache_size(self, size): - self.cover_grid_current_disk_cache.setText( - _('Current space used: %s') % human_readable(size)) - - def tab_changed(self, index): - if self.tabWidget.currentWidget() is self.cover_grid_tab: - self.show_current_cache_usage() - - def show_current_cache_usage(self): - t = Thread(target=self.calc_cache_size) - t.daemon = True - t.start() - - def calc_cache_size(self): - self.size_calculated.emit(self.gui.grid_view.thumbnail_cache.current_size) - - def set_cg_color(self, val): - self.cg_bg_widget.bcol = QColor(*val) - self.cg_bg_widget.update_brush() - - def set_cg_texture(self, val): - self.cg_bg_widget.btex = val - self.cg_bg_widget.update_brush() - - def empty_cache(self): - self.gui.grid_view.thumbnail_cache.empty() - self.calc_cache_size() - def restore_defaults(self): ConfigWidgetBase.restore_defaults(self) self.default_author_link.value = DEFAULT_AUTHOR_LINK @@ -552,38 +398,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.bd_vertical_cats_model.restore_defaults() self.edit_rules.clear() self.icon_rules.clear() - self.grid_rules.clear() self.changed_signal.emit() - self.set_cg_color(gprefs.defaults['cover_grid_color']) - self.set_cg_texture(gprefs.defaults['cover_grid_texture']) self.opt_book_details_css.setPlainText(P('templates/book_details.css', allow_user_override=False, data=True).decode('utf-8')) - def change_cover_grid_color(self): - col = QColorDialog.getColor(self.cg_bg_widget.bcol, - self.gui, _('Choose background color for the Cover grid')) - if col.isValid(): - col = tuple(col.getRgb())[:3] - self.set_cg_color(col) - self.changed_signal.emit() - if self.cg_bg_widget.btex: - if question_dialog( - self, _('Remove background image?'), - _('There is currently a background image set, so the color' - ' you have chosen will not be visible. Remove the background image?')): - self.set_cg_texture(None) - - def change_cover_grid_texture(self): - from calibre.gui2.preferences.texture_chooser import TextureChooser - d = TextureChooser(parent=self, initial=self.cg_bg_widget.btex) - if d.exec() == QDialog.DialogCode.Accepted: - self.set_cg_texture(d.texture) - self.changed_signal.emit() - - def restore_cover_grid_appearance(self): - self.set_cg_color(gprefs.defaults['cover_grid_color']) - self.set_cg_texture(gprefs.defaults['cover_grid_texture']) - self.changed_signal.emit() - def commit(self, *args): with BusyCursor(): self.display_model.commit() @@ -591,9 +408,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.bd_vertical_cats_model.commit() self.edit_rules.commit(self.gui.current_db.prefs) self.icon_rules.commit(self.gui.current_db.prefs) - self.grid_rules.commit(self.gui.current_db.prefs) - gprefs['cover_grid_color'] = tuple(self.cg_bg_widget.bcol.getRgb())[:3] - gprefs['cover_grid_texture'] = self.cg_bg_widget.btex gprefs['default_author_link'] = self.default_author_link.value bcss = self.opt_book_details_css.toPlainText().encode('utf-8') defcss = P('templates/book_details.css', data=True, allow_user_override=False) @@ -608,10 +422,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): m.beginResetModel(), m.endResetModel() gui.tags_view.model().reset_tag_browser() gui.library_view.refresh_book_details(force=True) - gui.library_view.refresh_grid() gui.library_view.refresh_composite_edit() - gui.grid_view.refresh_settings() - gui.update_auto_scroll_timeout() if __name__ == '__main__': diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index c8eb4a4e92..c554566888 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -28,10 +28,7 @@ &Main interface
- - - 0 - + :/images/grid.png:/images/grid.png @@ -39,412 +36,6 @@ Cover &grid - - - &Layout - - - - QFormLayout::ExpandingFieldsGrow - - - - - Control the Cover grid view. You can enable this view by clicking the "Layout" button in the bottom right corner of the main calibre window. - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Field to show &under the covers: - - - opt_field_under_covers_in_grid - - - - - - - - - - Spac&ing between covers: - - - opt_cover_grid_spacing - - - - - - - The spacing between covers. A value of zero means calculate automatically based on cover size. - - - Automatic - - - cm - - - 2 - - - 0.100000000000000 - - - - - - - Background for the Cover grid - - - - - - - - Cover size - - - - - - Make the covers larger, maintaining current aspect ratio. - - - &Larger covers - - - - :/images/plus.png:/images/plus.png - - - - - - - Make the covers smaller, maintaining current aspect ratio. - - - &Smaller covers - - - - :/images/minus.png:/images/minus.png - - - - - - - Cover &height: - - - opt_cover_grid_height - - - - - - - Cover &width: - - - opt_cover_grid_width - - - - - - - The height of displayed covers. -A value of zero means calculate automatically. - - - Automatic - - - cm - - - 1 - - - - - - - Reset size to automatic - - - &Reset size - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - The width of displayed covers. -A value of zero means calculate automatically. - - - Automatic - - - cm - - - 1 - - - - - - - - 0 - 0 - - - - By default, calibre chooses a cover size based on your computer's screen size. You can change the cover size here: - - - true - - - - - - - - - - px - - - 16 - - - 1000 - - - - - - - Size of the emblems (if any) shown &next to the covers: - - - opt_emblem_size - - - - - - - Show a &field (such as title) under the covers - - - - - - - &Location of the emblems shown next to the covers: - - - opt_emblem_position - - - - - - - - - - - &Emblems - - - - - &Performance - - - - - - Caching of covers for improved performance - - - - - - There are two kinds of caches that calibre uses to improve performance when rendering covers in the grid view. A disk cache that is kept on your hard disk and stores the cover thumbnails and an in memory cache used to ensure flicker free rendering of covers. For best results, keep the memory cache small and the disk cache large, unless you have a lot of extra RAM in your computer and don't mind it being used by the memory cache. - - - true - - - - - - - Disable - - - MB - - - 100 - - - 100 - - - - - - - &Empty disk cache - - - - - - - &Open cache folder - - - - - - - Qt::Horizontal - - - - 310 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Number of screenfulls of covers to cache in &memory (keep this small): - - - opt_cover_grid_cache_size_multiple - - - - - - - Maximum amount of &disk space to use for caching thumbnails: - - - opt_cover_grid_disk_cache_size - - - - - - - The maximum number of screenfulls of thumbnails to keep in memory. Increasing this will make rendering faster, at the cost of more memory usage. Note that regardless of this setting, a minimum of one hundred thumbnails are always kept in memory, to ensure flicker free rendering. - - - 2 - - - 100 - - - - - - - - - - - - - - - @@ -1035,6 +626,11 @@ columns". Editing with mouse clicks and the Tab key will be disabled.</p ConfigWidgetBase
calibre/gui2/preferences/look_feel_tabs/main_interface.h
+ + CoverGridTab + ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/cover_grid.h
+
TbDisplayTab ConfigWidgetBase @@ -1069,22 +665,4 @@ columns". Editing with mouse clicks and the Tab key will be disabled.</p - - - opt_cover_grid_show_title - toggled(bool) - opt_field_under_covers_in_grid - setEnabled(bool) - - - 86 - 49 - - - 101 - 51 - - - -
diff --git a/src/calibre/gui2/preferences/look_feel_tabs/__init__.py b/src/calibre/gui2/preferences/look_feel_tabs/__init__.py index 9252a5b93f..d183bbd909 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/__init__.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/__init__.py @@ -11,6 +11,7 @@ from qt.core import QAbstractListModel, QIcon, QItemSelectionModel, Qt from calibre.gui2 import choose_files, choose_save_file, error_dialog from calibre.gui2.book_details import get_field_list +from calibre.gui2.ui import get_gui class DisplayedFields(QAbstractListModel): @@ -154,3 +155,16 @@ def move_field_down(widget, model): sm = widget.selectionModel() sm.select(idx, QItemSelectionModel.SelectionFlag.ClearAndSelect) widget.setCurrentIndex(idx) + + +def selected_rows_metadatas(): + rslt = [] + try: + db = get_gui().current_db + rows = get_gui().current_view().selectionModel().selectedRows() + for row in rows: + if row.isValid(): + rslt.append(db.new_api.get_proxy_metadata(db.data.index_to_id(row.row()))) + except: + pass + return rslt diff --git a/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.py b/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.py new file mode 100644 index 0000000000..5659b49017 --- /dev/null +++ b/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2025, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +from threading import Thread + +from qt.core import QBrush, QColor, QColorDialog, QDialog, QPainter, QPixmap, QPushButton, QSize, QSizePolicy, Qt, QVBoxLayout, QWidget, pyqtSignal + +from calibre import human_readable +from calibre.gui2 import gprefs, open_local_file, question_dialog +from calibre.gui2.library.alternate_views import CM_TO_INCH, auto_height +from calibre.gui2.preferences import LazyConfigWidgetBase +from calibre.gui2.preferences.coloring import EditRules +from calibre.gui2.preferences.look_feel_tabs import selected_rows_metadatas +from calibre.gui2.preferences.look_feel_tabs.cover_grid_ui import Ui_Form +from calibre.gui2.widgets import BusyCursor +from calibre.startup import connect_lambda +from calibre.utils.icu import sort_key + + +class Background(QWidget): + + def __init__(self, parent): + QWidget.__init__(self, parent) + self.bcol = QColor(*gprefs['cover_grid_color']) + self.btex = gprefs['cover_grid_texture'] + self.update_brush() + self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) + + def update_brush(self): + self.brush = QBrush(self.bcol) + if self.btex: + from calibre.gui2.preferences.texture_chooser import texture_path + path = texture_path(self.btex) + if path: + p = QPixmap(path) + try: + dpr = self.devicePixelRatioF() + except AttributeError: + dpr = self.devicePixelRatio() + p.setDevicePixelRatio(dpr) + self.brush.setTexture(p) + self.update() + + def sizeHint(self): + return QSize(200, 120) + + def paintEvent(self, ev): + painter = QPainter(self) + painter.fillRect(ev.rect(), self.brush) + painter.end() + + +class CoverGridTab(LazyConfigWidgetBase, Ui_Form): + + size_calculated = pyqtSignal(object) + + def genesis(self, gui): + self.gui = gui + db = self.gui.library_view.model().db + r = self.register + + r('cover_grid_width', gprefs) + r('cover_grid_height', gprefs) + r('cover_grid_cache_size_multiple', gprefs) + r('cover_grid_disk_cache_size', gprefs) + r('cover_grid_spacing', gprefs) + r('cover_grid_show_title', gprefs) + r('emblem_size', gprefs) + r('emblem_position', gprefs, choices=[ + (_('Left'), 'left'), (_('Top'), 'top'), (_('Right'), 'right'), (_('Bottom'), 'bottom')]) + + fm = db.field_metadata + choices = sorted(((fm[k]['name'], k) for k in fm.displayable_field_keys() if fm[k]['name']), + key=lambda x:sort_key(x[0])) + r('field_under_covers_in_grid', db.prefs, choices=choices) + + self.grid_rules = EditRules(self.emblems_tab) + self.grid_rules.changed.connect(self.changed_signal) + self.emblems_tab.setLayout(QVBoxLayout()) + self.emblems_tab.layout().addWidget(self.grid_rules) + + self.size_calculated.connect(self.update_cg_cache_size, type=Qt.ConnectionType.QueuedConnection) + + l = self.cg_background_box.layout() + self.cg_bg_widget = w = Background(self) + l.addWidget(w, 0, 0, 3, 1) + self.cover_grid_color_button = b = QPushButton(_('Change &color'), self) + b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) + l.addWidget(b, 0, 1) + b.clicked.connect(self.change_cover_grid_color) + self.cover_grid_texture_button = b = QPushButton(_('Change &background image'), self) + b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) + l.addWidget(b, 1, 1) + b.clicked.connect(self.change_cover_grid_texture) + self.cover_grid_default_appearance_button = b = QPushButton(_('Restore default &appearance'), self) + b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) + l.addWidget(b, 2, 1) + b.clicked.connect(self.restore_cover_grid_appearance) + self.cover_grid_empty_cache.clicked.connect(self.empty_cache) + self.cover_grid_open_cache.clicked.connect(self.open_cg_cache) + connect_lambda(self.cover_grid_smaller_cover.clicked, self, lambda self: self.resize_cover(True)) + connect_lambda(self.cover_grid_larger_cover.clicked, self, lambda self: self.resize_cover(False)) + self.cover_grid_reset_size.clicked.connect(self.cg_reset_size) + self.opt_cover_grid_disk_cache_size.setMinimum(self.gui.grid_view.thumbnail_cache.min_disk_cache) + self.opt_cover_grid_disk_cache_size.setMaximum(self.gui.grid_view.thumbnail_cache.min_disk_cache * 100) + self.opt_cover_grid_width.valueChanged.connect(self.update_aspect_ratio) + self.opt_cover_grid_height.valueChanged.connect(self.update_aspect_ratio) + + def lazy_initialize(self): + self.show_current_cache_usage() + + db = self.gui.current_db + self.blockSignals(True) + self.grid_rules.initialize(db.field_metadata, db.prefs, selected_rows_metadatas(), 'cover_grid_icon_rules') + self.blockSignals(False) + self.set_cg_color(gprefs['cover_grid_color']) + self.set_cg_texture(gprefs['cover_grid_texture']) + self.update_aspect_ratio() + + def show_current_cache_usage(self): + t = Thread(target=self.calc_cache_size) + t.daemon = True + t.start() + + def calc_cache_size(self): + self.size_calculated.emit(self.gui.grid_view.thumbnail_cache.current_size) + + @property + def current_cover_size(self): + cval = self.opt_cover_grid_height.value() + wval = self.opt_cover_grid_width.value() + if cval < 0.1: + dpi = self.opt_cover_grid_height.logicalDpiY() + cval = auto_height(self.opt_cover_grid_height) / dpi / CM_TO_INCH + if wval < 0.1: + wval = 0.75 * cval + return wval, cval + + def update_aspect_ratio(self): + width, height = self.current_cover_size + ar = width / height + self.cover_grid_aspect_ratio.setText(_('Current aspect ratio (width/height): %.2g') % ar) + + def resize_cover(self, smaller): + wval, cval = self.current_cover_size + ar = wval / cval + delta = 0.2 * (-1 if smaller else 1) + cval += delta + cval = max(0, cval) + self.opt_cover_grid_height.setValue(cval) + self.opt_cover_grid_width.setValue(cval * ar) + + def cg_reset_size(self): + self.opt_cover_grid_width.setValue(0) + self.opt_cover_grid_height.setValue(0) + + def open_cg_cache(self): + open_local_file(self.gui.grid_view.thumbnail_cache.location) + + def update_cg_cache_size(self, size): + self.cover_grid_current_disk_cache.setText( + _('Current space used: %s') % human_readable(size)) + + def empty_cache(self): + self.gui.grid_view.thumbnail_cache.empty() + self.calc_cache_size() + + def set_cg_color(self, val): + self.cg_bg_widget.bcol = QColor(*val) + self.cg_bg_widget.update_brush() + + def set_cg_texture(self, val): + self.cg_bg_widget.btex = val + self.cg_bg_widget.update_brush() + + def change_cover_grid_color(self): + col = QColorDialog.getColor(self.cg_bg_widget.bcol, + self.gui, _('Choose background color for the Cover grid')) + if col.isValid(): + col = tuple(col.getRgb())[:3] + self.set_cg_color(col) + self.changed_signal.emit() + if self.cg_bg_widget.btex: + if question_dialog( + self, _('Remove background image?'), + _('There is currently a background image set, so the color' + ' you have chosen will not be visible. Remove the background image?')): + self.set_cg_texture(None) + + def change_cover_grid_texture(self): + from calibre.gui2.preferences.texture_chooser import TextureChooser + d = TextureChooser(parent=self, initial=self.cg_bg_widget.btex) + if d.exec() == QDialog.DialogCode.Accepted: + self.set_cg_texture(d.texture) + self.changed_signal.emit() + + def restore_cover_grid_appearance(self): + self.set_cg_color(gprefs.defaults['cover_grid_color']) + self.set_cg_texture(gprefs.defaults['cover_grid_texture']) + self.changed_signal.emit() + + def commit(self): + with BusyCursor(): + self.grid_rules.commit(self.gui.current_db.prefs) + gprefs['cover_grid_color'] = tuple(self.cg_bg_widget.bcol.getRgb())[:3] + gprefs['cover_grid_texture'] = self.cg_bg_widget.btex + return LazyConfigWidgetBase.commit(self) + + def restore_defaults(self): + LazyConfigWidgetBase.restore_defaults(self) + self.grid_rules.clear() + self.set_cg_color(gprefs.defaults['cover_grid_color']) + self.set_cg_texture(gprefs.defaults['cover_grid_texture']) + self.changed_signal.emit() + + def refresh_gui(self, gui): + gui.library_view.refresh_grid() + gui.grid_view.refresh_settings() + gui.update_auto_scroll_timeout() diff --git a/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.ui b/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.ui new file mode 100644 index 0000000000..e54851c90a --- /dev/null +++ b/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.ui @@ -0,0 +1,442 @@ + + + Form + + + + + + 0 + + + + &Layout + + + + QFormLayout::ExpandingFieldsGrow + + + + + Control the Cover grid view. You can enable this view by clicking the "Layout" button in the bottom right corner of the main calibre window. + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Field to show &under the covers: + + + opt_field_under_covers_in_grid + + + + + + + + + + Spac&ing between covers: + + + opt_cover_grid_spacing + + + + + + + The spacing between covers. A value of zero means calculate automatically based on cover size. + + + Automatic + + + cm + + + 2 + + + 0.100000000000000 + + + + + + + Background for the Cover grid + + + + + + + + Cover size + + + + + + Make the covers larger, maintaining current aspect ratio. + + + &Larger covers + + + + :/images/plus.png:/images/plus.png + + + + + + + Make the covers smaller, maintaining current aspect ratio. + + + &Smaller covers + + + + :/images/minus.png:/images/minus.png + + + + + + + Cover &height: + + + opt_cover_grid_height + + + + + + + Cover &width: + + + opt_cover_grid_width + + + + + + + The height of displayed covers. +Aue of zero means calculate automatically. + + + Automatic + + + cm + + + 1 + + + + + + + Reset size to automatic + + + &Reset size + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + The width of displayed covers. +Aue of zero means calculate automatically. + + + Automatic + + + cm + + + 1 + + + + + + + + 0 + 0 + + + + By default, calibre chooses a cover size based on your computer's screen size. You can change the cover size here: + + + true + + + + + + + + + + px + + + 16 + + + 1000 + + + + + + + Size of the emblems (if any) shown &next to the covers: + + + opt_emblem_size + + + + + + + Show a &field (such as title) under the covers + + + + + + + &Location of the emblems shown next to the covers: + + + opt_emblem_position + + + + + + + + + + + &Emblems + + + + + &Performance + + + + + + Caching of covers for improved performance + + + + + + There are two kinds of caches that calibre uses to improve performance when rendering covers in the grid view. A disk cache that is kept on your hard disk and stores the cover thumbnails and an in memory cache used to ensure flicker free rendering of covers. For best results, keep the memory cache small and the disk cache large, unless you have a lot of extra RAM in your computer and don't mind it being used by the memory cache. + + + true + + + + + + + Disable + + + MB + + + 100 + + + 100 + + + + + + + &Empty disk cache + + + + + + + &Open cache folder + + + + + + + Qt::Horizontal + + + + 310 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Number of screenfulls of covers to cache in &memory (keep this small): + + + opt_cover_grid_cache_size_multiple + + + + + + + Maximum amount of &disk space to use for caching thumbnails: + + + opt_cover_grid_disk_cache_size + + + + + + + The maximum number of screenfulls of thumbnails to keep in memory. Increasing this will make rendering faster, at the cost of more memory usage. Note that regardless of this setting, a minimum of one hundred thumbnails are always kept in memory, to ensure flicker free rendering. + + + 2 + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + opt_cover_grid_show_title + toggled(bool) + opt_field_under_covers_in_grid + setEnabled(bool) + + + 86 + 49 + + + 101 + 51 + + + + + From b32e1beaf4832228cde67297733bf88cb578019e Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Sat, 1 Feb 2025 07:38:24 +0100 Subject: [PATCH 4/8] move "Look & Feel/Book details" to its own widget --- src/calibre/gui2/preferences/look_feel.py | 295 +----------------- src/calibre/gui2/preferences/look_feel.ui | 244 +-------------- .../preferences/look_feel_tabs/__init__.py | 65 +++- .../look_feel_tabs/book_details.py | 256 +++++++++++++++ .../look_feel_tabs/book_details.ui | 258 +++++++++++++++ 5 files changed, 586 insertions(+), 532 deletions(-) create mode 100644 src/calibre/gui2/preferences/look_feel_tabs/book_details.py create mode 100644 src/calibre/gui2/preferences/look_feel_tabs/book_details.ui diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 7e8f4b912b..488a569f06 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -5,33 +5,11 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from collections import defaultdict from functools import partial -from qt.core import ( - QComboBox, - QDialog, - QDialogButtonBox, - QFormLayout, - QHeaderView, - QIcon, - QKeySequence, - QLabel, - QLineEdit, - QListWidgetItem, - QPushButton, - QSize, - Qt, - QTableWidget, - QTableWidgetItem, - QVBoxLayout, - QWidget, - pyqtSignal, -) +from qt.core import QIcon, QKeySequence, QListWidgetItem, Qt -from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK -from calibre.ebooks.metadata.sources.prefs import msprefs -from calibre.gui2 import config, default_author_link, error_dialog, gprefs +from calibre.gui2 import gprefs from calibre.gui2.custom_column_widgets import get_field_list as em_get_field_list from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.coloring import EditRules @@ -46,187 +24,6 @@ from calibre.gui2.preferences.look_feel_tabs import ( ) from calibre.gui2.preferences.look_feel_ui import Ui_Form from calibre.gui2.widgets import BusyCursor -from calibre.gui2.widgets2 import Dialog -from calibre.startup import connect_lambda -from calibre.utils.icu import sort_key -from calibre.utils.resources import get_path as P -from calibre.utils.resources import set_data -from polyglot.builtins import iteritems - - -class DefaultAuthorLink(QWidget): # {{{ - - changed_signal = pyqtSignal() - - def __init__(self, parent): - QWidget.__init__(self, parent) - l = QVBoxLayout(parent) - l.addWidget(self) - l.setContentsMargins(0, 0, 0, 0) - l = QFormLayout(self) - l.setContentsMargins(0, 0, 0, 0) - l.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) - self.choices = c = QComboBox() - c.setMinimumContentsLength(30) - for text, data in [ - (_('Search for the author on Goodreads'), 'search-goodreads'), - (_('Search for the author on Amazon'), 'search-amzn'), - (_('Search for the author in your calibre library'), 'search-calibre'), - (_('Search for the author on Wikipedia'), 'search-wikipedia'), - (_('Search for the author on Google Books'), 'search-google'), - (_('Search for the book on Goodreads'), 'search-goodreads-book'), - (_('Search for the book on Amazon'), 'search-amzn-book'), - (_('Search for the book on Google Books'), 'search-google-book'), - (_('Use a custom search URL'), 'url'), - ]: - c.addItem(text, data) - l.addRow(_('Clicking on &author names should:'), c) - self.custom_url = u = QLineEdit(self) - u.setToolTip(_( - 'Enter the URL to search. It should contain the string {0}' - '\nwhich will be replaced by the author name. For example,' - '\n{1}').format('{author}', 'https://en.wikipedia.org/w/index.php?search={author}')) - u.textChanged.connect(self.changed_signal) - u.setPlaceholderText(_('Enter the URL')) - c.currentIndexChanged.connect(self.current_changed) - l.addRow(u) - self.current_changed() - c.currentIndexChanged.connect(self.changed_signal) - - @property - def value(self): - k = self.choices.currentData() - if k == 'url': - return self.custom_url.text() - return k if k != DEFAULT_AUTHOR_LINK else None - - @value.setter - def value(self, val): - i = self.choices.findData(val) - if i < 0: - i = self.choices.findData('url') - self.custom_url.setText(val) - self.choices.setCurrentIndex(i) - - def current_changed(self): - k = self.choices.currentData() - self.custom_url.setVisible(k == 'url') -# }}} - - -# IdLinksEditor {{{ -class IdLinksRuleEdit(Dialog): - - def __init__(self, key='', name='', template='', parent=None): - title = _('Edit rule') if key else _('Create a new rule') - Dialog.__init__(self, title=title, name='id-links-rule-editor', parent=parent) - self.key.setText(key), self.nw.setText(name), self.template.setText(template or 'https://example.com/{id}') - if self.size().height() < self.sizeHint().height(): - self.resize(self.sizeHint()) - - @property - def rule(self): - return self.key.text().lower(), self.nw.text(), self.template.text() - - def setup_ui(self): - self.l = l = QFormLayout(self) - l.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) - l.addRow(QLabel(_( - 'The key of the identifier, for example, in isbn:XXX, the key is "isbn"'))) - self.key = k = QLineEdit(self) - l.addRow(_('&Key:'), k) - l.addRow(QLabel(_( - 'The name that will appear in the Book details panel'))) - self.nw = n = QLineEdit(self) - l.addRow(_('&Name:'), n) - la = QLabel(_( - 'The template used to create the link.' - ' The placeholder {0} in the template will be replaced' - ' with the actual identifier value. Use {1} to avoid the value' - ' being quoted.').format('{id}', '{id_unquoted}')) - la.setWordWrap(True) - l.addRow(la) - self.template = t = QLineEdit(self) - l.addRow(_('&Template:'), t) - t.selectAll() - t.setFocus(Qt.FocusReason.OtherFocusReason) - l.addWidget(self.bb) - - def accept(self): - r = self.rule - for i, which in enumerate([_('Key'), _('Name'), _('Template')]): - if not r[i]: - return error_dialog(self, _('Value needed'), _( - 'The %s field cannot be empty') % which, show=True) - Dialog.accept(self) - - -class IdLinksEditor(Dialog): - - def __init__(self, parent=None): - Dialog.__init__(self, title=_('Create rules for identifiers'), name='id-links-rules-editor', parent=parent) - - def setup_ui(self): - self.l = l = QVBoxLayout(self) - self.la = la = QLabel(_( - 'Create rules to convert identifiers into links.')) - la.setWordWrap(True) - l.addWidget(la) - items = [] - for k, lx in iteritems(msprefs['id_link_rules']): - for n, t in lx: - items.append((k, n, t)) - items.sort(key=lambda x: sort_key(x[1])) - self.table = t = QTableWidget(len(items), 3, self) - t.setHorizontalHeaderLabels([_('Key'), _('Name'), _('Template')]) - for r, (key, val, template) in enumerate(items): - t.setItem(r, 0, QTableWidgetItem(key)) - t.setItem(r, 1, QTableWidgetItem(val)) - t.setItem(r, 2, QTableWidgetItem(template)) - l.addWidget(t) - t.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch) - self.cb = b = QPushButton(QIcon.ic('plus.png'), _('&Add rule'), self) - connect_lambda(b.clicked, self, lambda self: self.edit_rule()) - self.bb.addButton(b, QDialogButtonBox.ButtonRole.ActionRole) - self.rb = b = QPushButton(QIcon.ic('minus.png'), _('&Remove rule'), self) - connect_lambda(b.clicked, self, lambda self: self.remove_rule()) - self.bb.addButton(b, QDialogButtonBox.ButtonRole.ActionRole) - self.eb = b = QPushButton(QIcon.ic('modified.png'), _('&Edit rule'), self) - connect_lambda(b.clicked, self, lambda self: self.edit_rule(self.table.currentRow())) - self.bb.addButton(b, QDialogButtonBox.ButtonRole.ActionRole) - l.addWidget(self.bb) - - def sizeHint(self): - return QSize(700, 550) - - def accept(self): - rules = defaultdict(list) - for r in range(self.table.rowCount()): - def item(c): - return self.table.item(r, c).text() - rules[item(0)].append([item(1), item(2)]) - msprefs['id_link_rules'] = dict(rules) - Dialog.accept(self) - - def edit_rule(self, r=-1): - key = name = template = '' - if r > -1: - key, name, template = (self.table.item(r, c).text() for c in range(3)) - d = IdLinksRuleEdit(key, name, template, self) - if d.exec() == QDialog.DialogCode.Accepted: - if r < 0: - self.table.setRowCount(self.table.rowCount() + 1) - r = self.table.rowCount() - 1 - rule = d.rule - for c in range(3): - self.table.setItem(r, c, QTableWidgetItem(rule[c])) - self.table.scrollToItem(self.table.item(r, 0)) - - def remove_rule(self): - r = self.table.currentRow() - if r > -1: - self.table.removeRow(r) -# }}} class EMDisplayedFields(DisplayedFields): # {{{ @@ -246,40 +43,6 @@ class EMDisplayedFields(DisplayedFields): # {{{ # }}} -class BDVerticalCats(DisplayedFields): # {{{ - - def __init__(self, db, parent=None, category_icons=None): - DisplayedFields.__init__(self, db, parent, category_icons=category_icons) - from calibre.gui2.ui import get_gui - self.gui = get_gui() - - def initialize(self, use_defaults=False, pref_data_override=None): - fm = self.db.field_metadata - cats = [k for k in fm if fm[k]['name'] and fm[k]['is_multiple'] and not k.startswith('#')] - cats.append('path') - cats.extend([k for k in fm if fm[k]['name'] and fm[k]['is_multiple'] and k.startswith('#')]) - ans = [] - if use_defaults: - ans = [[k, False] for k in cats] - self.changed = True - elif pref_data_override: - ph = dict(pref_data_override) - ans = [[k, ph.get(k, False)] for k in cats] - self.changed = True - else: - vertical_cats = self.db.prefs.get('book_details_vertical_categories') or () - for key in cats: - ans.append([key, key in vertical_cats]) - self.beginResetModel() - self.fields = ans - self.endResetModel() - - def commit(self): - if self.changed: - self.db.prefs.set('book_details_vertical_categories', [k for k,v in self.fields if v]) -# }}} - - class ConfigWidget(ConfigWidgetBase, Ui_Form): def genesis(self, gui): @@ -289,17 +52,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r = self.register - self.default_author_link = DefaultAuthorLink(self.default_author_link_container) - self.default_author_link.changed_signal.connect(self.changed_signal) - r('bd_show_cover', gprefs) - r('bd_overlay_cover_size', gprefs) - r('book_details_comments_heading_pos', gprefs, choices=[ - (_('Never'), 'hide'), (_('Above text'), 'above'), (_('Beside text'), 'side')]) - r('book_details_note_link_icon_width', gprefs) - self.id_links_button.clicked.connect(self.edit_id_link_rules) - - r('use_roman_numerals_for_series_number', config) - choices = [(_('Default'), 'default'), (_('Compact metadata'), 'alt1'), (_('All on 1 tab'), 'alt2')] r('edit_metadata_single_layout', gprefs, @@ -315,15 +67,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('edit_metadata_single_cc_label_length', gprefs) r('edit_metadata_templates_only_F2_on_booklist', gprefs) - self.display_model = DisplayedFields(self.gui.current_db, self.field_display_order) - self.display_model.dataChanged.connect(self.changed_signal) - self.field_display_order.setModel(self.display_model) - mu = partial(move_field_up, self.field_display_order, self.display_model) - md = partial(move_field_down, self.field_display_order, self.display_model) - self.df_up_button.clicked.connect(mu) - self.df_down_button.clicked.connect(md) - self.field_display_order.set_movement_functions(mu, md) - self.em_display_model = EMDisplayedFields(self.gui.current_db, self.em_display_order) self.em_display_model.dataChanged.connect(self.changed_signal) self.em_display_order.setModel(self.em_display_model) @@ -336,10 +79,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.em_import_layout_button.clicked.connect(partial(import_layout, self, model=self.em_display_model)) self.em_reset_layout_button.clicked.connect(partial(reset_layout, model=self.em_display_model)) - self.bd_vertical_cats_model = BDVerticalCats(self.gui.current_db, self.tb_hierarchy_tab.tb_hierarchical_cats) - self.bd_vertical_cats_model.dataChanged.connect(self.changed_signal) - self.bd_vertical_cats.setModel(self.bd_vertical_cats_model) - self.edit_rules = EditRules(self.tabWidget) self.edit_rules.changed.connect(self.changed_signal) self.tabWidget.addTab(self.edit_rules, QIcon.ic('format-fill-color.png'), _('Column &coloring')) @@ -354,11 +93,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): 'Ctrl+Shift+F', QKeySequence.SequenceFormat.PortableText)] keys = [str(x.toString(QKeySequence.SequenceFormat.NativeText)) for x in keys] - self.opt_book_details_css.textChanged.connect(self.changed_signal) - from calibre.gui2.tweak_book.editor.text import get_highlighter, get_theme - self.css_highlighter = get_highlighter('css')() - self.css_highlighter.apply_theme(get_theme(None)) - self.css_highlighter.set_document(self.opt_book_details_css.document()) for i in range(self.tabWidget.count()): self.sections_view.addItem(QListWidgetItem(self.tabWidget.tabIcon(i), self.tabWidget.tabText(i).replace('&', ''))) self.sections_view.setCurrentRow(self.tabWidget.currentIndex()) @@ -371,58 +105,33 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def initial_tab_changed(self): self.sections_view.setCurrentRow(self.tabWidget.currentIndex()) - def edit_id_link_rules(self): - if IdLinksEditor(self).exec() == QDialog.DialogCode.Accepted: - self.changed_signal.emit() - def initialize(self): ConfigWidgetBase.initialize(self) - self.default_author_link.value = default_author_link() - self.display_model.initialize() self.em_display_model.initialize() - self.bd_vertical_cats_model.initialize() db = self.gui.current_db mi = selected_rows_metadatas() self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules') self.icon_rules.initialize(db.field_metadata, db.prefs, mi, 'column_icon_rules') - self.opt_book_details_css.blockSignals(True) - self.opt_book_details_css.setPlainText(P('templates/book_details.css', data=True).decode('utf-8')) - self.opt_book_details_css.blockSignals(False) def restore_defaults(self): ConfigWidgetBase.restore_defaults(self) - self.default_author_link.value = DEFAULT_AUTHOR_LINK - self.display_model.restore_defaults() self.em_display_model.restore_defaults() - self.bd_vertical_cats_model.restore_defaults() self.edit_rules.clear() self.icon_rules.clear() self.changed_signal.emit() - self.opt_book_details_css.setPlainText(P('templates/book_details.css', allow_user_override=False, data=True).decode('utf-8')) def commit(self, *args): with BusyCursor(): - self.display_model.commit() self.em_display_model.commit() - self.bd_vertical_cats_model.commit() self.edit_rules.commit(self.gui.current_db.prefs) self.icon_rules.commit(self.gui.current_db.prefs) - gprefs['default_author_link'] = self.default_author_link.value - bcss = self.opt_book_details_css.toPlainText().encode('utf-8') - defcss = P('templates/book_details.css', data=True, allow_user_override=False) - if defcss == bcss: - bcss = None - set_data('templates/book_details.css', bcss) def refresh_gui(self, gui): - gui.book_details.book_info.refresh_css() m = gui.library_view.model() m.update_db_prefs_cache() m.beginResetModel(), m.endResetModel() gui.tags_view.model().reset_tag_browser() - gui.library_view.refresh_book_details(force=True) - gui.library_view.refresh_composite_edit() if __name__ == '__main__': diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index c554566888..c2a8de4f12 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -37,7 +37,7 @@ Cover &grid
- + :/images/book.png:/images/book.png @@ -45,243 +45,6 @@ &Book details - - - - - Text styling - - - - - - - - - - - - Create rules to convert &identifiers into links - - - - - - - - 0 - 0 - - - - - - - - <p>Check the box if you want the category's values displayed on separate lines instead of separated by commas</p> - - - Categories on separate lines - - - - - - true - - - - - - - - - - Select displayed metadata - - - - - - Move down. Keyboard shortcut: Ctrl-Down arrow - - - - :/images/arrow-down.png:/images/arrow-down.png - - - - - - - Move up. Keyboard shortcut: Ctrl-Up arrow - - - - :/images/arrow-up.png:/images/arrow-up.png - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - true - - - - - - - <p>Note: <b>comments</b>-like columns will always - be displayed at the end unless their "Heading position" is - "Show heading to the side".</p> - - - true - - - - - - - - - - - - Show &cover - - - - - - - Show the size of the book's cover in pixels - - - Show cover &size - - - - - - - Use &Roman numerals for series - - - true - - - - - - - Qt::Horizontal - - - - 20 - 0 - - - - - - - - - - - - Show comments &heading: - - - opt_book_details_comments_heading_pos - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 10 - 0 - - - - - - - - Link and note icon si&ze: - - - opt_book_details_note_link_icon_width - - - - - - - Increase or decrease the size of the links and notes icons by this number. Larger -than one increases the icon size while smaller than one decreases it. - - - 1 - - - 0.5 - - - 4.0 - - - 0.1 - - - - - - - Qt::Horizontal - - - - 30 - 0 - - - - - - - @@ -631,6 +394,11 @@ columns". Editing with mouse clicks and the Tab key will be disabled.</p ConfigWidgetBase
calibre/gui2/preferences/look_feel_tabs/cover_grid.h
+ + BookDetailsTab + ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/book_details.h
+
TbDisplayTab ConfigWidgetBase diff --git a/src/calibre/gui2/preferences/look_feel_tabs/__init__.py b/src/calibre/gui2/preferences/look_feel_tabs/__init__.py index d183bbd909..4b2ad7211e 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/__init__.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/__init__.py @@ -7,13 +7,76 @@ __docformat__ = 'restructuredtext en' import json -from qt.core import QAbstractListModel, QIcon, QItemSelectionModel, Qt +from qt.core import QAbstractListModel, QComboBox, QFormLayout, QIcon, QItemSelectionModel, QLineEdit, Qt, QVBoxLayout, QWidget, pyqtSignal +from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK from calibre.gui2 import choose_files, choose_save_file, error_dialog from calibre.gui2.book_details import get_field_list from calibre.gui2.ui import get_gui +class DefaultAuthorLink(QWidget): + + changed_signal = pyqtSignal() + + def __init__(self, parent): + QWidget.__init__(self, parent) + l = QVBoxLayout() + l.addWidget(self) + l.setContentsMargins(0, 0, 0, 0) + l = QFormLayout(self) + l.setContentsMargins(0, 0, 0, 0) + l.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) + self.choices = c = QComboBox() + c.setMinimumContentsLength(30) + for text, data in [ + (_('Search for the author on Goodreads'), 'search-goodreads'), + (_('Search for the author on Amazon'), 'search-amzn'), + (_('Search for the author in your calibre library'), 'search-calibre'), + (_('Search for the author on Wikipedia'), 'search-wikipedia'), + (_('Search for the author on Google Books'), 'search-google'), + (_('Search for the book on Goodreads'), 'search-goodreads-book'), + (_('Search for the book on Amazon'), 'search-amzn-book'), + (_('Search for the book on Google Books'), 'search-google-book'), + (_('Use a custom search URL'), 'url'), + ]: + c.addItem(text, data) + l.addRow(_('Clicking on &author names should:'), c) + self.custom_url = u = QLineEdit(self) + u.setToolTip(_( + 'Enter the URL to search. It should contain the string {0}' + '\nwhich will be replaced by the author name. For example,' + '\n{1}').format('{author}', 'https://en.wikipedia.org/w/index.php?search={author}')) + u.textChanged.connect(self.changed_signal) + u.setPlaceholderText(_('Enter the URL')) + c.currentIndexChanged.connect(self.current_changed) + l.addRow(u) + self.current_changed() + c.currentIndexChanged.connect(self.changed_signal) + + @property + def value(self): + k = self.choices.currentData() + if k == 'url': + return self.custom_url.text() + return k if k != DEFAULT_AUTHOR_LINK else None + + @value.setter + def value(self, val): + i = self.choices.findData(val) + if i < 0: + i = self.choices.findData('url') + self.custom_url.setText(val) + self.choices.setCurrentIndex(i) + + def current_changed(self): + k = self.choices.currentData() + self.custom_url.setVisible(k == 'url') + + def restore_defaults(self): + self.value = DEFAULT_AUTHOR_LINK + + class DisplayedFields(QAbstractListModel): def __init__(self, db, parent=None, pref_name=None, category_icons=None): diff --git a/src/calibre/gui2/preferences/look_feel_tabs/book_details.py b/src/calibre/gui2/preferences/look_feel_tabs/book_details.py new file mode 100644 index 0000000000..1f295b1225 --- /dev/null +++ b/src/calibre/gui2/preferences/look_feel_tabs/book_details.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2025, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +from collections import defaultdict +from functools import partial + +from qt.core import ( + QDialog, + QDialogButtonBox, + QFormLayout, + QHeaderView, + QIcon, + QLabel, + QLineEdit, + QPushButton, + QSize, + Qt, + QTableWidget, + QTableWidgetItem, + QVBoxLayout, +) + +from calibre.ebooks.metadata.sources.prefs import msprefs +from calibre.gui2 import config, default_author_link, error_dialog, gprefs +from calibre.gui2.preferences import LazyConfigWidgetBase +from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, move_field_down, move_field_up +from calibre.gui2.preferences.look_feel_tabs.book_details_ui import Ui_Form +from calibre.gui2.widgets import BusyCursor +from calibre.gui2.widgets2 import Dialog +from calibre.startup import connect_lambda +from calibre.utils.icu import sort_key +from calibre.utils.resources import set_data +from polyglot.builtins import iteritems + + +class IdLinksRuleEdit(Dialog): + + def __init__(self, key='', name='', template='', parent=None): + title = _('Edit rule') if key else _('Create a new rule') + Dialog.__init__(self, title=title, name='id-links-rule-editor', parent=parent) + self.key.setText(key), self.nw.setText(name), self.template.setText(template or 'https://example.com/{id}') + if self.size().height() < self.sizeHint().height(): + self.resize(self.sizeHint()) + + @property + def rule(self): + return self.key.text().lower(), self.nw.text(), self.template.text() + + def setup_ui(self): + self.l = l = QFormLayout(self) + l.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) + l.addRow(QLabel(_( + 'The key of the identifier, for example, in isbn:XXX, the key is "isbn"'))) + self.key = k = QLineEdit(self) + l.addRow(_('&Key:'), k) + l.addRow(QLabel(_( + 'The name that will appear in the Book details panel'))) + self.nw = n = QLineEdit(self) + l.addRow(_('&Name:'), n) + la = QLabel(_( + 'The template used to create the link.' + ' The placeholder {0} in the template will be replaced' + ' with the actual identifier value. Use {1} to avoid the value' + ' being quoted.').format('{id}', '{id_unquoted}')) + la.setWordWrap(True) + l.addRow(la) + self.template = t = QLineEdit(self) + l.addRow(_('&Template:'), t) + t.selectAll() + t.setFocus(Qt.FocusReason.OtherFocusReason) + l.addWidget(self.bb) + + def accept(self): + r = self.rule + for i, which in enumerate([_('Key'), _('Name'), _('Template')]): + if not r[i]: + return error_dialog(self, _('Value needed'), _( + 'The %s field cannot be empty') % which, show=True) + Dialog.accept(self) + + +class IdLinksEditor(Dialog): + + def __init__(self, parent=None): + Dialog.__init__(self, title=_('Create rules for identifiers'), name='id-links-rules-editor', parent=parent) + + def setup_ui(self): + self.l = l = QVBoxLayout(self) + self.la = la = QLabel(_( + 'Create rules to convert identifiers into links.')) + la.setWordWrap(True) + l.addWidget(la) + items = [] + for k, lx in iteritems(msprefs['id_link_rules']): + for n, t in lx: + items.append((k, n, t)) + items.sort(key=lambda x: sort_key(x[1])) + self.table = t = QTableWidget(len(items), 3, self) + t.setHorizontalHeaderLabels([_('Key'), _('Name'), _('Template')]) + for r, (key, val, template) in enumerate(items): + t.setItem(r, 0, QTableWidgetItem(key)) + t.setItem(r, 1, QTableWidgetItem(val)) + t.setItem(r, 2, QTableWidgetItem(template)) + l.addWidget(t) + t.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch) + self.cb = b = QPushButton(QIcon.ic('plus.png'), _('&Add rule'), self) + connect_lambda(b.clicked, self, lambda self: self.edit_rule()) + self.bb.addButton(b, QDialogButtonBox.ButtonRole.ActionRole) + self.rb = b = QPushButton(QIcon.ic('minus.png'), _('&Remove rule'), self) + connect_lambda(b.clicked, self, lambda self: self.remove_rule()) + self.bb.addButton(b, QDialogButtonBox.ButtonRole.ActionRole) + self.eb = b = QPushButton(QIcon.ic('modified.png'), _('&Edit rule'), self) + connect_lambda(b.clicked, self, lambda self: self.edit_rule(self.table.currentRow())) + self.bb.addButton(b, QDialogButtonBox.ButtonRole.ActionRole) + l.addWidget(self.bb) + + def sizeHint(self): + return QSize(700, 550) + + def accept(self): + rules = defaultdict(list) + for r in range(self.table.rowCount()): + def item(c): + return self.table.item(r, c).text() + rules[item(0)].append([item(1), item(2)]) + msprefs['id_link_rules'] = dict(rules) + Dialog.accept(self) + + def edit_rule(self, r=-1): + key = name = template = '' + if r > -1: + key, name, template = (self.table.item(r, c).text() for c in range(3)) + d = IdLinksRuleEdit(key, name, template, self) + if d.exec() == QDialog.DialogCode.Accepted: + if r < 0: + self.table.setRowCount(self.table.rowCount() + 1) + r = self.table.rowCount() - 1 + rule = d.rule + for c in range(3): + self.table.setItem(r, c, QTableWidgetItem(rule[c])) + self.table.scrollToItem(self.table.item(r, 0)) + + def remove_rule(self): + r = self.table.currentRow() + if r > -1: + self.table.removeRow(r) + + +class BDVerticalCats(DisplayedFields): + + def __init__(self, db, parent=None, category_icons=None): + DisplayedFields.__init__(self, db, parent, category_icons=category_icons) + from calibre.gui2.ui import get_gui + self.gui = get_gui() + + def initialize(self, use_defaults=False, pref_data_override=None): + fm = self.db.field_metadata + cats = [k for k in fm if fm[k]['name'] and fm[k]['is_multiple'] and not k.startswith('#')] + cats.append('path') + cats.extend([k for k in fm if fm[k]['name'] and fm[k]['is_multiple'] and k.startswith('#')]) + ans = [] + if use_defaults: + ans = [[k, False] for k in cats] + self.changed = True + elif pref_data_override: + ph = dict(pref_data_override) + ans = [[k, ph.get(k, False)] for k in cats] + self.changed = True + else: + vertical_cats = self.db.prefs.get('book_details_vertical_categories') or () + for key in cats: + ans.append([key, key in vertical_cats]) + self.beginResetModel() + self.fields = ans + self.endResetModel() + + def commit(self): + if self.changed: + self.db.prefs.set('book_details_vertical_categories', [k for k,v in self.fields if v]) + + +class BookDetailsTab(LazyConfigWidgetBase, Ui_Form): + + def genesis(self, gui): + self.gui = gui + r = self.register + + self.default_author_link.changed_signal.connect(self.changed_signal) + r('bd_show_cover', gprefs) + r('bd_overlay_cover_size', gprefs) + r('book_details_comments_heading_pos', gprefs, choices=[ + (_('Never'), 'hide'), (_('Above text'), 'above'), (_('Beside text'), 'side')]) + r('book_details_note_link_icon_width', gprefs) + self.id_links_button.clicked.connect(self.edit_id_link_rules) + + r('use_roman_numerals_for_series_number', config) + + self.bd_vertical_cats_model = BDVerticalCats(self.gui.current_db, parent=self) + self.bd_vertical_cats_model.dataChanged.connect(self.changed_signal) + self.bd_vertical_cats.setModel(self.bd_vertical_cats_model) + + self.display_model = DisplayedFields(self.gui.current_db, self.field_display_order) + self.display_model.dataChanged.connect(self.changed_signal) + self.field_display_order.setModel(self.display_model) + mu = partial(move_field_up, self.field_display_order, self.display_model) + md = partial(move_field_down, self.field_display_order, self.display_model) + self.df_up_button.clicked.connect(mu) + self.df_down_button.clicked.connect(md) + self.field_display_order.set_movement_functions(mu, md) + + self.opt_book_details_css.textChanged.connect(self.changed_signal) + from calibre.gui2.tweak_book.editor.text import get_highlighter, get_theme + self.css_highlighter = get_highlighter('css')() + self.css_highlighter.apply_theme(get_theme(None)) + self.css_highlighter.set_document(self.opt_book_details_css.document()) + + def lazy_initialize(self): + self.default_author_link.value = default_author_link() + self.display_model.initialize() + self.blockSignals(True) + self.bd_vertical_cats_model.initialize() + self.opt_book_details_css.setPlainText(P('templates/book_details.css', data=True).decode('utf-8')) + self.blockSignals(False) + + def edit_id_link_rules(self): + if IdLinksEditor(self).exec() == QDialog.DialogCode.Accepted: + self.changed_signal.emit() + + def commit(self): + with BusyCursor(): + self.display_model.commit() + self.bd_vertical_cats_model.commit() + gprefs['default_author_link'] = self.default_author_link.value + bcss = self.opt_book_details_css.toPlainText().encode('utf-8') + defcss = P('templates/book_details.css', data=True, allow_user_override=False) + if defcss == bcss: + bcss = None + set_data('templates/book_details.css', bcss) + return LazyConfigWidgetBase.commit(self) + + def restore_defaults(self): + LazyConfigWidgetBase.restore_defaults(self) + self.default_author_link.restore_defaults() + self.display_model.restore_defaults() + self.bd_vertical_cats_model.restore_defaults() + self.opt_book_details_css.setPlainText(P('templates/book_details.css', allow_user_override=False, data=True).decode('utf-8')) + self.changed_signal.emit() + + def refresh_gui(self, gui): + gui.book_details.book_info.refresh_css() + gui.library_view.refresh_book_details(force=True) + gui.library_view.refresh_composite_edit() diff --git a/src/calibre/gui2/preferences/look_feel_tabs/book_details.ui b/src/calibre/gui2/preferences/look_feel_tabs/book_details.ui new file mode 100644 index 0000000000..6b974614c5 --- /dev/null +++ b/src/calibre/gui2/preferences/look_feel_tabs/book_details.ui @@ -0,0 +1,258 @@ + + + Form + + + + + + Text styling + + + + + + + + + + + + Create rules to convert &identifiers into links + + + + + + + + 0 + 0 + + + + + + + + <p>Check the box if you want the category's values displayed on separate lines instead of separated by commas</p> + + + Categories on separate lines + + + + + + true + + + + + + + + + + Select displayed metadata + + + + + + Move down. Keyboard shortcut: Ctrl-Down arrow + + + + :/images/arrow-down.png:/images/arrow-down.png + + + + + + + Move up. Keyboard shortcut: Ctrl-Up arrow + + + + :/images/arrow-up.png:/images/arrow-up.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + + + + <p>Note: <b>comments</b>-like columns will always + be displayed at the end unless their "Heading position" is + "Show heading to the side".</p> + + + true + + + + + + + + + + + + Show &cover + + + + + + + Show the size of the book's cover in pixels + + + Show cover &size + + + + + + + Use &Roman numerals for series + + + true + + + + + + + Qt::Horizontal + + + + 20 + 0 + + + + + + + + + + + + Show comments &heading: + + + opt_book_details_comments_heading_pos + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 10 + 0 + + + + + + + + Link and note icon si&ze: + + + opt_book_details_note_link_icon_width + + + + + + + Increase or decrease the size of the links and notes icons by this number. Larger +than one increases the icon size while smaller than one decreases it. + + + 1 + + + 0.5 + + + 4.0 + + + 0.1 + + + + + + + Qt::Horizontal + + + + 30 + 0 + + + + + + + + + + + ListViewWithMoveByKeyPress + QListView +
calibre/gui2/preferences.h
+
+ + DefaultAuthorLink + QWidget +
calibre/gui2/preferences/look_feel_tabs.h
+
+
+ + + +
From 78bfa97e03aaf4bd48b05f0fbbd70c4a163cdf98 Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Sat, 1 Feb 2025 08:28:00 +0100 Subject: [PATCH 5/8] move "Look & Feel/Edit_metadata" to its own widget --- src/calibre/gui2/preferences/look_feel.py | 65 +---- src/calibre/gui2/preferences/look_feel.ui | 263 +---------------- .../look_feel_tabs/edit_metadata.py | 77 +++++ .../look_feel_tabs/edit_metadata.ui | 274 ++++++++++++++++++ 4 files changed, 358 insertions(+), 321 deletions(-) create mode 100644 src/calibre/gui2/preferences/look_feel_tabs/edit_metadata.py create mode 100644 src/calibre/gui2/preferences/look_feel_tabs/edit_metadata.ui diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 488a569f06..4cf696b74b 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -5,80 +5,20 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from functools import partial - from qt.core import QIcon, QKeySequence, QListWidgetItem, Qt -from calibre.gui2 import gprefs -from calibre.gui2.custom_column_widgets import get_field_list as em_get_field_list from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.coloring import EditRules -from calibre.gui2.preferences.look_feel_tabs import ( - DisplayedFields, - export_layout, - import_layout, - move_field_down, - move_field_up, - reset_layout, - selected_rows_metadatas, -) +from calibre.gui2.preferences.look_feel_tabs import selected_rows_metadatas from calibre.gui2.preferences.look_feel_ui import Ui_Form from calibre.gui2.widgets import BusyCursor -class EMDisplayedFields(DisplayedFields): # {{{ - def __init__(self, db, parent=None): - DisplayedFields.__init__(self, db, parent) - - def initialize(self, use_defaults=False, pref_data_override=None): - self.beginResetModel() - self.fields = [[x[0], x[1]] for x in - em_get_field_list(self.db, use_defaults=use_defaults, pref_data_override=pref_data_override)] - self.endResetModel() - self.changed = True - - def commit(self): - if self.changed: - self.db.new_api.set_pref('edit_metadata_custom_columns_to_display', self.fields) -# }}} - - class ConfigWidget(ConfigWidgetBase, Ui_Form): def genesis(self, gui): self.gui = gui - db = gui.library_view.model().db - - r = self.register - - choices = [(_('Default'), 'default'), (_('Compact metadata'), 'alt1'), - (_('All on 1 tab'), 'alt2')] - r('edit_metadata_single_layout', gprefs, - choices=[(_('Default'), 'default'), (_('Compact metadata'), 'alt1'), - (_('All on 1 tab'), 'alt2')]) - r('edit_metadata_ignore_display_order', db.prefs) - r('edit_metadata_elision_point', gprefs, - choices=[(_('Left'), 'left'), (_('Middle'), 'middle'), - (_('Right'), 'right')]) - r('edit_metadata_elide_labels', gprefs) - r('edit_metadata_single_use_2_cols_for_custom_fields', gprefs) - r('edit_metadata_bulk_cc_label_length', gprefs) - r('edit_metadata_single_cc_label_length', gprefs) - r('edit_metadata_templates_only_F2_on_booklist', gprefs) - - self.em_display_model = EMDisplayedFields(self.gui.current_db, self.em_display_order) - self.em_display_model.dataChanged.connect(self.changed_signal) - self.em_display_order.setModel(self.em_display_model) - mu = partial(move_field_up, self.em_display_order, self.em_display_model) - md = partial(move_field_down, self.em_display_order, self.em_display_model) - self.em_display_order.set_movement_functions(mu, md) - self.em_up_button.clicked.connect(mu) - self.em_down_button.clicked.connect(md) - self.em_export_layout_button.clicked.connect(partial(export_layout, self, model=self.em_display_model)) - self.em_import_layout_button.clicked.connect(partial(import_layout, self, model=self.em_display_model)) - self.em_reset_layout_button.clicked.connect(partial(reset_layout, model=self.em_display_model)) - self.edit_rules = EditRules(self.tabWidget) self.edit_rules.changed.connect(self.changed_signal) self.tabWidget.addTab(self.edit_rules, QIcon.ic('format-fill-color.png'), _('Column &coloring')) @@ -108,7 +48,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def initialize(self): ConfigWidgetBase.initialize(self) - self.em_display_model.initialize() db = self.gui.current_db mi = selected_rows_metadatas() self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules') @@ -116,14 +55,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def restore_defaults(self): ConfigWidgetBase.restore_defaults(self) - self.em_display_model.restore_defaults() self.edit_rules.clear() self.icon_rules.clear() self.changed_signal.emit() def commit(self, *args): with BusyCursor(): - self.em_display_model.commit() self.edit_rules.commit(self.gui.current_db.prefs) self.icon_rules.commit(self.gui.current_db.prefs) diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index c2a8de4f12..db5d1388da 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -46,7 +46,7 @@ &Book details
- + :/images/edit_input.png:/images/edit_input.png @@ -54,257 +54,6 @@ Edit &metadata - - - - - - - Edit metadata (single) &layout: - - - opt_edit_metadata_single_layout - - - - - - - Choose a different layout for the Edit metadata dialog. The compact metadata layout favors editing custom metadata over changing covers and formats. - - - - - - - - - Select the custom columns (for this library) to display in the edit metadata dialogs and their order: - - - - - - Move down. Keyboard shortcut: Ctrl-Down arrow - - - - :/images/arrow-down.png:/images/arrow-down.png - - - - - - - Move up. Keyboard shortcut: Ctrl-Up arrow - - - - :/images/arrow-up.png:/images/arrow-up.png - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - true - - - - - - - - - Click this button to reset the list to its default order. - - - Reset list - - - - - - - <p>Click this button to set the list to one -previously exported. This could be useful if you have several libraries with -similar structure and you want to use the same column order for each one. Columns -in the imported list that aren't in the current library are ignored. Columns in -the library that are not in the imported list are put at the end and marked -as displayable.</p> - - - Import list - - - - - - - <p>Click this button to write the current display -settings to a file. This could be useful if you have several libraries with similar -structure and you want to use the same column order for each one.</p> - - - Export list - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - <p>Check this box to make the edit metadata dialogs ignore the -above specifications, showing all the columns in the default order. This is -useful for temporarily seeing all your columns in the dialogs without losing -the display and order specifications.</p> - - - Show &all columns in default order when editing metadata - - - - - - - If checked then labels wider than the label width -will be elided, otherwise they will be word wrapped. - - - &Elide labels when editing custom columns - - - - - - - - - - - - - - - - Elision point: - - - opt_edit_metadata_elision_point - - - - - - - Choose where in the label to put the... - - - - - - - - - <p>Check this box to allow only the F2 (Edit) key to -open the template editor in the book list for a "Column built from other -columns". Editing with mouse clicks and the Tab key will be disabled.</p> - - - Only the &F2 (Edit) key edits column templates in the book list - - - - - - - - - - - Use &two columns for custom columns in the Default layout - - - - - - - &Bulk edit custom column label length: - - - opt_edit_metadata_bulk_cc_label_length - - - - - - - The maximum width of a custom column label for the bulk metadata edit dialog in average characters. - - - chars - - - - - - - &Single edit custom column label length: - - - opt_edit_metadata_single_cc_label_length - - - - - - - The maximum width of a custom column label for the single metadata edit dialog in average characters. - - - chars - - - - - - - - @@ -368,11 +117,6 @@ columns". Editing with mouse clicks and the Tab key will be disabled.</p
- - ListViewWithMoveByKeyPress - QListView -
calibre/gui2/preferences.h
-
ScrollingTabWidget QTabWidget @@ -399,6 +143,11 @@ columns". Editing with mouse clicks and the Tab key will be disabled.</p ConfigWidgetBase
calibre/gui2/preferences/look_feel_tabs/book_details.h
+ + EditMetadataTab + ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs/edit_metadata.h
+
TbDisplayTab ConfigWidgetBase diff --git a/src/calibre/gui2/preferences/look_feel_tabs/edit_metadata.py b/src/calibre/gui2/preferences/look_feel_tabs/edit_metadata.py new file mode 100644 index 0000000000..727cc6e7aa --- /dev/null +++ b/src/calibre/gui2/preferences/look_feel_tabs/edit_metadata.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2025, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +from functools import partial + +from calibre.gui2 import gprefs +from calibre.gui2.custom_column_widgets import get_field_list as em_get_field_list +from calibre.gui2.preferences import LazyConfigWidgetBase +from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, export_layout, import_layout, move_field_down, move_field_up, reset_layout +from calibre.gui2.preferences.look_feel_tabs.edit_metadata_ui import Ui_Form +from calibre.gui2.widgets import BusyCursor + + +class EMDisplayedFields(DisplayedFields): + def __init__(self, db, parent=None): + DisplayedFields.__init__(self, db, parent) + + def initialize(self, use_defaults=False, pref_data_override=None): + self.beginResetModel() + self.fields = [[x[0], x[1]] for x in + em_get_field_list(self.db, use_defaults=use_defaults, pref_data_override=pref_data_override)] + self.endResetModel() + self.changed = True + + def commit(self): + if self.changed: + self.db.new_api.set_pref('edit_metadata_custom_columns_to_display', self.fields) + + +class EditMetadataTab(LazyConfigWidgetBase, Ui_Form): + + def genesis(self, gui): + self.gui = gui + db = self.gui.library_view.model().db + r = self.register + + r('edit_metadata_single_layout', gprefs, + choices=[(_('Default'), 'default'), (_('Compact metadata'), 'alt1'), + (_('All on 1 tab'), 'alt2')]) + r('edit_metadata_ignore_display_order', db.prefs) + r('edit_metadata_elision_point', gprefs, + choices=[(_('Left'), 'left'), (_('Middle'), 'middle'), + (_('Right'), 'right')]) + r('edit_metadata_elide_labels', gprefs) + r('edit_metadata_single_use_2_cols_for_custom_fields', gprefs) + r('edit_metadata_bulk_cc_label_length', gprefs) + r('edit_metadata_single_cc_label_length', gprefs) + r('edit_metadata_templates_only_F2_on_booklist', gprefs) + + self.em_display_model = EMDisplayedFields(self.gui.current_db, self.em_display_order) + self.em_display_model.dataChanged.connect(self.changed_signal) + self.em_display_order.setModel(self.em_display_model) + mu = partial(move_field_up, self.em_display_order, self.em_display_model) + md = partial(move_field_down, self.em_display_order, self.em_display_model) + self.em_display_order.set_movement_functions(mu, md) + self.em_up_button.clicked.connect(mu) + self.em_down_button.clicked.connect(md) + self.em_export_layout_button.clicked.connect(partial(export_layout, self, model=self.em_display_model)) + self.em_import_layout_button.clicked.connect(partial(import_layout, self, model=self.em_display_model)) + self.em_reset_layout_button.clicked.connect(partial(reset_layout, model=self.em_display_model)) + + def lazy_initialize(self): + self.em_display_model.initialize() + + def commit(self): + with BusyCursor(): + self.em_display_model.commit() + return LazyConfigWidgetBase.commit(self) + + def restore_defaults(self): + LazyConfigWidgetBase.restore_defaults(self) + self.em_display_model.restore_defaults() + self.changed_signal.emit() diff --git a/src/calibre/gui2/preferences/look_feel_tabs/edit_metadata.ui b/src/calibre/gui2/preferences/look_feel_tabs/edit_metadata.ui new file mode 100644 index 0000000000..ad2d167446 --- /dev/null +++ b/src/calibre/gui2/preferences/look_feel_tabs/edit_metadata.ui @@ -0,0 +1,274 @@ + + + Form + + + + :/images/edit_input.png:/images/edit_input.png + + + Edit &metadata + + + + + + + + Edit metadata (single) &layout: + + + opt_edit_metadata_single_layout + + + + + + + Choose a different layout for the Edit metadata dialog. The compact metadata layout favors editing custom metadata over changing covers and formats. + + + + + + + + + Select the custom columns (for this library) to display in the edit metadata dialogs and their order: + + + + + + Move down. Keyboard shortcut: Ctrl-Down arrow + + + + :/images/arrow-down.png:/images/arrow-down.png + + + + + + + Move up. Keyboard shortcut: Ctrl-Up arrow + + + + :/images/arrow-up.png:/images/arrow-up.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + + + + + + Click this button to reset the list to its default order. + + + Reset list + + + + + + + <p>Click this button to set the list to one +previously exported. This could be useful if you have several libraries with +similar structure and you want to use the same column order for each one. Columns +in the imported list that aren't in the current library are ignored. Columns in +the library that are not in the imported list are put at the end and marked +as displayable.</p> + + + Import list + + + + + + + <p>Click this button to write the current display +settings to a file. This could be useful if you have several libraries with similar +structure and you want to use the same column order for each one.</p> + + + Export list + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + <p>Check this box to make the edit metadata dialogs ignore the +above specifications, showing all the columns in the default order. This is +useful for temporarily seeing all your columns in the dialogs without losing +the display and order specifications.</p> + + + Show &all columns in default order when editing metadata + + + + + + + If checked then labels wider than the label width +will be elided, otherwise they will be word wrapped. + + + &Elide labels when editing custom columns + + + + + + + + + + + + + + + + Elision point: + + + opt_edit_metadata_elision_point + + + + + + + Choose where in the label to put the... + + + + + + + + + <p>Check this box to allow only the F2 (Edit) key to +open the template editor in the book list for a "Column built from other +columns". Editing with mouse clicks and the Tab key will be disabled.</p> + + + Only the &F2 (Edit) key edits column templates in the book list + + + + + + + + + + + Use &two columns for custom columns in the Default layout + + + + + + + &Bulk edit custom column label length: + + + opt_edit_metadata_bulk_cc_label_length + + + + + + + The maximum width of a custom column label for the bulk metadata edit dialog in average characters. + + + chars + + + + + + + &Single edit custom column label length: + + + opt_edit_metadata_single_cc_label_length + + + + + + + The maximum width of a custom column label for the single metadata edit dialog in average characters. + + + chars + + + + + + + + + + + + ListViewWithMoveByKeyPress + QListView +
calibre/gui2/preferences.h
+
+
+ + + +
From 7a91da159973984fbdaaae6b6bcfed60b920ec4a Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Sat, 1 Feb 2025 10:49:26 +0100 Subject: [PATCH 6/8] move EditRules from "Look & Feel" to their own widgets --- src/calibre/gui2/preferences/look_feel.py | 28 +----------- src/calibre/gui2/preferences/look_feel.ui | 28 ++++++++++++ .../preferences/look_feel_tabs/__init__.py | 43 +++++++++++++++++++ 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 4cf696b74b..4b93410235 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -5,13 +5,10 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from qt.core import QIcon, QKeySequence, QListWidgetItem, Qt +from qt.core import QKeySequence, QListWidgetItem, Qt from calibre.gui2.preferences import ConfigWidgetBase, test_widget -from calibre.gui2.preferences.coloring import EditRules -from calibre.gui2.preferences.look_feel_tabs import selected_rows_metadatas from calibre.gui2.preferences.look_feel_ui import Ui_Form -from calibre.gui2.widgets import BusyCursor class ConfigWidget(ConfigWidgetBase, Ui_Form): @@ -19,14 +16,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def genesis(self, gui): self.gui = gui - self.edit_rules = EditRules(self.tabWidget) - self.edit_rules.changed.connect(self.changed_signal) - self.tabWidget.addTab(self.edit_rules, QIcon.ic('format-fill-color.png'), _('Column &coloring')) - - self.icon_rules = EditRules(self.tabWidget) - self.icon_rules.changed.connect(self.changed_signal) - self.tabWidget.addTab(self.icon_rules, QIcon.ic('icon_choose.png'), _('Column &icons')) - self.tabWidget.setCurrentIndex(0) self.tabWidget.tabBar().setVisible(False) keys = [QKeySequence('F11', QKeySequence.SequenceFormat.PortableText), QKeySequence( @@ -45,25 +34,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def initial_tab_changed(self): self.sections_view.setCurrentRow(self.tabWidget.currentIndex()) - def initialize(self): - ConfigWidgetBase.initialize(self) - - db = self.gui.current_db - mi = selected_rows_metadatas() - self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules') - self.icon_rules.initialize(db.field_metadata, db.prefs, mi, 'column_icon_rules') - def restore_defaults(self): ConfigWidgetBase.restore_defaults(self) - self.edit_rules.clear() - self.icon_rules.clear() self.changed_signal.emit() - def commit(self, *args): - with BusyCursor(): - self.edit_rules.commit(self.gui.current_db.prefs) - self.icon_rules.commit(self.gui.current_db.prefs) - def refresh_gui(self, gui): m = gui.library_view.model() m.update_db_prefs_cache() diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index db5d1388da..dba93c0d3f 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -102,6 +102,24 @@ &Quickview
+ + + + :/images/format-fill-color.png:/images/format-fill-color.png + + + Column &coloring + + + + + + :/images/icon_choose.png:/images/icon_choose.png + + + Column &icons + +
@@ -178,6 +196,16 @@ ConfigWidgetBase
calibre/gui2/preferences/look_feel_tabs/quickview.h
+ + ColumnColorRules + ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs.h
+
+ + ColumnIconRules + ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs.h
+
diff --git a/src/calibre/gui2/preferences/look_feel_tabs/__init__.py b/src/calibre/gui2/preferences/look_feel_tabs/__init__.py index 4b2ad7211e..9ae9a57cb7 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/__init__.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/__init__.py @@ -12,6 +12,8 @@ from qt.core import QAbstractListModel, QComboBox, QFormLayout, QIcon, QItemSele from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK from calibre.gui2 import choose_files, choose_save_file, error_dialog from calibre.gui2.book_details import get_field_list +from calibre.gui2.preferences import LazyConfigWidgetBase +from calibre.gui2.preferences.coloring import EditRules from calibre.gui2.ui import get_gui @@ -167,6 +169,47 @@ class DisplayedFields(QAbstractListModel): return idx +class LazyEditRulesBase(LazyConfigWidgetBase): + + rule_set_name = None + + def __init__(self, parent=None): + super().__init__(parent) + self.rules_editor = EditRules(parent) + self.setLayout(self.rules_editor.layout()) + + def genesis(self, gui): + self.gui = gui + self.rules_editor.changed.connect(self.changed_signal) + + def lazy_initialize(self): + if not self.rule_set_name: + raise NotImplementedError('You must define the attribut "rule_set_name" in LazyEditRulesBase subclasses') + self.load_rule_set(self.rule_set_name) + + def load_rule_set(self, name): + db = self.gui.current_db + mi = selected_rows_metadatas() + self.rules_editor.initialize(db.field_metadata, db.prefs, mi, name) + + def commit(self): + self.rules_editor.commit(self, self.gui.current_db.prefs) + return LazyConfigWidgetBase.commit(self) + + def restore_defaults(self): + LazyConfigWidgetBase.restore_defaults(self) + self.rules_editor.clear(self) + self.changed_signal.emit() + + +class ColumnColorRules(LazyEditRulesBase): + rule_set_name = 'column_color_rules' + + +class ColumnIconRules(LazyEditRulesBase): + rule_set_name = 'column_icon_rules' + + def export_layout(in_widget, model=None): filename = choose_save_file(in_widget, 'look_feel_prefs_import_export_field_list', _('Save column list to file'), From 68809470b51bbead56a1f90085e45b03c9ccfa2e Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:28:17 +0100 Subject: [PATCH 7/8] ... --- src/calibre/gui2/preferences/look_feel_tabs/book_details.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/preferences/look_feel_tabs/book_details.py b/src/calibre/gui2/preferences/look_feel_tabs/book_details.py index 1f295b1225..5f2780d62f 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/book_details.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/book_details.py @@ -219,9 +219,9 @@ class BookDetailsTab(LazyConfigWidgetBase, Ui_Form): self.css_highlighter.set_document(self.opt_book_details_css.document()) def lazy_initialize(self): + self.blockSignals(True) self.default_author_link.value = default_author_link() self.display_model.initialize() - self.blockSignals(True) self.bd_vertical_cats_model.initialize() self.opt_book_details_css.setPlainText(P('templates/book_details.css', data=True).decode('utf-8')) self.blockSignals(False) From 5bec0d125a15c23b485d4b540a804cc802891bc5 Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:31:23 +0100 Subject: [PATCH 8/8] remove margin around CoverGridTab --- .../preferences/look_feel_tabs/cover_grid.py | 9 +- .../preferences/look_feel_tabs/cover_grid.ui | 820 +++++++++--------- 2 files changed, 414 insertions(+), 415 deletions(-) diff --git a/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.py b/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.py index 5659b49017..5c6ad8d652 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.py @@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en' from threading import Thread -from qt.core import QBrush, QColor, QColorDialog, QDialog, QPainter, QPixmap, QPushButton, QSize, QSizePolicy, Qt, QVBoxLayout, QWidget, pyqtSignal +from qt.core import QBrush, QColor, QColorDialog, QDialog, QPainter, QPixmap, QPushButton, QSize, QSizePolicy, Qt, QTabWidget, QVBoxLayout, QWidget, pyqtSignal from calibre import human_readable from calibre.gui2 import gprefs, open_local_file, question_dialog @@ -54,10 +54,15 @@ class Background(QWidget): painter.end() -class CoverGridTab(LazyConfigWidgetBase, Ui_Form): +class CoverGridTab(QTabWidget, LazyConfigWidgetBase, Ui_Form): + changed_signal = pyqtSignal() + restart_now = pyqtSignal() size_calculated = pyqtSignal(object) + def __init__(self, parent=None): + super().__init__(parent) + def genesis(self, gui): self.gui = gui db = self.gui.library_view.model().db diff --git a/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.ui b/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.ui index e54851c90a..8e3a45e522 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.ui +++ b/src/calibre/gui2/preferences/look_feel_tabs/cover_grid.ui @@ -1,423 +1,417 @@ Form - - - - - - 0 + + + 0 + + + + &Layout + + + + QFormLayout::ExpandingFieldsGrow - - - &Layout - - - - QFormLayout::ExpandingFieldsGrow + + + + Control the Cover grid view. You can enable this view by clicking the "Layout" button in the bottom right corner of the main calibre window. - - - - Control the Cover grid view. You can enable this view by clicking the "Layout" button in the bottom right corner of the main calibre window. - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Field to show &under the covers: - - - opt_field_under_covers_in_grid - - - - - - - - - - Spac&ing between covers: - - - opt_cover_grid_spacing - - - - - - - The spacing between covers. A value of zero means calculate automatically based on cover size. - - - Automatic - - - cm - - - 2 - - - 0.100000000000000 - - - - - - - Background for the Cover grid - - - - - - - - Cover size - - - - - - Make the covers larger, maintaining current aspect ratio. - - - &Larger covers - - - - :/images/plus.png:/images/plus.png - - - - - - - Make the covers smaller, maintaining current aspect ratio. - - - &Smaller covers - - - - :/images/minus.png:/images/minus.png - - - - - - - Cover &height: - - - opt_cover_grid_height - - - - - - - Cover &width: - - - opt_cover_grid_width - - - - - - - The height of displayed covers. + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Field to show &under the covers: + + + opt_field_under_covers_in_grid + + + + + + + + + + Spac&ing between covers: + + + opt_cover_grid_spacing + + + + + + + The spacing between covers. A value of zero means calculate automatically based on cover size. + + + Automatic + + + cm + + + 2 + + + 0.100000000000000 + + + + + + + Background for the Cover grid + + + + + + + + Cover size + + + + + + Make the covers larger, maintaining current aspect ratio. + + + &Larger covers + + + + :/images/plus.png:/images/plus.png + + + + + + + Make the covers smaller, maintaining current aspect ratio. + + + &Smaller covers + + + + :/images/minus.png:/images/minus.png + + + + + + + Cover &height: + + + opt_cover_grid_height + + + + + + + Cover &width: + + + opt_cover_grid_width + + + + + + + The height of displayed covers. Aue of zero means calculate automatically. - - - Automatic - - - cm - - - 1 - - - - - - - Reset size to automatic - - - &Reset size - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - The width of displayed covers. + + + Automatic + + + cm + + + 1 + + + + + + + Reset size to automatic + + + &Reset size + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + The width of displayed covers. Aue of zero means calculate automatically. - - - Automatic - - - cm - - - 1 - - - - - - - - 0 - 0 - - - - By default, calibre chooses a cover size based on your computer's screen size. You can change the cover size here: - - - true - - - - - - - - - - px - - - 16 - - - 1000 - - - - - - - Size of the emblems (if any) shown &next to the covers: - - - opt_emblem_size - - - - - - - Show a &field (such as title) under the covers - - - - - - - &Location of the emblems shown next to the covers: - - - opt_emblem_position - - - - - - - - - - - &Emblems - - - - - &Performance - - - - - - Caching of covers for improved performance - - - - - - There are two kinds of caches that calibre uses to improve performance when rendering covers in the grid view. A disk cache that is kept on your hard disk and stores the cover thumbnails and an in memory cache used to ensure flicker free rendering of covers. For best results, keep the memory cache small and the disk cache large, unless you have a lot of extra RAM in your computer and don't mind it being used by the memory cache. - - - true - - - - - - - Disable - - - MB - - - 100 - - - 100 - - - - - - - &Empty disk cache - - - - - - - &Open cache folder - - - - - - - Qt::Horizontal - - - - 310 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Number of screenfulls of covers to cache in &memory (keep this small): - - - opt_cover_grid_cache_size_multiple - - - - - - - Maximum amount of &disk space to use for caching thumbnails: - - - opt_cover_grid_disk_cache_size - - - - - - - The maximum number of screenfulls of thumbnails to keep in memory. Increasing this will make rendering faster, at the cost of more memory usage. Note that regardless of this setting, a minimum of one hundred thumbnails are always kept in memory, to ensure flicker free rendering. - - - 2 - - - 100 - - - - - - - - - - - - - - - - - - - + + + Automatic + + + cm + + + 1 + + + + + + + + 0 + 0 + + + + By default, calibre chooses a cover size based on your computer's screen size. You can change the cover size here: + + + true + + + + + +
+ + + + px + + + 16 + + + 1000 + + + + + + + Size of the emblems (if any) shown &next to the covers: + + + opt_emblem_size + + + + + + + Show a &field (such as title) under the covers + + + + + + + &Location of the emblems shown next to the covers: + + + opt_emblem_position + + + + + + +
+
+ + + &Emblems + + + + + &Performance + + + + + + Caching of covers for improved performance + + + + + + There are two kinds of caches that calibre uses to improve performance when rendering covers in the grid view. A disk cache that is kept on your hard disk and stores the cover thumbnails and an in memory cache used to ensure flicker free rendering of covers. For best results, keep the memory cache small and the disk cache large, unless you have a lot of extra RAM in your computer and don't mind it being used by the memory cache. + + + true + + + + + + + Disable + + + MB + + + 100 + + + 100 + + + + + + + &Empty disk cache + + + + + + + &Open cache folder + + + + + + + Qt::Horizontal + + + + 310 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Number of screenfulls of covers to cache in &memory (keep this small): + + + opt_cover_grid_cache_size_multiple + + + + + + + Maximum amount of &disk space to use for caching thumbnails: + + + opt_cover_grid_disk_cache_size + + + + + + + The maximum number of screenfulls of thumbnails to keep in memory. Increasing this will make rendering faster, at the cost of more memory usage. Note that regardless of this setting, a minimum of one hundred thumbnails are always kept in memory, to ensure flicker free rendering. + + + 2 + + + 100 + + + + + + + + + + + + + + + +