Three changes to template processing:

1) If the user presses ESC, ask "are you sure" if the template has changed.
2) Fix syntax highlighting of comments
3) Improve the python template example in the template docs, better using the new_api.
This commit is contained in:
Charles Haley 2022-10-12 11:24:49 +01:00
parent 3e4ee63f2b
commit 9b68f5c89a
2 changed files with 33 additions and 39 deletions

View File

@ -677,10 +677,17 @@ Here is an example of a PTM template that produces a list of all the authors for
def evaluate(book, context):
if book.series is None:
return ''
db = context.db.new_api
ans = set()
db = context.db
for id_ in db.search_getting_ids(f'series:"={book.series}"', ''):
ans.update(v.strip() for v in db.new_api.field_for('author_sort', id_).split('&'))
# Get the list of books in the series
ids = db.search(f'series:"={book.series}"', '')
if ids:
# Get all the author_sort values for the books in the series
author_sorts = (v for v in db.all_field_for('author_sort', ids).values())
# Add the names to the result set, removing duplicates
for aus in author_sorts:
ans.update(v.strip() for v in aus.split('&'))
# Make a sorted comma-separated string from the result set
return ', '.join(v.replace(',', ';') for v in sorted(ans))
The output in :guilabel:`Book details` looks like this:

View File

@ -18,7 +18,8 @@ from calibre import sanitize_file_name
from calibre.constants import config_dir
from calibre.ebooks.metadata.book.base import Metadata
from calibre.ebooks.metadata.book.formatter import SafeFormat
from calibre.gui2 import gprefs, error_dialog, choose_files, choose_save_file, pixmap_to_data
from calibre.gui2 import (gprefs, error_dialog, choose_files, choose_save_file,
pixmap_to_data, question_dialog)
from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog
from calibre.library.coloring import (displayable_columns, color_row_key)
from calibre.utils.config_base import tweaks
@ -84,45 +85,30 @@ class TemplateHighlighter(QSyntaxHighlighter):
r.append((re.compile(a), b))
if not for_python:
a(
r"\b[a-zA-Z]\w*\b(?!\(|\s+\()"
r"|\$+#?[a-zA-Z]\w*",
"identifier")
a(r"\b[a-zA-Z]\w*\b(?!\(|\s+\()"
r"|\$+#?[a-zA-Z]\w*",
"identifier")
a(r"^program:", "keymode")
a(
"|".join([r"\b%s\b" % keyword for keyword in self.KEYWORDS_GPM]),
"keyword")
a(
"|".join([r"\b%s\b" % builtin for builtin in
a("|".join([r"\b%s\b" % keyword for keyword in self.KEYWORDS_GPM]), "keyword")
a("|".join([r"\b%s\b" % builtin for builtin in
(builtin_functions if builtin_functions else
formatter_functions().get_builtins())]),
"builtin")
a(r"""(?<!:)'[^']*'|"[^"]*\"""", "string")
else:
a(r"^python:", "keymode")
a(
"|".join([r"\b%s\b" % keyword for keyword in self.KEYWORDS_PYTHON]),
"keyword")
a(
"|".join([r"\b%s\b" % builtin for builtin in self.BUILTINS_PYTHON]),
"builtin")
a(
"|".join([r"\b%s\b" % constant for constant in self.CONSTANTS_PYTHON]),
"constant")
a("|".join([r"\b%s\b" % keyword for keyword in self.KEYWORDS_PYTHON]), "keyword")
a("|".join([r"\b%s\b" % builtin for builtin in self.BUILTINS_PYTHON]), "builtin")
a("|".join([r"\b%s\b" % constant for constant in self.CONSTANTS_PYTHON]), "constant")
a(r"\bPyQt6\b|\bqt.core\b|\bQt?[A-Z][a-z]\w+\b", "pyqt")
a(r"@\w+(\.\w+)?\b", "decorator")
a(r"""('|").*?\1""", "string")
stringRe = r"""((?:"|'){3}).*?\1"""
a(stringRe, "string")
self.stringRe = re.compile(stringRe)
self.tripleSingleRe = re.compile(r"""'''(?!")""")
self.tripleDoubleRe = re.compile(r'''"""(?!')''')
a(r'#[^\n]*', "comment")
a(
r"\b[+-]?[0-9]+[lL]?\b"
r"|\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b"
@ -197,17 +183,6 @@ class TemplateHighlighter(QSyntaxHighlighter):
elif text[0] == "#":
self.setFormat(0, textLength, self.Formats["comment"])
return
elif self.for_python:
stack = []
for i, c in enumerate(text):
if c in ('"', "'"):
if stack and stack[-1] == c:
stack.pop()
else:
stack.append(c)
elif c == "#" and len(stack) == 0:
self.setFormat(i, len(text), self.Formats["comment"])
return
for regex, format_ in self.Rules:
for m in regex.finditer(text):
@ -430,6 +405,8 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
self.textbox.setPlainText(text)
else:
text = ''
self.original_text = text
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(_('&OK'))
self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(_('&Cancel'))
@ -852,6 +829,16 @@ def evaluate(book, context):
gprefs['template_editor_table_widths'] = self.table_column_widths
gprefs['template_editor_dialog_geometry'] = bytearray(self.saveGeometry())
def keyPressEvent(self, ev):
if ev.key() == Qt.Key.Key_Escape:
# Check about ESC to avoid killing the dialog by mistake
if self.textbox.toPlainText() != self.original_text:
r = question_dialog(self, _('Discard changes?'),
_('Do you really want to close this dialog, discarding any changes?'))
if not r:
return
QDialog.keyPressEvent(self, ev)
def accept(self):
txt = str(self.textbox.toPlainText()).rstrip()
if (self.coloring or self.iconing or self.embleming) and not txt: