This commit is contained in:
Kovid Goyal 2025-05-11 19:50:04 +05:30
commit a1a611cd1a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 72 additions and 12 deletions

View File

@ -149,17 +149,32 @@ class Plugin: # {{{
from calibre.gui2 import gprefs from calibre.gui2 import gprefs
class ConfigDialog(QDialog):
def __init__(self, parent, config_widget):
super().__init__(parent)
self.config_widget = config_widget
def accept(self):
if ((validate := getattr(self.config_widget, 'validate', None)) and
getattr(self.config_widget, 'validate_before_accept', False)):
if not validate():
return
super().accept()
try:
config_widget = self.config_widget()
except NotImplementedError:
config_widget = None
prefname = 'plugin config dialog:'+self.type + ':' + self.name prefname = 'plugin config dialog:'+self.type + ':' + self.name
config_dialog = QDialog(parent)
config_dialog = ConfigDialog(parent, config_widget)
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
v = QVBoxLayout(config_dialog) v = QVBoxLayout(config_dialog)
button_box.accepted.connect(config_dialog.accept) button_box.accepted.connect(config_dialog.accept)
button_box.rejected.connect(config_dialog.reject) button_box.rejected.connect(config_dialog.reject)
config_dialog.setWindowTitle(_('Customize') + ' ' + self.name) config_dialog.setWindowTitle(_('Customize') + ' ' + self.name)
try:
config_widget = self.config_widget()
except NotImplementedError:
config_widget = None
if isinstance(config_widget, tuple): if isinstance(config_widget, tuple):
from calibre.gui2 import warning_dialog from calibre.gui2 import warning_dialog

View File

