This commit is contained in:
Kovid Goyal 2025-01-26 09:58:40 +05:30
commit 5a0e9aae9e
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
14 changed files with 1083 additions and 778 deletions

View File

@ -114,6 +114,13 @@ class ConfigWidgetInterface:
def do_on_child_tabs(self, method, *args): def do_on_child_tabs(self, method, *args):
r = False r = False
for t in self.child_tabs: for t in self.child_tabs:
lazy_init_called = getattr(t, 'lazy_init_called', True)
if method in ('commit', 'refresh_gui') and not lazy_init_called:
continue
if method == 'restore_defaults' and not lazy_init_called:
if hasattr(t, 'lazy_initialize'):
t.lazy_initialize()
t.lazy_init_called = True
r = r | bool(getattr(t, method)(*args)) r = r | bool(getattr(t, method)(*args))
return r return r
@ -291,7 +298,7 @@ class ConfigWidgetBase(QWidget, ConfigWidgetInterface):
self.settings = {} self.settings = {}
self.child_tabs = [] self.child_tabs = []
for v in self.__dict__.values(): for v in self.__dict__.values():
if isinstance(v, ConfigTabWidget): if isinstance(v, LazyConfigWidgetBase):
self.child_tabs.append(v) self.child_tabs.append(v)
def register(self, name, config_obj, gui_name=None, choices=None, def register(self, name, config_obj, gui_name=None, choices=None,
@ -349,11 +356,30 @@ def get_plugin(category, name):
(category, name)) (category, name))
class ConfigTabWidget(ConfigWidgetBase): class LazyConfigWidgetBase(ConfigWidgetBase):
'''
Use this for dialogs that are tabs, accessed either from the left or on the
top. It directly replaces ConfigWidgetBase, supporting the lazy operations.
'''
def __init__(self, parent=None):
super().__init__(parent)
self.lazy_init_called = False
def set_changed_signal(self, changed_signal): def set_changed_signal(self, changed_signal):
self.changed_signal.connect(changed_signal) self.changed_signal.connect(changed_signal)
def showEvent(self, event):
# called when the widget is actually displays. We can't do something like
# lazy_genesis because Qt does "things" before showEvent() is called. In
# particular, the register function doesn't work with combo boxes if
# genesis isn't called before everythign else. Why is a mystery.
if not self.lazy_init_called:
if hasattr(self, 'lazy_initialize'):
self.lazy_initialize()
self.lazy_init_called = True
super().showEvent(event)
class ConfigDialog(QDialog): class ConfigDialog(QDialog):

View File

