El lenguaje de plantillas de calibre¶
El lenguaje de plantillas de calibre es un lenguaje específico de calibre que se utiliza en todo calibre para tareas como especificar rutas de archivo, dar formato a valores y calcular el valor de las columnas especificadas por el usuario. Ejemplos:
Especifar la estructura de carpetas y los nombres de archivo al guardar archivos de la biblioteca de calibre en el disco o en el lector de libros electrónicos.
Definir reglas para añadir iconos y colores a la lista de libros de calibre.
Definir columnas virtuales que contienen datos de otras columnas.
Búsqueda avanzada en la bibliotecas.
Búsqueda y sustitución avanzada de metadatos.
El lenguaje está construido en torno al concepto de una «plantilla», que especifica qué metadatos del libro se usan, las operaciones que se realizan sobre los metadatos y qué formato se aplica.
Plantillas básicas¶
Una plantilla básica consiste en una o varias expresiones de plantilla
. Una expresión de plantilla
consiste en texto y nombres entre llaves ({}
) que se sustituyen por los metadatos correspondientes del libro que está siendo procesado. Por ejemplo, la plantilla predeterminada de calibre para guardar libros en un dispositivo tiene 4 expresiones de plantilla
:
{author_sort}/{title}/{title} - {authors}
Para el libro «The Foundation» de «Isaac Asimov» da lugar a:
Asimov, Isaac/The Foundation/The Foundation - Isaac Asimov
Las barras no son expresiones de plantilla
porque no están entre {}
. Este tipo de texto se mantiene donde aparece. Por ejemplo, si la plantilla es:
{author_sort} Some Important Text {title}/{title} - {authors}
entonces para «The Foundation» la plantilla produce:
Asimov, Isaac Some Important Text The Foundation/The Foundation - Isaac Asimov
Una expresión de plantilla
tiene acceso a todos los metadatos disponibles en calibre, incluidas las columnas personalizadas que haya creado, usando su el nombre de búsqueda
de una columna. Para obtener el nombre de búsqueda o consulta de una columna (también llamada campo), pase el cursor sobre el encabezado de la columna en la lista de libros de calibre. Los nombres de búsqueda para las columnas personalizadas siempre empiezan por #
. Para columnas personalizadas de tipo serie, siempre hay un campo adicional llamado #nombre de búsqueda_index
que es el índice de serie para el libro en la serie. Por ejemplo, si tiene una columna personalizada de serie llamada #miserie
, también habrá una columna llamada #miserie_index
. La columna de índice para la serie normal se llama series_index
.
Además de los campos basados en columnas normales, también puede usar:
{formats}
- Una lista de los formatos disponibles en la biblioteca de calibre para un libro
{identifiers:select(isbn)}
- El ISBN del libro
Si un campo de metadatos para un libro determinado no está definido, entonces el campo en la plantilla se sustituye por un texto vacío (''
). Por ejemplo, considere la siguiente plantilla:
{author_sort}/{series}/{title} {series_index}
Si el libro de Asimov «Second Foundation» está en la serie «Foundation», la plantilla produce:
Asimov, Isaac/Foundation/Second Foundation 3
Si no se ha introducido la serie para el libro, la plantilla produce:
Asimov, Isaac/Second Foundation
El procesador de plantillas elimina automáticamente barras múltiples y espacios iniciales o finales.
Formato avanzado¶
Además de sustituir metadatos, las plantillas pueden incluir texto adicional de manera condicional y controlar el formato de los datos sustituidos.
Incluir texto de mane condicional
A veces puede querer que aparezca un texto en la salida sólo si un campo no está vacío. Un caso común es series
y series_index
, para los que puede querer o nada o ambos valores separados por un guión. calibre tiene en cuenta este caso usando una sintaxis de expresión de plantilla
especial.
Por ejemplo, y usando el caso anterior de la serie «Foundation», suponga que quiere que la plantilla produzca Foundation - 3 - Second Foundation. Esta plantilla cumple el cometido:
{series} - {series_index} - {title}
Sin embargo, si un libro no tiene serie, la plantilla producirá - - el título, que probablemente no sea lo que deseaba. Normalmente preferiría que el resultado fuese el título sin los guiones. Puede conseguir esto usando la siguiente sintaxis de plantilla:
{campo:|prefijo|sufijo}
Esta expresión de plantilla
dice que si campo
tiene el valor XXXX, el resultado será prefijoXXXXXsufijo. Si campo
está vacío (no tiene ningún valor) el resultado será un texto vacío (nada), porque el prefijo y el sufijo se ignoran. Tanto el prefijo como el sufijo pueden contener espacios.
No use subplantillas ({…}) o funciones (ver más adelante) en el prefijo o el sufijo.
Usando esta sintaxis, podemos resolver el problema anterior de los libros sin serie con esta plantilla:
{series}{series_index:| - | - }{title}
Los guiones se incluirán solamente si el libro tiene índice de serie, que sólo tendrá si tiene una serie. Continuando con el ejemplo de la serie «Foundation», la plantilla producirá Foundation - 1 - Second Foundation.
Notas:
Debe incluir los dos puntos tras el
nombre de búsqueda
si usa un prefijo o sufijo.Debe incluir ambos caracteres
|
o ninguno. No está permitido usar sólo uno como en{campo:| - }
.Es posible asignar un texto vació para el prefijo o sufijo, como en
{series:|| - }
. La plantilla{title:||}
es lo mismo que{title}
.
Formato
Supongamos que queremos asegurarnos de que series_index
aparezca con dígitos, con ceros a la izquierda. Se consigue de esta manera:
{series_index:0>3s}
- Tres dígitos con ceros a la izquierda
Para obtener ceros a la derecha use:
{series_index:0<3s}
- Tres dígitos con ceros a la derecha
Si usa índices de serie con decimales (por ejemplo 1.1), puede querer asegurarse de que los decimales queden alineados. Por ejemplo, puede que los índices 1 y 2.5 aparezcan como 01.00 y 02.50 para que se ordenen correctamente en un dispositivo que utilice un orden lexicográfico. Para ello, use:
{series_index:0>5.2f}
- Cinco caracteres que incluyen dos dígitos con cero a la izquierda, un punto decimal y dos dígitos tras el punto decimal.
Si quiere sólo las dos primeras letras de los datos, use:
{author_sort:.2}
- Sólo las primeras dos letras del orden de autor
Gran parte del formato del lenguaje de plantillas de calibre proviene de Python. Para obtener más detalles sobre la sintaxis de estas operaciones de formato avanzadas, vea la documentación de Python (en inglés).
Usar plantillas para definir columnas personalizadas¶
Las plantillas pueden usarse para mostrar información que no está en los metadatos de calibre o para mostrar los metadatos de una manera diferente al formato normal de calibre. Por ejemplo, puede querer mostrar el ISBN
, un campo que calibre no muestra. Puede conseguirlo creando una columna personalizadad de tipo columna generada a partir de otras columnas (en lo sucesivo llamadas columnas compuestas), e introduciendo una plantilla para generar el texto mostrado. La columna mostrará el resultado de evaluar la plantilla. Por ejemplo, para mostrar el ISBN, cree la columna e introduzca {identifiers:select(isbn)}
en el cuadro de plantilla. Para mostrar una columna que contenga los valores de dos campos personalizados de serie separados por una coma, use {#serie1:||,}{#serie2}
.
Las columnas compuestas pueden utilizar cualquier opción de plantilla, incluidas las de formato.
Nota: No puede modificar los datos mostrados en una columna compuesta, debe modificar las columnas de origen. Si modifica una columna compuesta, por ejemplo pulsando dos veces sobre ella, calibre abrirá la plantilla para modificarla, no los datos resultantes.
Plantillas y controles de metadatos¶
Los paneles de conexiones se usan para cambiar los metadatos escritos en los libros durante las operaciones de guardado en disco y de envío a dispositivo. Un panel de conexiones le permite especificar una plantilla para suministrar los datos que se escribirán en los metadatos del libro. Puede usar los paneles de conexiones para modificar los siguientes campos: authors, author_sort, language, publisher, tags, title, title_sort. Esa función es útil para los que quieren usar metadatos diferentes en los libros de los dispositivos, para solucionar problemas de ordenación o de visualización.
Cuando cree un panel de conexiones, especifique el formato y dispositivo para los que se usará. Hay un dispositivo especial save_to_disk
, que se usa para guardar formatos (en lugar de enviarlos a un dispositivo). Una vez que ha elegido el formato y dispositivo, elija los campos de metadatos para cambiar, y suministre plantillas para obtener los nuevos valores. Estas plantillas están conectadas con sus campos de destino, de ahí el nombre panel de conexiones. Por supuesto, puede usar columnas compuestas en estas plantillas.
Los paneles de conexiones son muy flexibles y pueden escribirse en modo de función única, en modo programación de plantillas, en modo de programación general o en modo de plantilla de Python.
Cuando un panel de conexiones pueda aplicarse (servidor de contenido, guardado en disco o envío a dispositivo), calibre busca los paneles definidos para elegir el correcto según el formato y dispositivo. Por ejemplo, para encontrar el panel de conexiones apropiado para enviar un libro EPUB a un dispositivo ANDROID, calibre busca en los paneles en el siguiente orden:
un panel de conexiones con una coincidencia exacta de formato y dispositivo, por ejemplo:
EPUB
yANDROID
un panel de conexiones con una coincidencia exacta de formato y el dispositivo especial
any device
, por ejemploEPUB
yany device
un panel de conexiones con el formato especial
any format
y una coincidencia exacta de dispositivo, por ejemplo:any format
yANDROID
un panel de conexiones con
any format
yany device
Los campos etiquetas y autores tienen un trato especial, debido a que ambos pueden tener más de un elemento. Un libro puede poseer varias etiquetas y varios autores. Cuando indique que desea cambiar uno de estos campos, la plantilla se examina para comprobar si hay más de un elemento. Para las etiquetas, el resultado se divide dondequiera que calibre encuentre una coma. Por ejemplo, si la plantilla produce el valor Intriga, Terror
, el resultado serán dos etiquetas: Intriga
y Terror
. No existe manera de poner una coma dentro de una etiqueta.
Lo mismo ocurre con los autores, pero usando un carácter diferente para el corte, un signo «&» en lugar de una coma. Por ejemplo, si la plantilla produce el valor Blogs, Joe&Posts, Susan
, el libro acabará con dos autores, Blogs, Joe
y Posts, Susan
. Si la plantilla produce el valor Blogs, Joe;Posts, Susan
, el libro tendrá un autor con un nombre peculiar.
Los paneles de conexiones afectan a los metadatos escritos en el libro cuando se guarda en disco o se escribe en un dispositivo. Los paneles de conexiones no afectan a los metadatos usados por las funciones Guardar en el disco Enviar al dispositivo para crear los nombres de archivo. En lugar de ello, los nombres de archivo se construyen usando las plantillas introducidas en la ventana de preferencias correspondiente.
Usar funciones en plantillas: modo de función única¶
Supongamos que desea mostrar el valor de un campo en mayúsculas, aunque normalmente el valor del campo tiene sólo las iniciales en mayúscula. Puede conseguir esto usando las funciones de plantilla. Por ejemplo, para mostrar el título en mayúsculas use la función uppercase
, como en {title:uppercase()}
. Para mostrarlo con las iniciales en mayúscula use {title:titlecase()}
.
Las funciones van en la parte del formato de la plantilla, después de :
y antes del primer |
o del }
de cierre si no se usa ningún prefijo o sufijo. Si tiene tanto un formato como una referencia de función, la función va después de otro :
. Las funciones devuelven el valor de la columna especificada en la plantilla, modificado de manera adecuada.
La sintaxis para usar funciones es una de:
{lookup_name:function(arguments)}
{lookup_name:format:function(arguments)}
{lookup_name:function(arguments)|prefix|suffix}
{lookup_name:format:function(arguments)|prefix|suffix}
Los nombres de función deben is siempre seguidos de paréntesis de apertura y cierre. Algunas funciones requieren valores adicionales (argumentos), y éstos van dentro de los paréntesis. Los argumentos se separan por comas. Una coma literal (como texto, no como separador de argumentos) debe ir precedida de una barra invertida (\
). El último (o único) argumento no puede contener un paréntesis de cierre textual.
Las funciones se evalúan antes de las especificaciones de formato y del prefijo y sufijo. Véase más abajo un ejemplo de cómo usar un formato y una función.
Importante: Si tiene experiencia en programación, tenga en cuenta que la sintaxis del modo de función única no es la que podría esperarse. Los textos van sin comillas y los espacios son importantes. Todos los argumentos se interpretan como constantes; no hay expresiones.
No use subplantillas ({…}) como argumentos de función. En su lugar, use el modo de programación de plantilla o el modo de programación general.
Notas sobre la ejecución de funciones en modo de función única:
Cuando se usan las funciones en modo de función única, el primer parámetro,
valor
, se sustituye automáticamente por el contenido del campo especificado en la plantilla. Por ejemplo, cuando se procesa la plantilla{title:capitalize()}
, el contenido del campotitle
se pasa como el parámetrovalor
a la funcióncapitalize
.En la documentación de funciones, la notación
[algo]*
significa quealgo
puede repetirse cero o más veces. La notación[algo]+
significa quealgo
se repite una o más veces (debe existir al menos una vez).Algunas funciones usan expresiones regulares. En el lenguaje de plantillas, las expresiones regulares no distinguen entre mayúsculas y minúsculas.
Las funciones están documentadas en Referencia de las funciones de plantilla. La documentación indica qué argumentos requieren las funciones y qué hacen. Por ejemplo, aquí está la documentación de la función ifempty.
ifempty(valor, texto_si_vacío)
– sivalor
no está vacío, devuelve dichovalor
, en caso contrario devuelvetexto_si_vacío
.
La función requiere dos argumentos, valor
y texto_si_vacío
. Sin embargo, debido a que estamos usando el modo de función única, omitimos el argumento valor
, pasando sólo texto_si_vacío
. Por ejemplo, esta plantilla:
{tags:ifempty(No tags on this book)}
muestra las etiquetas de un libro, si tiene alguna. Si no tiene etiquetas, muestra No tags on this book.
Las siguientes funciones se pueden usar en el modo de función única porque su primer parámetro es valor
.
capitalize
(valor)
– devuelvevalor
con la primera letra mayúscula y el resto en minúsculas.ceiling
(valor)
– devuelve el menor entero que es mayor o igual avalor
.cmp
(valor, y, mn, ig, my)
– comparavalor
ey
después de convertirlas en números.contains
(valor, patrón, texto_si_coincide, texto_si_no_coincide)
– comprueba sivalor
coincide con la expresión regularpatrón
.date_arithmetic
(valor, cálculo, fmt)
– calcula una nueva fecha a partir devalor
usandocálculo
.floor
(valor)
– devuelve el mayor entero que es menor o igual avalor
.format_date
(valor, texto_formato)
– da formato avalor
, que debe ser un campo de fecha, segúntexto_formato
y devuelve un texto.format_number
(valor, plantilla)
– interpretavalor
como un número y le da formato usando una plantilla de formato de Python como{0:5.2f}
o{0:,d}
o${0:5,.2f}
.fractional_part
(valor)
– devuelve la parte del valor tras el punto decimal.human_readable
(valor)
– espera quevalor
sea un número y devuelve un texto que representa ese número en KB, MB, GB, etc.ifempty
(valor, texto_si_vacío)
– sivalor
no está vacío, devuelve dichovalor
, en caso contrario devuelvetexto_si_vacío
.language_strings
(valor, localizar)
– devuelve los nombres de los idiomas identificados por los códigos de idioma (ver aquí los nombres y códigos) pasados comovalor
.list_contains
(valor, separador, [ patrón, encontrado, ]* no_encontrado)
– interpretavalor
como una lista de elementos separados porseparador
y compruebapatrón
para cada elemento de la lista.list_count
(valor, separador)
– interpreta el valor como una lista de elementos separados porseparador
y devuelve el número de elementos de la lista.list_count_matching
(valor, patrón, separador)
– interpretavalor
como una lista de elementos separados porseparador
y devuelve el número de elementos de la lista que coinciden con la expresión regularpatrón
.list_item
(valor, índice, separador)
– interpretavalor
como una lista de elementos separados poseparador
y devuelve el elemento númeroíndice
.list_sort
(valor, dirección, separador)
– devuelve la listavalor
ordenada lexicográficamente sin distinción de mayúsculas y minúsculas.lookup
(valor, [ patrón, clave, ]* clave_alternativa)
– Cadapatrón
se compara convalor
en orden.lowercase
(valor)
– devuelvevalor
en minúsculas.mod
(valor, y)
– devuelvefloor
del resto devalor / y
.rating_to_stars
(valor, usar_media_estrella)
– Devuelvevalor
como una serie de caracteres de estrella (★
).re
(valor, patrón, sustitución)
– devuelvevalor
después de aplicar la expresión regular.re_group
(valor, patrón [, plantilla_para_grupo]*)
– devuelve un texto formado por aplicación de la expresión regularpatrón
avalor
, sustituyendo cada coincidenciaround
(valor)
– devuelve el entero más cercano avalor
.select
(valor, clave)
– interpretavalor
como una lista de elementos separados por comas, con cada elemento de la formaid:valor_id
(el formato de la columnaidentifier
de calibre).shorten
(valor, car_izq, texto_medio, car_der)
– devuelve una versión abreviadavalor
str_in_list
(valor, separador, [ texto, encontrado, ]+ no_encontrado)
– interpretavalor
como una lista de elementos separados porseparador
, y comparatexto
con cada valor de la lista.subitems
(valor, índice_inicio, índice_fin)
– Esta función separa listas de elementos jerárquicos de tipo etiqueta, tales como los géneros.sublist
(valor, índice_inicio, índice_fin, separador)
– interpretavalor
como una lista de elementos separados porseparador
y devuelve una nueva lista con los elementos comprendidos entre la posicióníndice_inicio
eíndice_fin
.substr
(valor, inicio, fin)
– devuelve los caracteres entre la posicióninicio
yfin
devalor
.swap_around_articles
(valor, separador)
– devuelvevalor
con los artículos puestos al final.swap_around_comma
(valor)
– dado unvalor
de la formaB, A
, devuelveA B
.switch
(valor, [patrónN, valorN,]+ otro_valor)
– para cada parejapatrónN, valorN
, comprueba sivalor
coincide con la expresión regularpatrónN
test
(valor, texto_si_no_vacío, texto_si_vacío)
– devuelvetexto_si_no_vacío
sivalor
no está vacío, devuelvetexto_si_vacío
en caso contrario.titlecase
(valor)
– devuelvevalor
con las iniciales en mayúscula.transliterate
(valor)
– Devuelve un texto en el alfabeto latino formado por aproximación del sonido de las palabras envalor
.uppercase
(valor)
– devuelvevalor
en mayúsculas.
Usar funciones y formato en la misma plantilla
Supongamos que tiene una columna personalizada con números enteros #myint
, que quiere mostrar con ceros a la izquierda, como en 003
. Una posibilidad es usa como formato 0>3s
. Sin embargo, de manera predeterminada, si un número (entero o decimal) es igual a cero, el valor se muestra como un texto vacío, así que el valor cero producirá un texto vacío, no 000
. Si quiere ver los valores como 000
, debe usar tanto el texto de formato como la función ifempty
para cambiar el valor vacío a cero de nuevo, la plantilla sería:
{#myint:0>3s:ifempty(0)}
Tenga en cuenta que puede usar también prefijo y sufijo. Si desea que el número aparezca como [003]
o [000]
, use la plantilla:
{#myint:0>3s:ifempty(0)|[|]}
Modo de programación general¶
El Modo de programación general (MPG) sustituye las expresiones de plantilla por un programa escrito en el lenguaje de plantillas. La sintaxis del lenguaje está definida por la siguiente gramática:
program ::= 'program:' expression_list
expression_list ::= top_expression [ ';' top_expression ]*
top_expression ::= or_expression
or_expression ::= and_expression [ '||' and_expression ]*
and_expression ::= not_expression [ '&&' not_expression ]*
not_expression ::= [ '!' not_expression ]* | concatenate_expr
concatenate_expr::= compare_expr [ '&' compare_expr ]*
compare_expr ::= add_sub_expr [ compare_op add_sub_expr ]
compare_op ::= '==' | '!=' | '>=' | '>' | '<=' | '<' |
'in' | 'inlist' | 'inlist_field' |
'==#' | '!=#' | '>=#' | '>#' | '<=#' | '<#'
add_sub_expr ::= times_div_expr [ add_sub_op times_div_expr ]*
add_sub_op ::= '+' | '-'
times_div_expr ::= unary_op_expr [ times_div_op unary_op_expr ]*
times_div_op ::= '*' | '/'
unary_op_expr ::= [ add_sub_op unary_op_expr ]* | expression
expression ::= identifier | constant | function | assignment | field_reference |
if_expr | for_expr | break_expr | continue_expr |
'(' expression_list ')' | function_def
field_reference ::= '$' [ '$' ] [ '#' ] identifier
identifier ::= id_start [ id_rest ]*
id_start ::= letter | underscore
id_rest ::= id_start | digit
constant ::= " string " | ' string ' | number
function ::= identifier '(' expression_list [ ',' expression_list ]* ')'
function_def ::= 'def' identifier '(' top_expression [ ',' top_expression ]* ')' ':'
expression_list 'fed'
assignment ::= identifier '=' top_expression
if_expr ::= 'if' condition 'then' expression_list
[ elif_expr ] [ 'else' expression_list ] 'fi'
condition ::= top_expression
elif_expr ::= 'elif' condition 'then' expression_list elif_expr | ''
for_expr ::= for_list | for_range
for_list ::= 'for' identifier 'in' list_expr
[ 'separator' separator_expr ] ':' expression_list 'rof'
for_range ::= 'for' identifier 'in' range_expr ':' expression_list 'rof'
range_expr ::= 'range' '(' [ start_expr ',' ] stop_expr
[ ',' step_expr [ ',' limit_expr ] ] ')'
list_expr ::= top_expression
break_expr ::= 'break'
continue_expr ::= 'continue'
separator_expr ::= top_expression
start_expr ::= top_expression
stop_expr ::= top_expression
step_expr ::= top_expression
limit_expr ::= top_expression
Notas:
una
top_expression
siempre tiene un valor. El valor de unaexpression_list
es el valor de la últimatop_expression
de la lista. Por ejemplo, el valor de laexpression_list
1;2;'blabla';3
es3
.En un contexto lógico, cualquier valor que no sea vacío es
True
(verdadero)En un contexto lógico, el valor vacío es
False
(falso)Textos y números pueden usarse indistintamente. Por ejemplo,
10
y'10'
son lo mismo.Los comentarios son líneas que empiezan por un carácter
#
. No se permiten comentarios que empiecen a mitad de línea.
Prioridad de operadores
La prioridad de los operadores (orden de evaluación) de mayor (se evalúa primero) a menor (se evalúa el último) es:
Llamadas a función, constantes, expresiones entre paréntesis, expresiones de declaración, expresiones de asignación, referencias de campo.
Más (
+
) y menos (-
) unarios. Estos operadores se evalúan de derecha a izquierda.Estas y todas las otras operaciones aritméticas devuelven enteros si la expresión resulta en una parte decimal igual a cero. Por ejemplo, si la expresión devuelve
3.0
se cambia a3
.Multiplicación (
*
) y división (/
). Estas operaciones son asociativas y se evalúan de izquierda a derecha. Use paréntesis si quiere cambiar el orden de evaluación.Suma (
+
) y resta (-
). Estas operaciones son asociativas y se evalúan de izquierda a derecha.Comparaciones numéricas y de textos. Estas operaciones devuelven
'1'
si la comparación tiene éxito, en caso contrario un texto vacío (''
). Las comparaciones no son asociativas:a < b < c
es un error de sintaxis.Concatenación de textos (
&
). El operador&
devuelve un texto formado por concatenación de las expresiones a su izquierda y derecha. Ejemplo:'aaa' & 'bbb'
devuelve'aaabbb'
. El operador es asociativo y se evalúa de izquierda a derecha.Negación lógica unaria (
!
). Esta operación devuelve'1'
si la expresión esFalse
(da como resultado un texto vacío), en caso contrario''
.Y lógico (
&&
). Esta operación devuelve'1'
si ambas expresiones a derecha e izquierda sonTrue
, o un texto vacío (''
) si alguna esFalse
. Es asociativa, se evalúa de izquierda a derecha y con cortocircuito https://chortle.ccsu.edu/java5/Notes/chap40/ch40_2.html.O lógico (
||
). Esta operación devuelve'1'
si alguna de las expresiones a derecha e izquierda esTrue
, o un''
si ambas sonFalse
. Es asociativa, se evalúa de izquierda a derecha y con cortocircuito https://chortle.ccsu.edu/java5/Notes/chap40/ch40_8.html.
Referencias de campo
Un elemento de tipo field_reference
(referencia de campo) se evalúa como el valor del campo de metadatos nombrado por el nombre de búsqueda que sigue al $
o $$
. Usar $
es equivalente a usar la función field. Usar $$
es equivalente a usar la función raw_field. Ejemplos:
* $authors ==> field('authors')
* $#genre ==> field('#genre')
* $$pubdate ==> raw_field('pubdate')
* $$#my_int ==> raw_field('#my_int')
Expresiones condicionales
Las expresiones condicionales (if_expression
) evalúan primero la condition
. Si la condition
es True
(un valor no vacío) entonces se evalúa expression_list
en la sentencia then
. Si es False
, entonces se evalúa expression_list
en la sentencia elif
o else
, si existen. Las partes elif
y else
son opcionales. Las palabras if
, then
, elif
, else
y fi
están reservadas, no pueden usarse como nombres de identificador. Se pueden añadir espacios y saltos de línea donde sea conveniente. La condition
es una top_expression
, no usa expression_list
, no se permite punto y coma. Los elementos de tipo expression_list
son secuencias de top_expression
separadas por punto y coma. Una expresión condicional devuelve el resultado de la última top_expression
en la expression_list
evaluada, o un texto vacío si no se evaluó ninguna expression_list
.
Ejemplos:
* program: if field('series') then 'yes' else 'no' fi
* program:
if field('series') then
a = 'yes';
b = 'no'
else
a = 'no';
b = 'yes'
fi;
strcat(a, '-', b)
Ejemplo de if
anidado:
program:
if field('series') then
if check_yes_no(field('#mybool'), '', '', '1') then
'yes'
else
'no'
fi
else
'no series'
fi
Como se ha mencionado, un if
produce un valor. Esto significa que todas las siguientes expresiones son equivalentes:
* program: if field('series') then 'foo' else 'bar' fi
* program: if field('series') then a = 'foo' else a = 'bar' fi; a
* program: a = if field('series') then 'foo' else 'bar' fi; a
Por ejemplo, este programa devuelve el valor de la columna series
si el libro tiene una serie, de lo contrario, el valor de la columna title
:
program: field(if field('series') then 'series' else 'title' fi)
Expresiones de bucle
Una expresión de bucle (for_expression
) itera sobre una lista de valores, procesando uno cada vez. La list_expression
debe resultar en el nombre de búsqueda
de un campo de metadatos, como tags
o #genero
, o una lista de valores. La función range genera una lista de números. Si el resultado es un nombre de búsqueda
válido, se obtiene el valor del campo y se usa el separador especificado para ese tipo de campo. Si el resultado no es un nombre de búsqueda
válido, se supone que es una lista de valores. La lista se supone separada por comas, a no ser que use la palabra clave opcional separator
, en ese caso los valores de la lista deben estar separados por el resultado de evaluar separator_expr
. No se puede usar un separador si la lista es generada por range()
. Cada valor de la lista se asigna a la variable especificada y luego se evalúa expression_list
. Puede usar break
para salir del bucle y continue
para comenzar con la siguiente iteración.
Ejemplo: Esta plantilla elimina el primer nombre jerárquico para cada valor de Género (#genre`
), construyendo una lista con los nuevos nombres.
program:
new_tags = '';
for i in '#genre':
j = re(i, '^.*?\.(.*)$', '\1');
new_tags = list_union(new_tags, j, ',')
rof;
new_tags
Si el Género original es Historia.Militar, Ciencia ficción.Historia alternativa, Léeme, la plantilla devolverá Militar, Historia alternativa, Léeme. Puede usar esta plantilla en Modificar metadatos en masa > Buscar y sustituir con Buscar establecido en plantilla
para eliminar el primer nivel de la jerarquía y asignar el valor resultante a Género.
Nota: la última línea en la plantilla, new_tags
, no es estrictamente necesaria en este caso, porque for
devuelve el valor de la última top_expression
en expression_list
. El valor de una asignación (assignment
) es el valor de su expresión, así que el valor del for
es lo que se asignó a new_tags
.
Definición de funciones
Si tiene código repetido en una plantilla, puede poner ese código en una función local. La palabra clave def
comienza la definición. Le sigue el nombre de la función, la lista de argumentos y el código en la función. La definición de función termina con la palabra clave fed
.
Los argumentos son posicionales. Cuando se invoca una función, los argumentos proporcionados se hacen corresponder de izquierda a derecha con los parámetros definidos, con el valor del argumento asignado al parámetro. Es un error proporcionar más argumentos que parámetros definidos. Los parámetros pueden tener valores predeterminados, como a = 25
. Si no se proporciona un argumento para un parámetro, se usará el valor predeterminado, en caso contrario el parámetro será un texto vacío.
La instrucción return
se puede usar en una función local.
Una función debe definirse antes de poder usarse.
Ejemplo: Esta plantilla calcula una duración aproximada en años, meses y días a partir de un número de días. La función to_plural()
da formato a los valores calculados. Tenga en cuenta que el ejemplo también usa el operador &
:
program:
days = 2112;
years = floor(days/360);
months = floor(mod(days, 360)/30);
days = days - ((years*360) + (months * 30));
def to_plural(v, str):
if v == 0 then return '' fi;
return v & ' ' & (if v == 1 then str else str & 's' fi) & ' '
fed;
to_plural(years, 'year') & to_plural(months, 'month') & to_plural(days,'day')
Operadores de relación
Los operadores de relación devuelven '1'
si la comparación es cierta, en caso contrario devuelven un texto vacío (''
).
Hay dos formas de operadores de relación: comparaciones de texto y comparaciones numéricas.
Las comparaciones de texto usan el orden lexicográfico y no distinguen mayúsculas y minúsculas. Las comparaciones de texto permitidas son ==
, !=
, <
, <=
, >
, >=
, in
, inlist
e inlist_field
. Para el operador in
, el resultado de la expresión de la izquierda se interpreta como un patrón de expresión regular. El operador in
es True
si el valor de la expresión regular de la izquierda coincide con el valor de la expresión de la derecha.
El operador inlist
es True
si la expresión regular de la izquierda coincide con alguno de los elementos de la lista de la derecha, en la que los elementos están separados por comas. El operador inlist_field
es True
si la expresión regular de la izquierda coincide con alguno de los elementos del campo (columna) nombrado por la expresión de la derecha, usando el separador definido para el campo. Nota: el operador inlist_field
requiere que la expresión de la derecha resulte en un nombre de campo, mientras que el operador inlist
requiere que la expresión de la derecha resulte en un texto que contenga una lista separada por comas. Debido a esta diferencia, inlist_field
es bastante más rápido que inlist
porque no implica conversiones de texto o construcciones de listas. Las expresiones regulares no distinguen mayúsculas y minúsculas.
Los operadores de comparación numérica son ==#
, !=#
, <#
, <=#
, >#
y >=#
. Las expresiones de la izquierda y la derecha deben resultar en valores numéricos con dos excepciones: Tanto el valor textual None
(campo no definido) como el texto vacío equivalen al valor cero.
Ejemplos:
program: field('series') == 'lala'
devuelve'1'
si la serie del libro es lala, en caso contrario devuelve''
.
program: 'l.a' in field('series')
devuelve'1'
si la serie del libro coincide con la expresión regularl..a
(por ejemplo, lala, El asesino de reyes, etc.), en caso contrario devuelve''
.
program: 'ciencia' inlist $#genero` devuelve ``'1'
si alguno de los valores obtenidos de los géneros del libro coincide con la expresión regularciencia
(por ejemplo Ciencia, Historia de la ciencia, Ciencia ficción, etc.), en caso contrario devuelve''
.
program: '^ciencia$' inlist $#genero
devuelve'1'
si alguno de los géneros del libro coincide con la expresión regular^ciencia$
(por ejemplo Ciencia). Los géneros Historia de la ciencia y Ciencia ficción no coinciden.
program: 'asimov' inlist $authors
devuelve'1'
si algún autor coincide con la expresión regularasimov
(por ejemplo Asimov, Isaac o Isaac Asimov), en caso contrario devuelve''
.
program: 'asimov' inlist_field 'authors'
devuelve'1'
si algún autor coincide con la expresión regularasimov
(por ejemplo Asimov, Isaac o Isaac Asimov), en caso contrario devuelve''
.
program: 'asimov$' inlist_field 'authors'
devuelve'1'
si algún autor coincide con la expresión regularasimov$
(por ejemplo Isaac Asimov), en caso contrario devuelve''
. No coincide con Asimov, Isaac debido al punto de anclaje$
en la expresión regular.
program: if field('series') != 'lala' then 'nana' else 'blabla' fi
devuelve'nana'
si la serie del libro no es lala, en caso contrario devuelve'blabla'
.
program: if field('series') == 'lala' || field('series') == '1632' then 'sí' else 'no' fi
devuelve'sí'
si la serie es lala o 1632, en caso contrario devuelve'no'
.
program: if '^(lala|1632)$' in field('series') then 'sí' else 'no' fi
devuelve'sí'
si la serie es lala o 1632, en caso contrario devuelve'no'
.
program: if 11 > 2 then 'sí' else 'no' fi
devuelve'no'
porque el operador>
realiza una comparación lexicográfica.
program: if 11 ># 2 then 'sí' else 'no' fi
devuelve'sí'
porque el operador>#
realiza una comparación numérica.
Funciones en modo de programación general
Consulte Referencia de las funciones de plantilla para ver la lista de funciones predefinidas en el lenguaje de plantillas.
Notas:
A diferencia del modo de función única, en el modo de programación general debe especificar el primer parámetro
valor
.Todos los parámetros son elementos de tipo
expression_list
(ver la gramática más arriba).
Programas más complejos en expresiones de plantilla: modo de programación de plantillas¶
El modo de programación de plantillas (MPP) es una mezcla entre el modo de programación general y el modo de función única. El MPP se diferencia del modo de función única en que permite escribir expresiones de plantilla que hacen referencia a otros campos de metadatos, usar funciones anidadas, modificar variables y hacer operaciones aritméticas. Se diferencia del modo de programación general en que la plantilla está entre caracteres {
y }
y no empieza por la palabra program
. La parte del programa de la plantilla es una expression_list
del modo de programación general.
Un ejemplo: supongamos que quiere una plantilla que muestre la serie de un libro si la tiene, y en caso contrario muestre el valor del campo personalizado #genre
. Esto no se puede hacer en el modo de función única, porque no se puede hacer referencia a otro campo en la expresión de la plantilla. En el MPP sí se puede. La siguiente expresión lo demuestra:
{series_index:0>7.1f:'ifempty($, -5)'}
El ejemplo muestra varias cosas:
El MPP se usa si la expresión empieza por
:'
y termina en'}
. Cualquier otra cosa se supone que corresponde al modo de función única.Si la plantilla contiene un prefijo y un sufijo, la expresión termina con
'|
donde|
es el delimitador para el prefijo. Ejemplo:{series_index:0>7.1f:'ifempty($, -5)'|prefix | suffix}
Las funciones deben llevar todos sus argumentos. Por ejemplo, las funciones estándar predefinidas deben tener el parámetro inicial
valor
.La variable
$
se puede usar como el argumentovalor
y representa el valor del campo nombrado en la plantilla,series_index
en este caso.los espacios en blanco se ignoran y se pueden utilizar en cualquier lugar dentro de la expresión.
los textos constantes se encierran en comillas del mismo tipo, ya sea
'
o"
.
En el MPP, usar los caracteres {
y }
en textos literales puede conducir a errores o resultados inesperados porque confunden al procesador de plantillas. Éste intenta interpretarlos como límites de expresiones de plantilla y no como caracteres. En algunos casos, pero no todos, puede sustituir un {
por [[
y un }
por ]]
. En general, si un program contiene caracteres {
y }
, debería usar el modo de programación general.
Modo de plantilla de Python¶
El modo de plantilla de Python (MPPy) le permite escribir plantillas usando el lenguaje Python nativo y la interfaz de programación de calibre. La interfaz de programación de base de datos será la más útil, pero en este manual no se discutirán más detalles. Las plantillas MPPy son más rápidas y pueden realizar operaciones más complicadas, pero debe saber escribir código Python usando la interfaz de programación de calibre.
Una plantilla MPPy empieza por:
python:
def evaluate(book, context):
# book is a calibre metadata object
# context is an instance of calibre.utils.formatter.PythonTemplateContext,
# which currently contains the following attributes:
# db: a calibre legacy database object.
# globals: the template global variable dictionary.
# arguments: is a list of arguments if the template is called by a GPM template, otherwise None.
# funcs: used to call Built-in/User functions and Stored GPM/Python templates.
# Example: context.funcs.list_re_group()
# your Python code goes here
return 'a string'
Puede añadir el texto anterior a la plantilla usando el menú contextual, al que normalmente se accede pulsando el botón derecho. Los comentarios no son importantes y pueden eliminarse. Debe usar la sangría de Python.
El objeto de contexto admite str(context)
, que devuelve un texto con el contenido del contexto y context.attributes
, que devuelve una lista con los nombres de los atributos del contexto.
El atributo context.funcs
le permite invocar funciones incorporadas y de usuario, así como plantillas guardadas MPG o Python, de manera que puede ejecutarlas directamente en el código. Las funciones se identifican por sus nombres. Si el nombre entra en conflicto con una palabra clave de Python, añada un guión bajo al final. Ejemplos:
context.funcs.list_re_group()
context.funcs.assert_()
He aquí un ejemplo de una plantilla MPPy que genera una lista de todos los autores de una serie. La lista se guarda en una Columna generada a partir de otras columnas, funciona como etiquetas. Se muestra en los detalles del libro y tiene la opción en líneas separadas activada (en Preferencias > Apariencia > Detalles del libro). Esta opción requiere que la lista esté separada por comas. Para satisfacer este requisito la plantilla convierte las comas en los nombres de autor a punto y coma y luego construye una lista de autores separados por comas. Los autores se ordenan, razón por la cual la plantilla usa author_sort.
python:
def evaluate(book, context):
if book.series is None:
return ''
db = context.db.new_api
ans = set()
# 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))
La salida en los detalles del libro tiene este aspecto:
Plantillas guardadas¶
El modo de programación general y el modo de plantilla de Python permiten guardar plantillas e invocarlas desde otra plantilla, casi como invocar funciones guardadas. Puede guardar plantillas en Preferencias > Avanzado > Funciones de plantilla. Hay más información en el diálogo correspondiente. Puede invocar una plantilla de la misma manera que una función, pasando argumentos por posición si lo desea. Un argumento puede ser una expresión. Ejemplos para invocar una plantilla, suponiendo que la plantilla guardada se llame foo
:
lala()
– invoca la plantilla sin pasar argumentos.lala(a, b)
– invoca la plantilla pasando los valores de las variablesa
yb
.lala(if field('series') then field('series_index') else 0 fi)
– si el libro tieneseries
, pasa el valor deseries_index
, en caso contrario pasa el valor0
.
En el MPG, puede obtener el valor de los argumentos pasados en la invocación de la plantilla guardada usando la función arguments
. Esta función declara e inicializa las variables locales, parámetros en la práctica. Las variables son posicionales: obtienen el valor del argumento usado en la misma posición en la invocación. Si el argumento correspondiente no aparece en la invocación, arguments
asigna a esa variable el valor predeterminado suministrado. Si no hay valor predeterminado, será un texto vacío. Por ejemplo, la siguiente función arguments
declara dos variables: key
y ``alternate`:
arguments(key, alternate='series')
Ejemplos, suponiendo de nuevo que la plantilla guardada se llame lala
:
lala('#miserie')
– a la variablekey
se le asigna el valor'#miserie'
y a la variablealternate
se le asigna el valor predeterminado'serie'
.lala('series', '#genero')
a la variablekey
se le asigna el valor'series'
y a la variablealternate
se le asigna el valor'#genero'
.lala()
– a la variablekey
se le asigna un texto vacío y a la variablealternate
se le asigna el valor predeterminado'series'
.
En el MPPy los argumentos se pasan en el parámetro arguments
, que es una lista de textos. No existe la posibilidad de especificar valores predeterminados. Debe comprobar la longitud de la lista arguments
para asegurarse de que el número de argumentos es el esperado.
Una buena manera de probar las plantillas guardadas es usar el diálogo Prueba de plantillas. Para facilitar su acceso, asígnele un atajo de teclado en Preferencias > Avanzado > Atajos > Prueba de plantillas. Si le asigna un atajo de teclado podrá alternar rápidamente entre la prueba de plantillas y la modificación del código de programa de la plantilla.
Pasar información adicional a las plantillas¶
Un desarrollador puede decidir pasar información adicional al procesador de plantillas, como metadatos del libro específicos para una aplicación o información sobre lo que se quiere que haga el procesador. Una plantilla puede acceder a esta información y usarla durante la evaluación.
Desarrollador: cómo pasar información adicional
La información adicional es un diccionario Python que contiene pares nombre_de_variable: valor de variable
donde los valores deberían ser textos. La plantilla puede acceder al diccionario, creando variables locales con nombre nombre_de_variable
y valor valor de variable
. El usuario no puede cambiar el nombre, así que lo mejor es usar nombres que no entren en conflicto con otras variables locales, por ejemplo añadiendo un guión bajo al principio del nombre.
El diccionario se pasa al procesador de plantillas (el formatter
) usando el argumento global_vars=diccionario
. La forma completa del método es:
def safe_format(self, fmt, kwargs, error_value, book,
column_name=None, template_cache=None,
strip_results=True, template_functions=None,
global_vars={})
Creador de plantillas: cómo acceder a la información adicional
Puede acceder a la información adicional (el diccionario globals
) en una plantilla usando la función de plantilla:
globals(id[=expression] [, id[=expression]]*)
donde id
es cualquier nombre de variable permitido. Esta función comprueba si la información adicional prevista por el desarrollador contiene el nombre. Si es así, la función asigna el valor a una variable local con ese nombre. Si el nombre no está presente en la información adicional y se suministra una expresión
, se evalua dicha expresión
y el resultado se asigna a la variable local. Si no existe ni un valor ni una expresión, la función asigna el texto vacío (''
) a la variable local.
Una plantilla puede establecer un valor en el diccionario globals
usando la función de plantilla:
set_globals(id[=expression] [, id[=expression]]*)
Esta función asigna la pareja id:valor
del diccionario globals
, donde valor
es el valor de la variable local de la plantilla id
. Si la variable no existe, valor
se toma como el resultado de evaluar expresión
.
Notas sobre las diferencias entre modos¶
Los tres modos de programación, el modo de función única (MFU), el modo de programación de plantillas (MPP) y el modo de programación general (MPG) funcionan de distinta manera. MFU pretende ser «sencillo» y oculta muchos elementos del lenguaje de programación.
Diferencias:
En el MFU el valor de la columna se pasa siempre como un primer argumento «invisible» a la función que se incluye en la plantilla.
El MFU no hace distinciones entre variables y textos; todos los valores son textos.
La siguiente plantilla MFU devuelve el nombre de la serie o el texto
'no series'
:{series:ifempty(no series)}
La plantilla equivalente en MPP es:
{series:'ifempty($, 'no series')'}
La plantilla equivalente en MPG es:
program: ifempty(field('series'), 'no series')
El primer argumento de
ifempty
es el valor del camposeries
. El segundo argumento es el textono series
. En MFU el primer argumento, el valor, se pasa automáticamente (es el argumento invisible).Varias funciones de plantilla, como
booksize()
ycurrent_library_name()
, no admiten argumentos. Debido al «argumento invisible», no puede usar estas funciones en MFU.Las funciones anidadas, en las que una función invoca a otra función para evaluar un argumento, no pueden usarse en MFU. Por ejemplo, esta plantilla, que pretende devolver los 5 primeros caracteres del valor de la serie convertidos en mayúsculas, no funcionará en MFU:
{series:uppercase(substr(0,5))}
MPP y MPG permiten usar funciones anidadas. La plantilla anterior en MPP sería:
{series:'uppercase(substr($, 0,5))'}
En MPG sería:
program: uppercase(substr(field('series'), 0,5))
Como se mencionó ateriormente en la sección modo de programación de plantillas, en el MPP, usar los caracteres
{
y}
en textos literales puede conducir a errores o resultados inesperados porque confunden al procesador de plantillas. Éste intenta interpretarlos como límites de plantilla y no como caracteres. En algunos casos, pero no todos, puede sustituir un{
por[[
y un}
por]]
. En general, si un program contiene caracteres{
y}
, debería usar el modo de programación general.
Funciones de plantilla Python definidas por el usuario¶
Puede añadir funciones propias al procesador de plantillas. Dichas funciones pueden usarse en cualquiera de los tres modos de programación de plantillas. Las funciones se añaden en Preferencias > Avanzado > Funciones de plantilla. Las instrucciones se muestran en el correspondiente cuadro de diálogo. Tenga en cuenta que puede usar plantillas de Python para lo mismo. Dado que es más rápido ejecutar funciones definidas por el usuario que una plantilla de Python, las funciones definidas por el usuario pueden ser más eficientes, dependiendo de la complejidad de lo que haga la función o plantilla.
Notas especiales para plantillas de guardado o envío¶
Cuando una plantilla se usa como plantilla de «guardado a disco» o de «envío a dispositivo», ocurre un procesado especial. Los valores de los campos se sanean, eliminando caracteres especiales para los sistemas operativos por guiones bajos, incluyendo barras. Esto significa que el texto de los campos no puede usarse para crear carpetas. Sin embargo, las barras no se modifican en los textos de prefijo o sufijo, por lo que las barras en estos textos harán que se creen carpetas. Gracias a esto, es posible crear estructuras de carpetas de profundidad variable.
Por ejemplo, supongamos que quiere una estructura de carpetas serie/índice de serie - título, con la salvedad de que si la serie no existe el título debe estar en la carpeta superior. La plantilla para conseguir esto es:
{series:||/}{series_index:|| - }{title}
La barra y el guión sólo aparecen si la serie no está vacía.
La función lookup()
nos permite realizar un procesado aún más complejo. Por ejemplo, supongamos que si un libro tiene una serie, entonces queremos una estructura de carpetas serie/índice de serie - título. Si el libro no tiene una serie, entonces queremos la estructura género/orden de autor/título. Si el libro no tiene género, queremos que use «Desconocido». Queremos seguir dos caminos completamente distintos según el valor de la serie.
Para lograr esto:
Creamos un campo compuesto (démosle el nombre de consulta #aa) que contiene
{series}/{series_index} - {title}
. Si la serie no está vacía, esta plantilla produce serie/número_de_serie - título.Creamos un campo compuesto (démosle el nombre de consulta #bb) que contenga
{#genre:ifempty(Desconocido)}/{author_sort}/{title}
. Esta plantilla produce género/orden de autor/título, donde un género vacío se sustituye por Desconocido.Establecemos la plantilla de guardado en
{series:lookup(.,#aa,#bb)}
. Esta plantilla elige el campo compuesto#aa
si la serie no está vacía y el campo compuesto#bb
si la serie está vacía. Obtenemos por lo tanto dos rutas de guardado completamente diferentes según el campo series esté o no vacío.
Consejos¶
Use la prueba de plantillas para probar plantillas. Añada esta función al menú contextual para libros en lea biblioteca o asígnele un atajo de teclado.
Las plantillas pueden usar otras plantillas haciendo referencia a columnas compuestas construidas con la plantilla deseada. También puede usar plantillas guardadas.
En un panel de conexiones, puede establecer un campo como vacío (o lo que sea equivalente a vacío) utilizando la plantilla especial
{}
. Esta plantilla siempre producirá un texto vacío.La técnica descrita anteriormente para mostrar los números incluso si son cero funciona con el campo estándar
series_index
.
Referencia de las funciones de plantilla¶
- Reference for all built-in template language functions
- Aritméticas
- Cambio de mayúsculas y minúsculas
- Consulta de listas
- Formato de valores
- Funciones de base de datos
- Funciones de fecha
- Iteración sobre valores
- Lógicas
- Manipulación de listas
- Manipulación de textos
- Obtención de valores de metadatos
- annotation_count
- approximate_formats
- author_links
- author_sorts
- booksize
- connected_device_name
- connected_device_uuid
- current_library_name
- current_library_path
- current_virtual_library_name
- field
- formats_modtimes
- formats_paths
- formats_sizes
- has_cover
- is_marked
- language_codes
- language_strings
- ondevice
- raw_field
- raw_list
- series_sort
- user_categories
- virtual_libraries
- Otros
- Recursivas
- Relacionales
- API of the Metadata objects