From 393cfe78cdca8f96b4e85d40e612087af86076f0 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 29 May 2011 11:30:04 +0100 Subject: [PATCH] Improvements to editing templates and formatter exception reporting. Largest improvement is adding a value preview box to the template editor. --- src/calibre/ebooks/metadata/book/base.py | 39 +++++++-------- src/calibre/gui2/dialogs/template_dialog.py | 10 +++- src/calibre/gui2/dialogs/template_dialog.ui | 50 +++++++++++++------ .../gui2/dialogs/template_line_editor.py | 6 ++- src/calibre/gui2/library/delegates.py | 3 +- src/calibre/gui2/preferences/look_feel.py | 10 ++++ src/calibre/utils/formatter.py | 9 ++-- src/calibre/utils/formatter_functions.py | 21 +++----- 8 files changed, 92 insertions(+), 56 deletions(-) diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 5dc3f25dfb..179a96e578 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -41,27 +41,24 @@ field_metadata = FieldMetadata() class SafeFormat(TemplateFormatter): - def get_value(self, key, args, kwargs): - try: - key = key.lower() - if key != 'title_sort' and key not in TOP_LEVEL_IDENTIFIERS: - key = field_metadata.search_term_to_field_key(key) - b = self.book.get_user_metadata(key, False) - if b and b['datatype'] == 'int' and self.book.get(key, 0) == 0: - v = '' - elif b and b['datatype'] == 'float' and self.book.get(key, 0.0) == 0.0: - v = '' - else: - v = self.book.format_field(key, series_with_index=False)[1] - if v is None: - return '' - if v == '': - return '' - return v - except: - if DEBUG: - traceback.print_exc() - return key + def get_value(self, orig_key, args, kwargs): + key = orig_key.lower() + if key != 'title_sort' and key not in TOP_LEVEL_IDENTIFIERS: + key = field_metadata.search_term_to_field_key(key) + if key is None or key not in self.book.all_field_keys(): + raise ValueError(_('Value: unknown field ') + orig_key) + b = self.book.get_user_metadata(key, False) + if b and b['datatype'] == 'int' and self.book.get(key, 0) == 0: + v = '' + elif b and b['datatype'] == 'float' and self.book.get(key, 0.0) == 0.0: + v = '' + else: + v = self.book.format_field(key, series_with_index=False)[1] + if v is None: + return '' + if v == '': + return '' + return v composite_formatter = SafeFormat() diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index ca55bb0e66..083dacbf00 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -11,6 +11,7 @@ from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QSyntaxHighlighter, from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog from calibre.utils.formatter_functions import formatter_functions +from calibre.ebooks.metadata.book.base import composite_formatter class ParenPosition: @@ -194,10 +195,13 @@ class TemplateHighlighter(QSyntaxHighlighter): class TemplateDialog(QDialog, Ui_TemplateDialog): - def __init__(self, parent, text): + def __init__(self, parent, text, mi): QDialog.__init__(self, parent) Ui_TemplateDialog.__init__(self) self.setupUi(self) + + self.mi = mi + # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint)) @@ -233,12 +237,16 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.function.addItems(func_names) self.function.setCurrentIndex(0) self.function.currentIndexChanged[str].connect(self.function_changed) + self.textbox_changed() def textbox_changed(self): cur_text = unicode(self.textbox.toPlainText()) if self.last_text != cur_text: self.last_text = cur_text self.highlighter.regenerate_paren_positions() + self.template_value.setText( + composite_formatter.safe_format(cur_text, self.mi, + _('EXCEPTION: '), self.mi)) def text_cursor_changed(self): cursor = self.textbox.textCursor() diff --git a/src/calibre/gui2/dialogs/template_dialog.ui b/src/calibre/gui2/dialogs/template_dialog.ui index dd8fb7bd88..d36cbbd3d4 100644 --- a/src/calibre/gui2/dialogs/template_dialog.ui +++ b/src/calibre/gui2/dialogs/template_dialog.ui @@ -23,19 +23,39 @@ - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - + + + Template value: + + + template_value + + + The value the of the template using the current book in the library view + + + + + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + Function &name: @@ -45,10 +65,10 @@ - + - + &Documentation: @@ -61,7 +81,7 @@ - + Python &code: @@ -74,7 +94,7 @@ - + @@ -84,7 +104,7 @@ - + diff --git a/src/calibre/gui2/dialogs/template_line_editor.py b/src/calibre/gui2/dialogs/template_line_editor.py index 98b74b391d..a724b5b072 100644 --- a/src/calibre/gui2/dialogs/template_line_editor.py +++ b/src/calibre/gui2/dialogs/template_line_editor.py @@ -21,6 +21,10 @@ class TemplateLineEditor(QLineEdit): def __init__(self, parent): QLineEdit.__init__(self, parent) self.tags = None + self.mi = None + + def set_mi(self, mi): + self.mi = mi def set_tags(self, tags): self.tags = tags @@ -37,7 +41,7 @@ class TemplateLineEditor(QLineEdit): menu.exec_(event.globalPos()) def open_editor(self): - t = TemplateDialog(self, self.text()) + t = TemplateDialog(self, self.text(), self.mi) t.setWindowTitle(_('Edit template')) if t.exec_(): self.setText(t.textbox.toPlainText()) diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index 6990a76b21..94c3deb403 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -418,8 +418,9 @@ class CcTemplateDelegate(QStyledItemDelegate): # {{{ def createEditor(self, parent, option, index): m = index.model() + mi = m.db.get_metadata(index.row(), index_is_id=False) text = m.custom_columns[m.column_map[index.column()]]['display']['composite_template'] - editor = TemplateDialog(parent, text) + editor = TemplateDialog(parent, text, mi) editor.setWindowTitle(_("Edit template")) editor.textbox.setTabChangesFocus(False) editor.textbox.setTabStopWidth(20) diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index b6e6de1902..fcdd56fd5f 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -205,11 +205,21 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): choices.insert(0, '') self.column_color_count = db.column_color_count+1 tags = db.all_tags() + + mi=None + try: + idx = gui.library_view.currentIndex().row() + if idx: + mi = db.get_metadata(idx, index_is_id=False) + except: + pass + for i in range(1, self.column_color_count): r('column_color_name_'+str(i), db.prefs, choices=choices) r('column_color_template_'+str(i), db.prefs) tpl = getattr(self, 'opt_column_color_template_'+str(i)) tpl.set_tags(tags) + tpl.set_mi(mi) toolbutton = getattr(self, 'opt_column_color_wizard_'+str(i)) toolbutton.clicked.connect(tpl.tag_wizard) all_colors = [unicode(s) for s in list(QColor.colorNames())] diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index fccd0015c1..695355330e 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -98,7 +98,10 @@ class _Parser(object): cls = funcs['assign'] return cls.eval_(self.parent, self.parent.kwargs, self.parent.book, self.parent.locals, id, self.expr()) - return self.parent.locals.get(id, _('unknown id ') + id) + val = self.parent.locals.get(id, None) + if val is None: + self.error(_('Unknown identifier ') + id) + return val # We have a function. # Check if it is a known one. We do this here so error reporting is # better, as it can identify the tokens near the problem. @@ -317,8 +320,8 @@ class TemplateFormatter(string.Formatter): try: ans = self.vformat(fmt, [], kwargs).strip() except Exception as e: - if DEBUG: - traceback.print_exc() +# if DEBUG: +# traceback.print_exc() ans = error_value + ' ' + e.message return ans diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index bf597d5b9c..d7b6e63f5e 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -63,20 +63,13 @@ class FormatterFunction(object): raise NotImplementedError() def eval_(self, formatter, kwargs, mi, locals, *args): - try: - ret = self.evaluate(formatter, kwargs, mi, locals, *args) - if isinstance(ret, (str, unicode)): - return ret - if isinstance(ret, (int, float, bool)): - return unicode(ret) - if isinstance(ret, list): - return ','.join(list) - except: - traceback.print_exc() - exc_type, exc_value, exc_traceback = sys.exc_info() - info = ': '.join(traceback.format_exception(exc_type, exc_value, - exc_traceback)[-2:]).replace('\n', '') - return _('Exception ') + info + ret = self.evaluate(formatter, kwargs, mi, locals, *args) + if isinstance(ret, (str, unicode)): + return ret + if isinstance(ret, (int, float, bool)): + return unicode(ret) + if isinstance(ret, list): + return ','.join(list) all_builtin_functions = [] class BuiltinFormatterFunction(FormatterFunction):