@ -44,7 +44,6 @@ from qt.core import (
from calibre import human_readable from calibre import human_readable
from calibre.constants import ismacos, iswindows from calibre.constants import ismacos, iswindows
from calibre.db.categories import is_standard_category
from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK
from calibre.ebooks.metadata.sources.prefs import msprefs from calibre.ebooks.metadata.sources.prefs import msprefs
from calibre.gui2 import ( from calibre.gui2 import (
@ -59,13 +58,20 @@ from calibre.gui2 import (
qt_app, qt_app,
question_dialog, question_dialog,
) )
from calibre.gui2.actions.show_quickview import get_quickview_action_plugin
from calibre.gui2.custom_column_widgets import get_field_list as em_get_field_list 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.dialogs.quickview import get_qv_field_list
from calibre.gui2.library.alternate_views import CM_TO_INCH, auto_height 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, Setting, set_help_tips, test_widget
from calibre.gui2.preferences.coloring import EditRules from calibre.gui2.preferences.coloring import EditRules
from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, move_field_down, move_field_up 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.preferences.look_feel_ui import Ui_Form
from calibre.gui2.widgets import BusyCursor from calibre.gui2.widgets import BusyCursor
from calibre.gui2.widgets2 import Dialog from calibre.gui2.widgets2 import Dialog
@ -481,12 +487,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('show_sb_all_actions_button', gprefs) r('show_sb_all_actions_button', gprefs)
# r('show_sb_preference_button', gprefs) # r('show_sb_preference_button', gprefs)
r('row_numbers_in_book_list', gprefs) r('row_numbers_in_book_list', gprefs)
r('tag_browser_old_look', gprefs)
r('tag_browser_hide_empty_categories', gprefs)
r('tag_browser_always_autocollapse', gprefs)
r('tag_browser_restore_tree_expansion', gprefs)
r('tag_browser_show_tooltips', gprefs)
r('tag_browser_allow_keyboard_focus', gprefs)
r('bd_show_cover', gprefs) r('bd_show_cover', gprefs)
r('bd_overlay_cover_size', gprefs) r('bd_overlay_cover_size', gprefs)
r('cover_corner_radius', gprefs) r('cover_corner_radius', gprefs)
@ -497,13 +497,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('cover_grid_disk_cache_size', gprefs) r('cover_grid_disk_cache_size', gprefs)
r('cover_grid_spacing', gprefs) r('cover_grid_spacing', gprefs)
r('cover_grid_show_title', gprefs) r('cover_grid_show_title', gprefs)
r('tag_browser_show_counts', gprefs)
r('tag_browser_item_padding', gprefs)
r('qv_respects_vls', gprefs)
r('qv_dclick_changes_column', gprefs)
r('qv_retkey_changes_column', gprefs)
r('qv_follows_column', gprefs)
r('emblem_size', gprefs) r('emblem_size', gprefs)
r('emblem_position', gprefs, choices=[ r('emblem_position', gprefs, choices=[
@ -536,10 +529,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
choices.insert(1, ((get_language(lul), lul))) choices.insert(1, ((get_language(lul), lul)))
r('language', prefs, choices=choices, restart_required=True, setting=LanguageSetting) r('language', prefs, choices=choices, restart_required=True, setting=LanguageSetting)
r('show_avg_rating', config)
r('show_links_in_tag_browser', gprefs)
r('show_notes_in_tag_browser', gprefs)
r('icons_on_right_in_tag_browser', gprefs)
r('disable_animations', config) r('disable_animations', config)
r('systray_icon', config, restart_required=True) r('systray_icon', config, restart_required=True)
r('show_splash_screen', gprefs) r('show_splash_screen', gprefs)
@ -554,12 +543,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
(_('Never'), 'never')] (_('Never'), 'never')]
r('toolbar_text', gprefs, choices=choices) r('toolbar_text', gprefs, choices=choices)
choices = [(_('Disabled'), 'disable'), (_('By first letter'), 'first letter'),
(_('Partitioned'), 'partition')]
r('tags_browser_partition_method', gprefs, choices=choices)
r('tags_browser_collapse_at', gprefs)
r('tags_browser_collapse_fl_at', gprefs)
fm = db.field_metadata fm = db.field_metadata
choices = sorted(((fm[k]['name'], k) for k in fm.displayable_field_keys() if fm[k]['name']), choices = sorted(((fm[k]['name'], k) for k in fm.displayable_field_keys() if fm[k]['name']),
key=lambda x:sort_key(x[0])) key=lambda x:sort_key(x[0]))
@ -583,15 +566,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.current_font = self.initial_font = None self.current_font = self.initial_font = None
self.change_font_button.clicked.connect(self.change_font) 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)
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 = EMDisplayedFields(self.gui.current_db, self.em_display_order)
self.em_display_model.dataChanged.connect(self.changed_signal) self.em_display_model.dataChanged.connect(self.changed_signal)
self.em_display_order.setModel(self.em_display_model) self.em_display_order.setModel(self.em_display_model)
@ -600,41 +574,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.em_display_order.set_movement_functions(mu, md) self.em_display_order.set_movement_functions(mu, md)
self.em_up_button.clicked.connect(mu) self.em_up_button.clicked.connect(mu)
self.em_down_button.clicked.connect(md) self.em_down_button.clicked.connect(md)
self.em_export_layout_button.clicked.connect(partial(self.export_layout, model=self.em_display_model)) self.em_export_layout_button.clicked.connect(partial(export_layout, self, model=self.em_display_model))
self.em_import_layout_button.clicked.connect(partial(self.import_layout, 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(self.reset_layout, model=self.em_display_model)) self.em_reset_layout_button.clicked.connect(partial(reset_layout, model=self.em_display_model))
self.qv_display_model = QVDisplayedFields(self.gui.current_db, self.qv_display_order)
self.qv_display_model.dataChanged.connect(self.changed_signal)
self.qv_display_order.setModel(self.qv_display_model)
mu = partial(move_field_up, self.qv_display_order, self.qv_display_model)
md = partial(move_field_down, self.qv_display_order, self.qv_display_model)
self.qv_display_order.set_movement_functions(mu, md)
self.qv_up_button.clicked.connect(mu)
self.qv_down_button.clicked.connect(md)
self.tb_display_model = TBDisplayedFields(self.gui.current_db, self.tb_display_order,
category_icons=self.gui.tags_view.model().category_custom_icons)
self.tb_display_model.dataChanged.connect(self.changed_signal)
self.tb_display_order.setModel(self.tb_display_model)
self.tb_reset_layout_button.clicked.connect(partial(self.reset_layout, model=self.tb_display_model))
self.tb_export_layout_button.clicked.connect(partial(self.export_layout, model=self.tb_display_model))
self.tb_import_layout_button.clicked.connect(partial(self.import_layout, model=self.tb_display_model))
self.tb_up_button.clicked.connect(self.tb_up_button_clicked)
self.tb_down_button.clicked.connect(self.tb_down_button_clicked)
self.tb_display_order.set_movement_functions(self.tb_up_button_clicked, self.tb_down_button_clicked)
self.tb_categories_to_part_model = TBPartitionedFields(self.gui.current_db,
self.tb_cats_to_partition,
category_icons=self.gui.tags_view.model().category_custom_icons)
self.tb_categories_to_part_model.dataChanged.connect(self.changed_signal)
self.tb_cats_to_partition.setModel(self.tb_categories_to_part_model)
self.tb_partition_reset_button.clicked.connect(partial(self.reset_layout,
model=self.tb_categories_to_part_model))
self.tb_partition_export_layout_button.clicked.connect(partial(self.export_layout,
model=self.tb_categories_to_part_model))
self.tb_partition_import_layout_button.clicked.connect(partial(self.import_layout,
model=self.tb_categories_to_part_model))
self.bd_vertical_cats_model = BDVerticalCats(self.gui.current_db, self.tb_hierarchy_tab.tb_hierarchical_cats) 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_model.dataChanged.connect(self.changed_signal)
@ -690,16 +632,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.css_highlighter = get_highlighter('css')() self.css_highlighter = get_highlighter('css')()
self.css_highlighter.apply_theme(get_theme(None)) self.css_highlighter.apply_theme(get_theme(None))
self.css_highlighter.set_document(self.opt_book_details_css.document()) self.css_highlighter.set_document(self.opt_book_details_css.document())
self.lazy_tabs = {}
for i in range(self.tabWidget.count()): for i in range(self.tabWidget.count()):
self.sections_view.addItem(QListWidgetItem(self.tabWidget.tabIcon(i), self.tabWidget.tabText(i).replace('&', ''))) self.sections_view.addItem(QListWidgetItem(self.tabWidget.tabIcon(i), self.tabWidget.tabText(i).replace('&', '')))
# retrieve tabs and subtabs of look & feel to load their content later when clicking of them
w = self.tabWidget.widget(i).widget()
self.lazy_tabs[(i, None)] = w
if isinstance(w, QTabWidget):
w.currentChanged.connect(partial(self.lazy_tab_operations, i))
for ii in range(w.count()):
self.lazy_tabs[(i, ii)] = w.widget(ii)
self.sections_view.setCurrentRow(self.tabWidget.currentIndex()) self.sections_view.setCurrentRow(self.tabWidget.currentIndex())
self.sections_view.currentRowChanged.connect(self.tabWidget.setCurrentIndex) self.sections_view.currentRowChanged.connect(self.tabWidget.setCurrentIndex)
self.sections_view.setMaximumWidth(self.sections_view.sizeHintForColumn(0) + 16) self.sections_view.setMaximumWidth(self.sections_view.sizeHintForColumn(0) + 16)
@ -728,59 +662,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
enabled = self.opt_ui_style.currentData() == 'calibre' enabled = self.opt_ui_style.currentData() == 'calibre'
self.button_adjust_colors.setEnabled(enabled) self.button_adjust_colors.setEnabled(enabled)
def export_layout(self, model=None):
filename = choose_save_file(self, 'em_import_export_field_list',
_('Save column list to file'),
filters=[(_('Column list'), ['json'])])
if filename:
try:
with open(filename, 'w') as f:
json.dump(model.fields, f, indent=1)
except Exception as err:
error_dialog(self, _('Export field layout'),
_('<p>Could not write field list. Error:<br>%s')%err, show=True)
def import_layout(self, model=None):
filename = choose_files(self, 'em_import_export_field_list',
_('Load column list from file'),
filters=[(_('Column list'), ['json'])])
if filename:
try:
with open(filename[0]) as f:
fields = json.load(f)
model.initialize(pref_data_override=fields)
self.changed_signal.emit()
except Exception as err:
error_dialog(self, _('Import layout'),
_('<p>Could not read field list. Error:<br>%s')%err, show=True)
def reset_layout(self, model=None):
model.initialize(use_defaults=True)
self.changed_signal.emit()
def tb_down_button_clicked(self):
idx = self.tb_display_order.currentIndex()
if idx.isValid():
row = idx.row()
model = self.tb_display_model
fields = model.fields
key = fields[row][0]
if not is_standard_category(key):
return
if row < len(fields) and is_standard_category(fields[row+1][0]):
move_field_down(self.tb_display_order, model)
def tb_up_button_clicked(self):
idx = self.tb_display_order.currentIndex()
if idx.isValid():
row = idx.row()
model = self.tb_display_model
fields = model.fields
key = fields[row][0]
if not is_standard_category(key):
return
move_field_up(self.tb_display_order, model)
def choose_icon_theme(self): def choose_icon_theme(self):
from calibre.gui2.icon_theme import ChooseTheme from calibre.gui2.icon_theme import ChooseTheme
d = ChooseTheme(self) d = ChooseTheme(self)
@ -833,11 +714,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
font.append(gprefs.get('font_stretch', QFont.Stretch.Unstretched)) font.append(gprefs.get('font_stretch', QFont.Stretch.Unstretched))
self.current_font = self.initial_font = font self.current_font = self.initial_font = font
self.update_font_display() self.update_font_display()
self.display_model.initialize()
self.em_display_model.initialize() self.em_display_model.initialize()
self.qv_display_model.initialize()
self.tb_display_model.initialize()
self.tb_categories_to_part_model.initialize()
self.bd_vertical_cats_model.initialize() self.bd_vertical_cats_model.initialize()
db = self.gui.current_db db = self.gui.current_db
mi = [] mi = []
@ -857,10 +734,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.opt_book_details_css.blockSignals(True) 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.setPlainText(P('templates/book_details.css', data=True).decode('utf-8'))
self.opt_book_details_css.blockSignals(False) self.opt_book_details_css.blockSignals(False)
self.tb_focus_label.setVisible(self.opt_tag_browser_allow_keyboard_focus.isChecked())
self.update_color_palette_state() self.update_color_palette_state()
self.opt_gui_layout.setCurrentIndex(0 if self.gui.layout_container.is_wide else 1) self.opt_gui_layout.setCurrentIndex(0 if self.gui.layout_container.is_wide else 1)
self.lazy_tab_operations(self.tabWidget.currentIndex(), None)
def open_cg_cache(self): def open_cg_cache(self):
open_local_file(self.gui.grid_view.thumbnail_cache.location) open_local_file(self.gui.grid_view.thumbnail_cache.location)
@ -870,20 +745,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
_('Current space used: %s') % human_readable(size)) _('Current space used: %s') % human_readable(size))
def tab_changed(self, index): def tab_changed(self, index):
self.lazy_tab_operations(index, None)
if self.tabWidget.currentWidget() is self.cover_grid_tab: if self.tabWidget.currentWidget() is self.cover_grid_tab:
self.show_current_cache_usage() self.show_current_cache_usage()
def lazy_tab_operations(self, idx_section, idx_subtab):
'''
Check if the tab has lazy operations.
Perfom the lazy operations only once, the first time the tab is selected.
'''
tab = self.lazy_tabs.get((idx_section, idx_subtab), None)
if hasattr(tab, 'lazy_populate_content'):
tab.lazy_populate_content()
self.lazy_tabs.pop((idx_section, idx_subtab), None)
def show_current_cache_usage(self): def show_current_cache_usage(self):
t = Thread(target=self.calc_cache_size) t = Thread(target=self.calc_cache_size)
t.daemon = True t.daemon = True
@ -912,9 +776,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
if ofont is not None: if ofont is not None:
self.changed_signal.emit() self.changed_signal.emit()
self.update_font_display() self.update_font_display()
self.display_model.restore_defaults()
self.em_display_model.restore_defaults() self.em_display_model.restore_defaults()
self.qv_display_model.restore_defaults()
self.bd_vertical_cats_model.restore_defaults() self.bd_vertical_cats_model.restore_defaults()
gprefs.set('tb_search_order', gprefs.defaults['tb_search_order']) gprefs.set('tb_search_order', gprefs.defaults['tb_search_order'])
self.edit_rules.clear() self.edit_rules.clear()
@ -986,11 +848,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
is not None else QFont.Stretch.Unstretched) is not None else QFont.Stretch.Unstretched)
QApplication.setFont(self.font_display.font()) QApplication.setFont(self.font_display.font())
rr = True rr = True
self.display_model.commit()
self.em_display_model.commit() self.em_display_model.commit()
self.qv_display_model.commit()
self.tb_display_model.commit()
self.tb_categories_to_part_model.commit()
self.bd_vertical_cats_model.commit() self.bd_vertical_cats_model.commit()
self.edit_rules.commit(self.gui.current_db.prefs) self.edit_rules.commit(self.gui.current_db.prefs)
self.icon_rules.commit(self.gui.current_db.prefs) self.icon_rules.commit(self.gui.current_db.prefs)
@ -1027,9 +885,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
gui.library_view.refresh_row_sizing() gui.library_view.refresh_row_sizing()
gui.grid_view.refresh_settings() gui.grid_view.refresh_settings()
gui.update_auto_scroll_timeout() gui.update_auto_scroll_timeout()
qv = get_quickview_action_plugin()
if qv:
qv.refill_quickview()
gui.sb_all_gui_actions_button.setVisible(gprefs['show_sb_all_actions_button']) gui.sb_all_gui_actions_button.setVisible(gprefs['show_sb_all_actions_button'])
# gui.sb_preferences_button.setVisible(gprefs['show_sb_preference_button']) # gui.sb_preferences_button.setVisible(gprefs['show_sb_preference_button'])