@ -54,11 +54,13 @@ class KOBOTOUCHConfig(TabbedDeviceConfig):
def __init__(self, device_settings, all_formats, supports_subdirs, def __init__(self, device_settings, all_formats, supports_subdirs,
must_read_metadata, supports_use_author_sort, must_read_metadata, supports_use_author_sort,
extra_customization_message, device, extra_customization_choices=None, parent=None): extra_customization_message, device, extra_customization_choices=None,
parent=None):
super().__init__(device_settings, all_formats, supports_subdirs, super().__init__(device_settings, all_formats, supports_subdirs,
must_read_metadata, supports_use_author_sort, must_read_metadata, supports_use_author_sort,
extra_customization_message, device, extra_customization_choices, parent) extra_customization_message, device, extra_customization_choices, parent,
validate_before_accept=True)
self.device_settings = device_settings self.device_settings = device_settings
self.all_formats = all_formats self.all_formats = all_formats
@ -88,6 +90,7 @@ class KOBOTOUCHConfig(TabbedDeviceConfig):
def validate(self): def validate(self):
validated = super().validate() validated = super().validate()
validated &= self.tab2.validate() validated &= self.tab2.validate()
validated &= self.tab1.validate()
return validated return validated
@property @property
@ -193,6 +196,12 @@ class Tab1Config(DeviceConfigTab): # {{{
self.addDeviceWidget(self.book_uploads_options) self.addDeviceWidget(self.book_uploads_options)
self.l.addStretch() self.l.addStretch()
def validate(self):
v = self.collections_options.validate()
v &= self.book_uploads_options.validate()
return v
# }}} # }}}
@ -364,6 +373,7 @@ class BookUploadsGroupBox(DeviceOptionsGroupBox):
self.template_la = la = QLabel('\xa0\xa0' + _('Template to decide conversion:')) self.template_la = la = QLabel('\xa0\xa0' + _('Template to decide conversion:'))
self.kepubify_template_edit = TemplateConfig( self.kepubify_template_edit = TemplateConfig(
self.kepubify_checkbox.text(),
device.get_pref('template_for_kepubify'), device.get_pref('template_for_kepubify'),
tooltip='<p>' + _( tooltip='<p>' + _(
'Enter a template to decide if an EPUB book is to be auto converted to KEPUB. ' 'Enter a template to decide if an EPUB book is to be auto converted to KEPUB. '
@ -399,6 +409,9 @@ class BookUploadsGroupBox(DeviceOptionsGroupBox):
def update_template_state(self): def update_template_state(self):
self.kepubify_template_edit.setEnabled(self.kepubify) self.kepubify_template_edit.setEnabled(self.kepubify)
def validate(self):
return self.kepubify_template_edit.validate()
@property @property
def override_kobo_replace_existing(self): def override_kobo_replace_existing(self):
return self.override_kobo_replace_existing_checkbox.isChecked() return self.override_kobo_replace_existing_checkbox.isChecked()
@ -518,6 +531,7 @@ class CollectionsGroupBox(DeviceOptionsGroupBox):
device.get_pref('use_collections_template') device.get_pref('use_collections_template')
) )
self.collections_template_edit = TemplateConfig( self.collections_template_edit = TemplateConfig(
self.use_collections_template_checkbox.text(),
device.get_pref('collections_template'), device.get_pref('collections_template'),
tooltip=_("Enter a template to generate collections." tooltip=_("Enter a template to generate collections."
" The result of the template will be combined with the values from Collections column." " The result of the template will be combined with the values from Collections column."
@ -559,6 +573,27 @@ class CollectionsGroupBox(DeviceOptionsGroupBox):
self.use_collections_columns_checkbox_clicked(device.get_pref('use_collections_columns')) self.use_collections_columns_checkbox_clicked(device.get_pref('use_collections_columns'))
self.use_collections_template_checkbox_clicked(device.get_pref('use_collections_template')) self.use_collections_template_checkbox_clicked(device.get_pref('use_collections_template'))
def validate(self):
v = self.validate_collections_columns()
v &= self.collections_template_edit.validate()
return v
def validate_collections_columns(self):
from calibre.gui2.ui import get_gui
db = get_gui().current_db
fm = db.field_metadata
bad_names = []
for l in [v.strip() for v in self.collections_columns.split(',') if v.strip()]:
if l not in fm.keys():
bad_names.append(l)
if bad_names:
s = ', '.join(bad_names)
error_dialog(self, _('Kobo configuration: Invalid collection column names'),
'<p>'+_("Collection column names that don't exist in the library: {0}").format(s),
show=True)
return False
return True
def use_collections_columns_checkbox_clicked(self, checked): def use_collections_columns_checkbox_clicked(self, checked):
self.collections_columns_edit.setEnabled(checked) self.collections_columns_edit.setEnabled(checked)
@ -872,6 +907,7 @@ class MetadataGroupBox(DeviceOptionsGroupBox):
device.get_pref('update_subtitle') device.get_pref('update_subtitle')
) )
self.subtitle_template_edit = TemplateConfig( self.subtitle_template_edit = TemplateConfig(
self.update_subtitle_checkbox.text(),
device.get_pref('subtitle_template'), device.get_pref('subtitle_template'),
tooltip=_('Enter a template to use to set the subtitle. ' tooltip=_('Enter a template to use to set the subtitle. '
'If the template is empty, the subtitle will be cleared.' 'If the template is empty, the subtitle will be cleared.'
@ -883,6 +919,7 @@ class MetadataGroupBox(DeviceOptionsGroupBox):
device.get_pref('update_bookstats') device.get_pref('update_bookstats')
) )
self.bookstats_wordcount_template_edit = TemplateConfig( self.bookstats_wordcount_template_edit = TemplateConfig(
self.update_bookstats_checkbox.text(),
device.get_pref('bookstats_wordcount_template'), device.get_pref('bookstats_wordcount_template'),
label=_('Words:'), label=_('Words:'),
tooltip=_('Enter a template to use to set the word count for the book. ' tooltip=_('Enter a template to use to set the word count for the book. '
@ -890,6 +927,7 @@ class MetadataGroupBox(DeviceOptionsGroupBox):
) )
) )
self.bookstats_pagecount_template_edit = TemplateConfig( self.bookstats_pagecount_template_edit = TemplateConfig(
_('Pages'),
device.get_pref('bookstats_pagecount_template'), device.get_pref('bookstats_pagecount_template'),
label=_('Pages:'), label=_('Pages:'),
tooltip=_('Enter a template to use to set the page count for the book. ' tooltip=_('Enter a template to use to set the page count for the book. '
@ -899,6 +937,7 @@ class MetadataGroupBox(DeviceOptionsGroupBox):
self.bookstats_timetoread_label = QLabel(_('Hours to read estimates:')) self.bookstats_timetoread_label = QLabel(_('Hours to read estimates:'))
self.bookstats_timetoread_upper_template_edit = TemplateConfig( self.bookstats_timetoread_upper_template_edit = TemplateConfig(
_('Upper estimate'),
device.get_pref('bookstats_timetoread_upper_template'), device.get_pref('bookstats_timetoread_upper_template'),
label=_('Upper:'), label=_('Upper:'),
tooltip=_('Enter a template to use to set the upper estimate of the time to read for the book. ' tooltip=_('Enter a template to use to set the upper estimate of the time to read for the book. '
@ -907,6 +946,7 @@ class MetadataGroupBox(DeviceOptionsGroupBox):
) )
) )
self.bookstats_timetoread_lower_template_edit = TemplateConfig( self.bookstats_timetoread_lower_template_edit = TemplateConfig(
_('Lower estimate'),
device.get_pref('bookstats_timetoread_lower_template'), device.get_pref('bookstats_timetoread_lower_template'),
label=_('Lower:'), label=_('Lower:'),
tooltip=_('Enter a template to use to set the lower estimate of the time to read for the book. ' tooltip=_('Enter a template to use to set the lower estimate of the time to read for the book. '
@ -1042,8 +1082,9 @@ class MetadataGroupBox(DeviceOptionsGroupBox):
class TemplateConfig(QWidget): # {{{ class TemplateConfig(QWidget): # {{{
def __init__(self, val, label=None, tooltip=None): def __init__(self, name, val, label=None, tooltip=None):
super().__init__() super().__init__()
self.name = name
self.l = l = QGridLayout(self) self.l = l = QGridLayout(self)
self.setLayout(l) self.setLayout(l)
col = 0 col = 0
@ -1080,10 +1121,10 @@ class TemplateConfig(QWidget): # {{{
tmpl = self.template tmpl = self.template
try: try:
validation_formatter.validate(tmpl) v = validation_formatter.validate(tmpl)
return True return True
except Exception as err: except Exception as err:
error_dialog(self, _('Invalid template'), error_dialog(self, _('Invalid template for {0}').format(self.name),
'<p>'+_('The template "%s" is invalid:')%tmpl + '<p>'+_('The template "%s" is invalid:')%tmpl +
'<br>'+str(err), show=True) '<br>'+str(err), show=True)

View File

@ -63,15 +63,19 @@ class TabbedDeviceConfig(QTabWidget):
DeviceConfigTab, for each set of options. Within the tabs, group boxes, subclassed DeviceConfigTab, for each set of options. Within the tabs, group boxes, subclassed
from DeviceOptionsGroupBox, are created to further group the options. The group from DeviceOptionsGroupBox, are created to further group the options. The group
boxes can be coded to support any control type and dependencies between them. boxes can be coded to support any control type and dependencies between them.
Set validate_before_accept to True if you want validation() to be called
when OK is pressed
''' '''
def __init__(self, device_settings, all_formats, supports_subdirs, def __init__(self, device_settings, all_formats, supports_subdirs,
must_read_metadata, supports_use_author_sort, must_read_metadata, supports_use_author_sort,
extra_customization_message, device, extra_customization_message, device,
extra_customization_choices=None, parent=None): extra_customization_choices=None, parent=None, validate_before_accept = False):
QTabWidget.__init__(self, parent) QTabWidget.__init__(self, parent)
self._device = weakref.ref(device) self._device = weakref.ref(device)
self.validate_before_accept = validate_before_accept
self.device_settings = device_settings self.device_settings = device_settings
self.all_formats = set(all_formats) self.all_formats = set(all_formats)
self.supports_subdirs = supports_subdirs self.supports_subdirs = supports_subdirs

View File

@ -2035,7 +2035,7 @@ class ValidateFormatter(TemplateFormatter):
def validate(self, x): def validate(self, x):
from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.book.base import Metadata
return self.safe_format(x, {}, 'VALIDATE ERROR', Metadata('')) return self.unsafe_format(x, {}, 'VALIDATE ERROR', Metadata(''))
validation_formatter = ValidateFormatter() validation_formatter = ValidateFormatter()