View File

@ -1307,487 +1307,15 @@ columns&quot;. Editing with mouse clicks and the Tab key will be disabled.&lt;/p
<attribute name="title"> <attribute name="title">
<string>&amp;Tag browser</string> <string>&amp;Tag browser</string>
</attribute> </attribute>
<widget class="QWidget" name="tb_tab1"> <widget class="TbDisplayTab" name="tb_display_tab">
<attribute name="title"> <attribute name="title">
<string>Di&amp;splay</string> <string>Di&amp;splay</string>
</attribute> </attribute>
<layout class="QGridLayout" name="verticalLayout_42">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>User categories and Saved searches cannot be moved</string>
</property>
<property name="text">
<string>Select the categories to display in the Tag browser, and their &amp;order</string>
</property>
<property name="buddy">
<cstring>tb_display_order</cstring>
</property>
</widget> </widget>
</item> <widget class="TbPartitioningTab" name="tb_partioning_tab">
<item row="1" column="0" rowspan="3">
<widget class="ListViewWithMoveByKeyPress" name="tb_display_order">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="tb_up_button">
<property name="toolTip">
<string>Move up. User categories and Saved searches cannot be moved. Keyboard shortcut: Ctrl-Up arrow</string>
</property>
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/arrow-up.png</normaloff>:/images/arrow-up.png</iconset>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="tb_down_button">
<property name="toolTip">
<string>Move down. User categories and Saved searches cannot be moved. Keyboard shortcut: Ctrl-Down arrow</string>
</property>
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/arrow-down.png</normaloff>:/images/arrow-down.png</iconset>
</property>
</widget>
</item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="tb_reset_layout_button">
<property name="toolTip">
<string>Click this button to reset the list to its default order.</string>
</property>
<property name="text">
<string>Reset list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tb_import_layout_button">
<property name="toolTip">
<string>&lt;p&gt;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.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Import list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tb_export_layout_button">
<property name="toolTip">
<string>&lt;p&gt;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.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Export list</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="5" column="0" colspan="3">
<layout class="QGridLayout" name="gridlayout_22">
<item row="0" column="0">
<widget class="QCheckBox" name="opt_show_avg_rating">
<property name="toolTip">
<string>Show the average rating per item indication in the Tag browser</string>
</property>
<property name="text">
<string>Show &amp;average ratings</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="opt_show_links_in_tag_browser">
<property name="toolTip">
<string>Show an icon if the item has an attached link</string>
</property>
<property name="text">
<string>Show &amp;links icons</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="opt_tag_browser_show_tooltips">
<property name="text">
<string>Show &amp;tooltips</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="opt_tag_browser_show_counts">
<property name="toolTip">
<string>&lt;p&gt;Show counts for items in the Tag browser. Such as the number of books
by each author, the number of authors, etc. If you turn it off, you can still
see the counts by hovering your mouse over any item.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Show &amp;counts</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="opt_show_notes_in_tag_browser">
<property name="toolTip">
<string>Show an icon if the item has an attached note</string>
</property>
<property name="text">
<string>Show &amp;notes icons</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="opt_tag_browser_old_look">
<property name="text">
<string>Use &amp;alternating row colors</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="opt_tag_browser_hide_empty_categories">
<property name="toolTip">
<string>&lt;p&gt;When checked, calibre will automatically hide any category
(a column, custom or standard) that has no items to show. For example, some
categories might not have values when using Virtual libraries. Checking this
box will cause these empty categories to be hidden.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Hide empt&amp;y categories (columns)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="opt_icons_on_right_in_tag_browser">
<property name="toolTip">
<string>If checked the notes and links icons will be placed at the right, after
the count and in columns. If unchecked, the icons will be placed immediately after the text,
to the left of the count and not in columns.</string>
</property>
<property name="text">
<string>Place icons on the &amp;right, in columns</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="opt_tag_browser_always_autocollapse">
<property name="toolTip">
<string>&lt;p&gt;When checked, Find in the Tag browser will show all items
that match the search instead of the first one. If Hide empty categories is
also checked then only categories containing a matched item will be shown.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Find &amp;shows all items that match</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="opt_tag_browser_restore_tree_expansion">
<property name="toolTip">
<string>&lt;p&gt;Ensure the last &quot;used&quot; item in the Tag browser is visible when opening a library.
An item is &quot;used&quot; when it is expanded, collapsed, or clicked.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Expand tr&amp;ee to show last used item</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="0" colspan="3">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="opt_tag_browser_allow_keyboard_focus">
<property name="toolTip">
<string>&lt;p&gt;When checked, the Tag browser can get keyboard focus, allowing
use of the keyboard to navigate the tree using the arrow keys. The Enter key simulates
a click on the selected item. The keyboard shortcut 'Tag browser /
Give the Tag browser keyboard focus' changes the keyboard focus without
using the mouse.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Allow the Tag browser to have keyboard &amp;focus</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tb_focus_label">
<property name="styleSheet">
<string notr="true">margin-left: 1.5em</string>
</property>
<property name="text">
<string>&lt;p style=&quot;text-indent: 2em; font-size:smaller&quot;&gt;
If you enable this option then you should set a keyboard shortcut to
focus the Tag browser under
&lt;code&gt;Preferences-&amp;gt;Shortcuts-&amp;gt;Tag browser-&amp;gt;Give
the Tag browser keyboard focus&lt;/code&gt;&lt;/p&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="0" colspan="3">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Spacing between &amp;items:</string>
</property>
<property name="buddy">
<cstring>opt_tag_browser_item_padding</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="opt_tag_browser_item_padding">
<property name="toolTip">
<string>&lt;p&gt;The spacing between consecutive items in the Tag browser.
In units of (ex) which is the approximate height of the letter 'x' in the
currently used font.&lt;/p&gt;</string>
</property>
<property name="suffix">
<string>ex</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-1.000000000000000</double>
</property>
<property name="maximum">
<double>2.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tb_tab2">
<attribute name="title"> <attribute name="title">
<string>&amp;Partitioning and collapsing</string> <string>&amp;Partitioning and collapsing</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>&amp;Category partitioning method:</string>
</property>
<property name="buddy">
<cstring>opt_tags_browser_partition_method</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="opt_tags_browser_partition_method">
<property name="toolTip">
<string>&lt;p&gt;Choose how Tag browser subcategories are displayed when
there are more items than the limit. Select by first
letter to see an A, B, C list. Choose partitioned to
have a list of fixed-sized groups. Set to disabled
if you never want subcategories&lt;/p&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Combine letters &amp;when fewer items than:</string>
</property>
<property name="buddy">
<cstring>opt_tags_browser_collapse_fl_at</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="opt_tags_browser_collapse_fl_at">
<property name="toolTip">
<string>&lt;p&gt;If collapsing by first letter, combine adjacent letters together if
there are fewer items under a letter than specified here. If the partition method is
not set to first letter, this value is ignored. Set to zero to disable.&lt;/p&gt;</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Co&amp;llapse when more items than:</string>
</property>
<property name="buddy">
<cstring>opt_tags_browser_collapse_at</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="opt_tags_browser_collapse_at">
<property name="toolTip">
<string>&lt;p&gt;If a Tag browser category has more than this number of items, it is divided
up into subcategories. If the partition method is set to disable, this value is ignored.&lt;/p&gt;</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>&lt;p&gt;Check the box for categories that are to
be partitioned using the criteria above. Uncheck the box if you don't want to
partition a category even if the number of items is larger than
the value shown above. This option can be used to
avoid collapsing hierarchical categories that have only
a few top-level elements.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Select categories to &amp;partition:</string>
</property>
<property name="buddy">
<cstring>tb_cats_to_partition</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QListView" name="tb_cats_to_partition">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_41">
<item>
<widget class="QPushButton" name="tb_partition_reset_button">
<property name="toolTip">
<string>Click this button to reset the list to its default order.</string>
</property>
<property name="text">
<string>Reset list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tb_partition_import_layout_button">
<property name="toolTip">
<string>&lt;p&gt;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 for each one.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Import list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tb_partition_export_layout_button">
<property name="toolTip">
<string>&lt;p&gt;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 for each one.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Export list</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget> </widget>
<widget class="TbHierarchyTab" name="tb_hierarchy_tab"> <widget class="TbHierarchyTab" name="tb_hierarchy_tab">
<attribute name="title"> <attribute name="title">
@ -1800,7 +1328,7 @@ structure and you want to use the same for each one.&lt;/p&gt;</string>
</attribute> </attribute>
</widget> </widget>
</widget> </widget>
<widget class="QWidget" name="cover_browser_tab"> <widget class="CoverView" name="cover_view">
<attribute name="icon"> <attribute name="icon">
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/cover_flow.png</normaloff>:/images/cover_flow.png</iconset> <normaloff>:/images/cover_flow.png</normaloff>:/images/cover_flow.png</iconset>
@ -1808,13 +1336,8 @@ structure and you want to use the same for each one.&lt;/p&gt;</string>
<attribute name="title"> <attribute name="title">
<string>Cover &amp;browser</string> <string>Cover &amp;browser</string>
</attribute> </attribute>
<layout class="QHBoxLayout">
<item>
<widget class="CoverView" name="cover_view"/>
</item>
</layout>
</widget> </widget>
<widget class="QWidget" name="quickview_tab"> <widget class="QuickviewTab" name="quickview_tab">
<attribute name="icon"> <attribute name="icon">
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/quickview.png</normaloff>:/images/quickview.png</iconset> <normaloff>:/images/quickview.png</normaloff>:/images/quickview.png</iconset>
@ -1822,108 +1345,6 @@ structure and you want to use the same for each one.&lt;/p&gt;</string>
<attribute name="title"> <attribute name="title">
<string>&amp;Quickview</string> <string>&amp;Quickview</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_122">
<item row="1" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_122">
<item row="2" column="0">
<widget class="QCheckBox" name="opt_qv_respects_vls">
<property name="toolTip">
<string>&lt;p&gt;Check this box to make Quickview show books only in the
current Virtual library. If unchecked, Quickview ignores Virtual libraries. If
unchecked then only row changes are taken into account.&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Apply Virtual libraries</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="opt_qv_follows_column">
<property name="toolTip">
<string>&lt;p&gt;Check this box to make Quickview change the column being examined
when the column in the book list is changed using the cursor arrow keys&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Change Quickview item when book list column changes</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="opt_qv_retkey_changes_column">
<property name="toolTip">
<string>&lt;p&gt;Pressing Enter in a cell changes both the book and the
column being examined (the left-hand panel)&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Pressing Enter changes the examined column</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="opt_qv_dclick_changes_column">
<property name="toolTip">
<string>&lt;p&gt;Double-clicking in a cell changes both the book and the
column being examined (the left-hand panel)&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Double click changes examined column</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" rowspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Select columns to display</string>
</property>
<layout class="QGridLayout" name="gridLayout_32">
<item row="0" column="1">
<widget class="QToolButton" name="qv_up_button">
<property name="toolTip">
<string>Move up. Keyboard shortcut: Ctrl-Up arrow</string>
</property>
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/arrow-up.png</normaloff>:/images/arrow-up.png</iconset>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QToolButton" name="qv_down_button">
<property name="toolTip">
<string>Move down. Keyboard shortcut: Ctrl-Down arrow</string>
</property>
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/arrow-down.png</normaloff>:/images/arrow-down.png</iconset>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3">
<widget class="ListViewWithMoveByKeyPress" name="qv_display_order">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget> </widget>
</widget> </widget>
</item> </item>
@ -1961,6 +1382,16 @@ column being examined (the left-hand panel)&lt;/p&gt;</string>
<extends>QWidget</extends> <extends>QWidget</extends>
<header>calibre/gui2/preferences/look_feel.h</header> <header>calibre/gui2/preferences/look_feel.h</header>
</customwidget> </customwidget>
<customwidget>
<class>TbDisplayTab</class>
<extends>ConfigWidget</extends>
<header>calibre/gui2/preferences/look_feel_tabs.tb_display.h</header>
</customwidget>
<customwidget>
<class>TbPartitioningTab</class>
<extends>ConfigWidget</extends>
<header>calibre/gui2/preferences/look_feel_tabs.tb_partitioning.h</header>
</customwidget>
<customwidget> <customwidget>
<class>TbIconRulesTab</class> <class>TbIconRulesTab</class>
<extends>ConfigWidget</extends> <extends>ConfigWidget</extends>
@ -1976,6 +1407,11 @@ column being examined (the left-hand panel)&lt;/p&gt;</string>
<extends>ConfigWidget</extends> <extends>ConfigWidget</extends>
<header>calibre/gui2/preferences/look_feel_tabs.cover_view.h</header> <header>calibre/gui2/preferences/look_feel_tabs.cover_view.h</header>
</customwidget> </customwidget>
<customwidget>
<class>QuickviewTab</class>
<extends>ConfigWidget</extends>
<header>calibre/gui2/preferences/look_feel_tabs.tb_quickview.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="../../../../resources/images.qrc"/> <include location="../../../../resources/images.qrc"/>
@ -1997,21 +1433,5 @@ column being examined (the left-hand panel)&lt;/p&gt;</string>
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>opt_tag_browser_allow_keyboard_focus</sender>
<signal>toggled(bool)</signal>
<receiver>tb_focus_label</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>275</x>
<y>528</y>
</hint>
<hint type="destinationlabel">
<x>358</x>
<y>555</y>
</hint>
</hints>
</connection>
</connections> </connections>
</ui> </ui>

View File

@ -5,9 +5,17 @@ __license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import json
from qt.core import QAbstractListModel, QIcon, QItemSelectionModel, Qt from qt.core import QAbstractListModel, QIcon, QItemSelectionModel, Qt
from calibre.gui2 import (
choose_files,
choose_save_file,
config,
error_dialog,
gprefs,
)
from calibre.gui2.book_details import get_field_list from calibre.gui2.book_details import get_field_list
@ -100,6 +108,36 @@ class DisplayedFields(QAbstractListModel): # {{{
self.changed = True self.changed = True
return idx return idx
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'),
filters=[(_('Column list'), ['json'])])
if filename:
try:
with open(filename, 'w') as f:
json.dump(model.fields, f, indent=1)
except Exception as err:
error_dialog(in_widget, _('Export field layout'),
_('<p>Could not write field list. Error:<br>%s')%err, show=True)
def import_layout(in_widget, model=None):
filename = choose_files(in_widget, 'look_feel_prefs_import_export_field_list',
_('Load column list from file'),
filters=[(_('Column list'), ['json'])])
if filename:
try:
with open(filename[0]) as f:
fields = json.load(f)
model.initialize(pref_data_override=fields)
in_widget.changed_signal.emit()
except Exception as err:
error_dialog(in_widget, _('Import layout'),
_('<p>Could not read field list. Error:<br>%s')%err, show=True)
def reset_layout(in_widget, model=None):
model.initialize(use_defaults=True)
in_widget.changed_signal.emit()
def move_field_up(widget, model): def move_field_up(widget, model):
idx = widget.currentIndex() idx = widget.currentIndex()

View File

@ -2,7 +2,7 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2025, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
@ -10,15 +10,15 @@ from qt.core import QKeySequence
from calibre.gui2 import config, gprefs from calibre.gui2 import config, gprefs
from calibre.gui2.dialogs.template_dialog import TemplateDialog from calibre.gui2.dialogs.template_dialog import TemplateDialog
from calibre.gui2.preferences import ConfigTabWidget, ConfigWidgetBase, set_help_tips from calibre.gui2.preferences import LazyConfigWidgetBase, ConfigWidgetBase, set_help_tips
from calibre.gui2.preferences.look_feel_tabs.cover_view_ui import Ui_Form from calibre.gui2.preferences.look_feel_tabs.cover_view_ui import Ui_Form
class CoverView(ConfigTabWidget, Ui_Form): class CoverView(LazyConfigWidgetBase, Ui_Form):
def genesis(self, gui): def genesis(self, gui):
self.gui = gui self.gui = gui
db = gui.library_view.model().db db = self.gui.library_view.model().db
r = self.register r = self.register
r('books_autoscroll_time', gprefs) r('books_autoscroll_time', gprefs)
@ -41,7 +41,7 @@ class CoverView(ConfigTabWidget, Ui_Form):
self.fs_help_msg.setText(self.fs_help_msg.text()%( self.fs_help_msg.setText(self.fs_help_msg.text()%(
QKeySequence(QKeySequence.StandardKey.FullScreen).toString(QKeySequence.SequenceFormat.NativeText))) QKeySequence(QKeySequence.StandardKey.FullScreen).toString(QKeySequence.SequenceFormat.NativeText)))
def initialize(self): def lazy_initialize(self):
ConfigWidgetBase.initialize(self) ConfigWidgetBase.initialize(self)
set_help_tips(self.opt_cover_browser_narrow_view_position, _( set_help_tips(self.opt_cover_browser_narrow_view_position, _(
'This option controls the position of the cover browser when using the Narrow user ' 'This option controls the position of the cover browser when using the Narrow user '
@ -51,11 +51,25 @@ class CoverView(ConfigTabWidget, Ui_Form):
'list. This option has no effect when using the Wide user interface layout.')) 'list. This option has no effect when using the Wide user interface layout.'))
def edit_cb_title_template(self): def edit_cb_title_template(self):
t = TemplateDialog(self, self.opt_cover_browser_title_template.text(), fm=self.gui.current_db.field_metadata) rows = self.gui.library_view.selectionModel().selectedRows()
mi = None
db = self.gui.current_db.new_api
if rows:
ids = list(map(self.gui.library_view.model().id, rows))
mi = []
for bk in ids[0:min(10, len(ids))]:
mi.append(db.get_proxy_metadata(bk))
t = TemplateDialog(self, self.opt_cover_browser_title_template.text(), mi=mi, fm=db.field_metadata)
t.setWindowTitle(_('Edit template for caption')) t.setWindowTitle(_('Edit template for caption'))
if t.exec(): if t.exec():
self.opt_cover_browser_title_template.setText(t.rule[1]) 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): def refresh_gui(self, gui):
gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections']) gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections'])
gui.cover_flow.setPreserveAspectRatio(gprefs['cb_preserve_aspect_ratio']) gui.cover_flow.setPreserveAspectRatio(gprefs['cb_preserve_aspect_ratio'])

View File

@ -0,0 +1,118 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2025, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
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.look_feel_tabs import (
DisplayedFields,
import_layout,
export_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): # {{{
# 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 TbDisplayTab(LazyConfigWidgetBase, Ui_Form):
def genesis(self, gui):
self.gui = gui
r = self.register
r('tag_browser_old_look', gprefs)
r('tag_browser_hide_empty_categories', gprefs)
r('tag_browser_always_autocollapse', gprefs)
r('tag_browser_restore_tree_expansion', gprefs)
r('tag_browser_show_tooltips', gprefs)
r('tag_browser_allow_keyboard_focus', gprefs)
r('tag_browser_show_counts', gprefs)
r('tag_browser_item_padding', gprefs)
r('show_avg_rating', config)
r('show_links_in_tag_browser', gprefs)
r('show_notes_in_tag_browser', gprefs)
r('icons_on_right_in_tag_browser', gprefs)
self.tb_display_model = TBDisplayedFields(self.gui.current_db, self.tb_display_order,
category_icons=self.gui.tags_view.model().category_custom_icons)
self.tb_display_model.dataChanged.connect(self.changed_signal)
self.tb_display_order.setModel(self.tb_display_model)
self.tb_reset_layout_button.clicked.connect(partial(reset_layout, self, model=self.tb_display_model))
self.tb_export_layout_button.clicked.connect(partial(export_layout, self, model=self.tb_display_model))
self.tb_import_layout_button.clicked.connect(partial(import_layout, self, model=self.tb_display_model))
self.tb_up_button.clicked.connect(self.tb_up_button_clicked)
self.tb_down_button.clicked.connect(self.tb_down_button_clicked)
self.tb_display_order.set_movement_functions(self.tb_up_button_clicked, self.tb_down_button_clicked)
def lazy_initialize(self):
self.tb_display_model.initialize()
self.tb_focus_label.setVisible(self.opt_tag_browser_allow_keyboard_focus.isChecked())
def tb_down_button_clicked(self):
idx = self.tb_display_order.currentIndex()
if idx.isValid():
row = idx.row()
model = self.tb_display_model
fields = model.fields
key = fields[row][0]
if not is_standard_category(key):
return
if row < len(fields) and is_standard_category(fields[row+1][0]):
move_field_down(self.tb_display_order, model)
def tb_up_button_clicked(self):
idx = self.tb_display_order.currentIndex()
if idx.isValid():
row = idx.row()
model = self.tb_display_model
fields = model.fields
key = fields[row][0]
if not is_standard_category(key):
return
move_field_up(self.tb_display_order, model)
def restore_defaults(self):
ConfigWidgetBase.restore_defaults(self)
self.tb_display_model.restore_defaults()
def commit(self):
self.tb_display_model.commit()
return ConfigWidgetBase.commit(self)

View File

@ -0,0 +1,360 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1096</width>
<height>791</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="verticalLayout_42">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>User categories and Saved searches cannot be moved</string>
</property>
<property name="text">
<string>Select the categories to display in the Tag browser, and their &amp;order</string>
</property>
<property name="buddy">
<cstring>tb_display_order</cstring>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="3">
<widget class="ListViewWithMoveByKeyPress" name="tb_display_order">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="tb_up_button">
<property name="toolTip">
<string>Move up. User categories and Saved searches cannot be moved. Keyboard shortcut: Ctrl-Up arrow</string>
</property>
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/arrow-up.png</normaloff>:/images/arrow-up.png</iconset>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="tb_down_button">
<property name="toolTip">
<string>Move down. User categories and Saved searches cannot be moved. Keyboard shortcut: Ctrl-Down arrow</string>
</property>
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/arrow-down.png</normaloff>:/images/arrow-down.png</iconset>
</property>
</widget>
</item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="tb_reset_layout_button">
<property name="toolTip">
<string>Click this button to reset the list to its default order.</string>
</property>
<property name="text">
<string>Reset list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tb_import_layout_button">
<property name="toolTip">
<string>&lt;p&gt;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.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Import list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tb_export_layout_button">
<property name="toolTip">
<string>&lt;p&gt;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.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Export list</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="5" column="0" colspan="3">
<layout class="QGridLayout" name="gridlayout_22">
<item row="0" column="0">
<widget class="QCheckBox" name="opt_show_avg_rating">
<property name="toolTip">
<string>Show the average rating per item indication in the Tag browser</string>
</property>
<property name="text">
<string>Show &amp;average ratings</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="opt_show_links_in_tag_browser">
<property name="toolTip">
<string>Show an icon if the item has an attached link</string>
</property>
<property name="text">
<string>Show &amp;links icons</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="opt_tag_browser_show_tooltips">
<property name="text">
<string>Show &amp;tooltips</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="opt_tag_browser_show_counts">
<property name="toolTip">
<string>&lt;p&gt;Show counts for items in the Tag browser. Such as the number of books
by each author, the number of authors, etc. If you turn it off, you can still
see the counts by hovering your mouse over any item.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Show &amp;counts</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="opt_show_notes_in_tag_browser">
<property name="toolTip">
<string>Show an icon if the item has an attached note</string>
</property>
<property name="text">
<string>Show &amp;notes icons</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="opt_tag_browser_old_look">
<property name="text">
<string>Use &amp;alternating row colors</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="opt_tag_browser_hide_empty_categories">
<property name="toolTip">
<string>&lt;p&gt;When checked, calibre will automatically hide any category
(a column, custom or standard) that has no items to show. For example, some
categories might not have values when using Virtual libraries. Checking this
box will cause these empty categories to be hidden.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Hide empt&amp;y categories (columns)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="opt_icons_on_right_in_tag_browser">
<property name="toolTip">
<string>If checked the notes and links icons will be placed at the right, after
the count and in columns. If unchecked, the icons will be placed immediately after the text,
to the left of the count and not in columns.</string>
</property>
<property name="text">
<string>Place icons on the &amp;right, in columns</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="opt_tag_browser_always_autocollapse">
<property name="toolTip">
<string>&lt;p&gt;When checked, Find in the Tag browser will show all items
that match the search instead of the first one. If Hide empty categories is
also checked then only categories containing a matched item will be shown.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Find &amp;shows all items that match</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="opt_tag_browser_restore_tree_expansion">
<property name="toolTip">
<string>&lt;p&gt;Ensure the last &quot;used&quot; item in the Tag browser is visible when opening a library.
An item is &quot;used&quot; when it is expanded, collapsed, or clicked.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Expand tr&amp;ee to show last used item</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="0" colspan="3">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="opt_tag_browser_allow_keyboard_focus">
<property name="toolTip">
<string>&lt;p&gt;When checked, the Tag browser can get keyboard focus, allowing
use of the keyboard to navigate the tree using the arrow keys. The Enter key simulates
a click on the selected item. The keyboard shortcut 'Tag browser /
Give the Tag browser keyboard focus' changes the keyboard focus without
using the mouse.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Allow the Tag browser to have keyboard &amp;focus</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tb_focus_label">
<property name="styleSheet">
<string notr="true">margin-left: 1.5em</string>
</property>
<property name="text">
<string>&lt;p style=&quot;text-indent: 2em; font-size:smaller&quot;&gt;
If you enable this option then you should set a keyboard shortcut to
focus the Tag browser under
&lt;code&gt;Preferences-&amp;gt;Shortcuts-&amp;gt;Tag browser-&amp;gt;Give
the Tag browser keyboard focus&lt;/code&gt;&lt;/p&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="0" colspan="3">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Spacing between &amp;items:</string>
</property>
<property name="buddy">
<cstring>opt_tag_browser_item_padding</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="opt_tag_browser_item_padding">
<property name="toolTip">
<string>&lt;p&gt;The spacing between consecutive items in the Tag browser.
In units of (ex) which is the approximate height of the letter 'x' in the
currently used font.&lt;/p&gt;</string>
</property>
<property name="suffix">
<string>ex</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-1.000000000000000</double>
</property>
<property name="maximum">
<double>2.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ListViewWithMoveByKeyPress</class>
<extends>QListView</extends>
<header>calibre/gui2/preferences.h</header>
</customwidget>
<customwidget>
<class>ListWidgetWithMoveByKeyPress</class>
<extends>QListWidget</extends>
<header>calibre/gui2/preferences.h</header>
</customwidget>
</customwidgets>
<connections>
<connection>
<sender>opt_tag_browser_allow_keyboard_focus</sender>
<signal>toggled(bool)</signal>
<receiver>tb_focus_label</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>275</x>
<y>528</y>
</hint>
<hint type="destinationlabel">
<x>358</x>
<y>555</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -11,7 +11,7 @@ from functools import partial
from qt.core import QListWidgetItem, Qt from qt.core import QListWidgetItem, Qt
from calibre.gui2 import choose_files, choose_save_file, error_dialog, gprefs from calibre.gui2 import choose_files, choose_save_file, error_dialog, gprefs
from calibre.gui2.preferences import ConfigTabWidget from calibre.gui2.preferences import LazyConfigWidgetBase
from calibre.gui2.preferences.look_feel_tabs import DisplayedFields from calibre.gui2.preferences.look_feel_tabs import DisplayedFields
from calibre.gui2.preferences.look_feel_tabs.tb_hierarchy_ui import Ui_Form from calibre.gui2.preferences.look_feel_tabs.tb_hierarchy_ui import Ui_Form
@ -54,18 +54,16 @@ class TBHierarchicalFields(DisplayedFields): # {{{
# }}} # }}}
class TbHierarchyTab(ConfigTabWidget, Ui_Form): class TbHierarchyTab(LazyConfigWidgetBase, Ui_Form):
def genesis(self, gui): def genesis(self, gui):
self.gui = gui self.gui = gui
self.tab_opened = False
self.tb_search_order_up_button.clicked.connect(self.move_tb_search_up) self.tb_search_order_up_button.clicked.connect(self.move_tb_search_up)
self.tb_search_order_down_button.clicked.connect(self.move_tb_search_down) self.tb_search_order_down_button.clicked.connect(self.move_tb_search_down)
self.tb_search_order.set_movement_functions(self.move_tb_search_up, self.move_tb_search_down) self.tb_search_order.set_movement_functions(self.move_tb_search_up, self.move_tb_search_down)
self.tb_search_order_reset_button.clicked.connect(self.reset_tb_search_order) self.tb_search_order_reset_button.clicked.connect(self.reset_tb_search_order)
def lazy_populate_content(self): def lazy_initialize(self):
self.tab_opened = True
self.fill_tb_search_order_box() self.fill_tb_search_order_box()
self.tb_hierarchical_cats_model = TBHierarchicalFields(self.gui.current_db, self.tb_hierarchical_cats, self.tb_hierarchical_cats_model = TBHierarchicalFields(self.gui.current_db, self.tb_hierarchical_cats,
@ -170,7 +168,10 @@ class TbHierarchyTab(ConfigTabWidget, Ui_Form):
error_dialog(self, _('Import layout'), error_dialog(self, _('Import layout'),
_('<p>Could not read field list. Error:<br>%s')%err, show=True) _('<p>Could not read field list. Error:<br>%s')%err, show=True)
def restore_defaults(self):
self.tb_hierarchical_cats_model.restore_defaults()
self.reset_tb_search_order()
def commit(self): def commit(self):
if self.tab_opened:
self.tb_search_order_commit() self.tb_search_order_commit()
self.tb_hierarchical_cats_model.commit() self.tb_hierarchical_cats_model.commit()

View File

@ -2,7 +2,7 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2025, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import copy import copy
@ -14,7 +14,7 @@ from qt.core import QAbstractItemView, QApplication, QIcon, QMenu, Qt, QTableWid
from calibre.constants import config_dir from calibre.constants import config_dir
from calibre.db.constants import TEMPLATE_ICON_INDICATOR from calibre.db.constants import TEMPLATE_ICON_INDICATOR
from calibre.gui2 import gprefs from calibre.gui2 import gprefs
from calibre.gui2.preferences import ConfigTabWidget, ConfigWidgetBase from calibre.gui2.preferences import ConfigWidgetBase, LazyConfigWidgetBase
from calibre.gui2.preferences.look_feel_tabs.tb_icon_rules_ui import Ui_Form from calibre.gui2.preferences.look_feel_tabs.tb_icon_rules_ui import Ui_Form
DELETED_COLUMN = 0 DELETED_COLUMN = 0
@ -98,7 +98,7 @@ class ChildrenTableWidgetItem(QTableWidgetItem):
self.setIcon(QIcon.cached_icon(icon)) self.setIcon(QIcon.cached_icon(icon))
class TbIconRulesTab(ConfigTabWidget, Ui_Form): class TbIconRulesTab(LazyConfigWidgetBase, Ui_Form):
def genesis(self, gui): def genesis(self, gui):
self.gui = gui self.gui = gui
@ -147,7 +147,7 @@ class TbIconRulesTab(ConfigTabWidget, Ui_Form):
except Exception: except Exception:
pass pass
def lazy_populate_content(self): def lazy_initialize(self):
field_metadata = self.gui.current_db.field_metadata field_metadata = self.gui.current_db.field_metadata
category_icons = self.gui.tags_view.model().category_custom_icons category_icons = self.gui.tags_view.model().category_custom_icons
v = gprefs['tags_browser_value_icons'] v = gprefs['tags_browser_value_icons']
@ -256,7 +256,6 @@ class TbIconRulesTab(ConfigTabWidget, Ui_Form):
self.rules_table.sortByColumn(FOR_CHILDREN_COLUMN, Qt.SortOrder(self.for_children_order)) self.rules_table.sortByColumn(FOR_CHILDREN_COLUMN, Qt.SortOrder(self.for_children_order))
def commit(self): def commit(self):
rr = ConfigWidgetBase.commit(self)
v = copy.deepcopy(gprefs['tags_browser_value_icons']) v = copy.deepcopy(gprefs['tags_browser_value_icons'])
for r in range(self.rules_table.rowCount()): for r in range(self.rules_table.rowCount()):
cat_item = self.rules_table.item(r, CATEGORY_COLUMN) cat_item = self.rules_table.item(r, CATEGORY_COLUMN)
@ -275,4 +274,4 @@ class TbIconRulesTab(ConfigTabWidget, Ui_Form):
if len(v[category]) == 0: if len(v[category]) == 0:
v.pop(category, None) v.pop(category, None)
gprefs['tags_browser_value_icons'] = v gprefs['tags_browser_value_icons'] = v
return rr return ConfigWidgetBase.commit(self)

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2025, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from functools import partial
from calibre.gui2 import gprefs
from calibre.gui2.preferences import ConfigWidgetBase, 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): # {{{
# 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 = {k:v for k,v in 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 TbPartitioningTab(LazyConfigWidgetBase, Ui_Form):
def genesis(self, gui):
self.gui = gui
r = self.register
choices = [(_('Disabled'), 'disable'), (_('By first letter'), 'first letter'),
(_('Partitioned'), 'partition')]
r('tags_browser_partition_method', gprefs, choices=choices)
r('tags_browser_collapse_at', gprefs)
r('tags_browser_collapse_fl_at', gprefs)
self.tb_categories_to_part_model = TBPartitionedFields(self.gui.current_db,
self.tb_cats_to_partition,
category_icons=self.gui.tags_view.model().category_custom_icons)
self.tb_categories_to_part_model.dataChanged.connect(self.changed_signal)
self.tb_cats_to_partition.setModel(self.tb_categories_to_part_model)
self.tb_partition_reset_button.clicked.connect(partial(reset_layout, self,
model=self.tb_categories_to_part_model))
self.tb_partition_export_layout_button.clicked.connect(partial(export_layout, self,
model=self.tb_categories_to_part_model))
self.tb_partition_import_layout_button.clicked.connect(partial(import_layout, self,
model=self.tb_categories_to_part_model))
def lazy_initialize(self):
self.tb_categories_to_part_model.initialize()
def commit(self):
self.tb_categories_to_part_model.commit()
return ConfigWidgetBase.commit(self)

View File

@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1035</width>
<height>547</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>&amp;Category partitioning method:</string>
</property>
<property name="buddy">
<cstring>opt_tags_browser_partition_method</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="opt_tags_browser_partition_method">
<property name="toolTip">
<string>&lt;p&gt;Choose how Tag browser subcategories are displayed when
there are more items than the limit. Select by first
letter to see an A, B, C list. Choose partitioned to
have a list of fixed-sized groups. Set to disabled
if you never want subcategories&lt;/p&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Combine letters &amp;when fewer items than:</string>
</property>
<property name="buddy">
<cstring>opt_tags_browser_collapse_fl_at</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="opt_tags_browser_collapse_fl_at">
<property name="toolTip">
<string>&lt;p&gt;If collapsing by first letter, combine adjacent letters together if
there are fewer items under a letter than specified here. If the partition method is
not set to first letter, this value is ignored. Set to zero to disable.&lt;/p&gt;</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Co&amp;llapse when more items than:</string>
</property>
<property name="buddy">
<cstring>opt_tags_browser_collapse_at</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="opt_tags_browser_collapse_at">
<property name="toolTip">
<string>&lt;p&gt;If a Tag browser category has more than this number of items, it is divided
up into subcategories. If the partition method is set to disable, this value is ignored.&lt;/p&gt;</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>&lt;p&gt;Check the box for categories that are to
be partitioned using the criteria above. Uncheck the box if you don't want to
partition a category even if the number of items is larger than
the value shown above. This option can be used to
avoid collapsing hierarchical categories that have only
a few top-level elements.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Select categories to &amp;partition:</string>
</property>
<property name="buddy">
<cstring>tb_cats_to_partition</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QListView" name="tb_cats_to_partition">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_41">
<item>
<widget class="QPushButton" name="tb_partition_reset_button">
<property name="toolTip">
<string>Click this button to reset the list to its default order.</string>
</property>
<property name="text">
<string>Reset list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tb_partition_import_layout_button">
<property name="toolTip">
<string>&lt;p&gt;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 for each one.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Import list</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tb_partition_export_layout_button">
<property name="toolTip">
<string>&lt;p&gt;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 for each one.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Export list</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</ui>

View File

@ -0,0 +1,74 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2025, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
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 LazyConfigWidgetBase, ConfigWidgetBase
from calibre.gui2.preferences.look_feel_tabs import DisplayedFields, move_field_down, move_field_up
from calibre.gui2.preferences.look_feel_tabs.tb_quickview_ui import Ui_Form
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 QuickviewTab(LazyConfigWidgetBase, Ui_Form):
def genesis(self, gui):
self.gui = gui
r = self.register
r('qv_respects_vls', gprefs)
r('qv_dclick_changes_column', gprefs)
r('qv_retkey_changes_column', gprefs)
r('qv_follows_column', gprefs)
self.qv_display_model = QVDisplayedFields(self.gui.current_db, self.qv_display_order)
self.qv_display_model.dataChanged.connect(self.changed_signal)
self.qv_display_order.setModel(self.qv_display_model)
mu = partial(move_field_up, self.qv_display_order, self.qv_display_model)
md = partial(move_field_down, self.qv_display_order, self.qv_display_model)
self.qv_display_order.set_movement_functions(mu, md)
self.qv_up_button.clicked.connect(mu)
self.qv_down_button.clicked.connect(md)
def lazy_initialize(self):
self.qv_display_model.initialize()
def restore_defaults(self):
ConfigWidgetBase.restore_defaults(self)
self.qv_display_model.restore_defaults()
def refresh_gui(self, gui):
qv = get_quickview_action_plugin()
if qv:
qv.refill_quickview()
def commit(self, *args):
rr = ConfigWidgetBase.commit(self, *args)
self.qv_display_model.commit()
return rr

View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="cover_browser_tab">
<attribute name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/cover_flow.png</normaloff>:/images/cover_flow.png</iconset>
</attribute>
<layout class="QGridLayout" name="gridLayout_122">
<item row="1" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_122">
<item row="2" column="0">
<widget class="QCheckBox" name="opt_qv_respects_vls">
<property name="toolTip">
<string>&lt;p&gt;Check this box to make Quickview show books only in the
current Virtual library. If unchecked, Quickview ignores Virtual libraries. If
unchecked then only row changes are taken into account.&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Apply Virtual libraries</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="opt_qv_follows_column">
<property name="toolTip">
<string>&lt;p&gt;Check this box to make Quickview change the column being examined
when the column in the book list is changed using the cursor arrow keys&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Change Quickview item when book list column changes</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="opt_qv_retkey_changes_column">
<property name="toolTip">
<string>&lt;p&gt;Pressing Enter in a cell changes both the book and the
column being examined (the left-hand panel)&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Pressing Enter changes the examined column</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="opt_qv_dclick_changes_column">
<property name="toolTip">
<string>&lt;p&gt;Double-clicking in a cell changes both the book and the
column being examined (the left-hand panel)&lt;/p&gt;</string>
</property>
<property name="text">
<string>&amp;Double click changes examined column</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" rowspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Select columns to display</string>
</property>
<layout class="QGridLayout" name="gridLayout_32">
<item row="0" column="1">
<widget class="QToolButton" name="qv_up_button">
<property name="toolTip">
<string>Move up. Keyboard shortcut: Ctrl-Up arrow</string>
</property>
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/arrow-up.png</normaloff>:/images/arrow-up.png</iconset>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QToolButton" name="qv_down_button">
<property name="toolTip">
<string>Move down. Keyboard shortcut: Ctrl-Down arrow</string>
</property>
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/arrow-down.png</normaloff>:/images/arrow-down.png</iconset>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3">
<widget class="ListViewWithMoveByKeyPress" name="qv_display_order">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ListViewWithMoveByKeyPress</class>
<extends>QListView</extends>
<header>calibre/gui2/preferences.h</header>
</customwidget>
<customwidget>
<class>ListWidgetWithMoveByKeyPress</class>
<extends>QListWidget</extends>
<header>calibre/gui2/preferences.h</header>
</customwidget>
</customwidgets>
</ui>

View File

@ -418,6 +418,7 @@ class Preferences(QDialog):
return self.close_after_initial or (must_restart and rc) or do_restart return self.close_after_initial or (must_restart and rc) or do_restart
def restore_defaults(self, *args): def restore_defaults(self, *args):
self.showing_widget.do_on_child_tabs('restore_defaults')
self.showing_widget.restore_defaults() self.showing_widget.restore_defaults()
def on_shutdown(self): def on_shutdown(self):