darktable viene con una versátil interfaz de secuencia de comandos para mejoras en sus funcionalidades.
Lua puede ser utilizado para definir las acciones que darktable realizará, cuando un evento específico sea desencadenado. Un ejemplo puede ser una llamada a una aplicación externa durante la exportación de un archivo para aplicar pasos adicionales de procesamiento fuera de darktable.
darktable utiliza Lua, el cual es un proyecto independiente fundado en 1993, quien provee un lenguaje de programación poderoso, rápido, ligero e integrado. Lua es ampliamente utilizado en muchas aplicaciones de código abierto, en programas comerciales y por programadores de videojuegos.
darktable uses Lua version 5.3. Describing the principles and syntax of Lua is beyond the scope of this usermanual. For a detailed introduction see the Lua reference manual.
Al inicio, darktable correrá dos scripts de Lua automáticamente.
un script llamado luarc en $DARKTABLE/share/darktable
un script llamado luarc en el directorio de configuración del usuario
$DARKTABLE es utilizado aquí para representar el directorio de su sistema de instalación de darktable
Esta es la única vez que darktable correrá un script de Lua por si mismo. El script puede registrar callbacks para realizar acciones sobre varios eventos de darktable. Este mecanismo de llamadas es la forma principal de activar las acciones lua.
Comencemos con un ejemplo simple. Imprimiremos algunos códigos en la consola. Cree un archivo llamado luarc en el directorio de configuración de darktable (usualmente ~/.config/darktable/) y agregue la siguiente línea:
print("Hola Mundo !")
Start darktable and you will see the sentence Hello World ! printed on the console. Nothing fancy but it's a start...
En este punto, no hay nada específico para darktable en el script. Simplemente utilizamos una función de print
para imprimir una cadena. Eso está bien y todo, pero podemos hacer algo mejor que eso. Para acceder a la API de darktable
primero necesita requerirlo
y almacenar el objeto regresado en una variable. Una vez realice esto podrá acceder a la API de darktable como un sub-campo
del objeto retornado. Todo esto está documentado en el manual de referencias del API de Lua (vea el Sección 9.2, “API Lua”).
local darktable = require "darktable" darktable.print_log("Hello World !")
Run the script ... and nothing happens. The function
darktable.print_log
is just like print but will only print the message if you have enabled lua traces with
-d lua on the command line. This is the recommended way to do traces
in a darktable lua script.
Este primer ejemplo nos muestra lo básico de lua y nos permite verificar que todo funcione propiamente. Hagamos algo un poco más complejo. Intentemos imprimir la lista de imágenes que tienen una etiqueta roja adjunta a ellas. Pero primero, ¿Qué es una imagen?
local darktable = require "darktable" local debug = require "darktable.debug" print(darktable.debug.dump(darktable.database[1]))
Corriendo el código anterior se producirá una gran salida. Ya la veremos en un momento, pero primero, demos un vistazo al código en sí.
Conocemos los requerimientos de darktable
. Aquí, necesitamos requerir por separado darktable.debug
la cual es una sección opcional de la API que provee funciones de ayuda para depurar los scripts lua.
darktable.database
es una tabla provista por la API que contiene todas las imágenes en la base de datos (visible o no, duplicada o no...). Cada
entrada en la base de datos es un objeto de imagen. Los objetos de imágenes son objetos complejos que le permiten manipular
su imagen de varias formas (todo esto está documentado en la sección types_dt_lua_image_t
del manual de la API). Para mostrar nuestras imágenes, utilizamos darktable.debug.dump
la cual es una función que tomará todo lo que sea un parámetro y mostrará recursivamente su contenido. Ya que las imágenes
son objetos complejos que hacen referencia indirecta a otros objetos complejos, la salida resultante es inmensa. Debajo encontrará
un ejemplo seccionado de la salida.
toplevel (userdata,dt_lua_image_t) : /images/100.JPG
publisher (string) : ""
path (string) : "/images"
move (function)
exif_aperture (number) : 2.7999999523163
rights (string) : ""
make_group_leader (function)
exif_crop (number) : 0
duplicate_index (number) : 0
is_raw (boolean) : false
exif_iso (number) : 200
is_ldr (boolean) :
true rating (number) : 1
description (string) : ""
red (boolean) : false
get_tags (function) duplicate (function) creator (string) : ""
latitude (nil)
blue (boolean) : false
exif_datetime_taken (string) : "2014:04:27 14:10:27"
exif_maker (string) : "Panasonic"
drop_cache (function) title (string) : ""
reset (function)
create_style (function)
apply_style (function)
film (userdata,dt_lua_film_t) : /images 1
(userdata,dt_lua_image_t): .toplevel
[......]
exif_exposure (number) : 0.0062500000931323
exif_lens (string) : ""
detach_tag (function): toplevel.film.2.detach_tag
exif_focal_length (number) : 4.5
get_group_members (function): toplevel.film.2.get_group_members
id (number) : 1
group_with (function): toplevel.film.2.group_with
delete (function): toplevel.film.2.delete
purple (boolean) : false
is_hdr (boolean) : false
exif_model (string) : "DMC-FZ200"
green (boolean) : false
yellow (boolean) : false
longitude (nil)
filename (string) : "100.JPG"
width (number) : 945
attach_tag (function): toplevel.film.2.attach_tag
exif_focus_distance (number) : 0
height (number) : 648
local_copy (boolean) : false
copy (function): toplevel.film.2.copy group_leader
(userdata,dt_lua_image_t): .toplevel
Como es evidente, una imagen tiene una gran cantidad de campos que proveen todo tipo de información sobre si misma. Nos interesa la etiqueta roja. Este campo es un boleano, y la documentación nos indica que puede ser escrito. Ahora solo necesitamos encontrar todas las imágenes con dicho campo e imprimirlas.
darktable = require "darktable" for _,v in ipairs(darktable.database) do if v.red then print(tostring(v)) end end
Este código es bastante sencillo de entender en este punto, pero contiene algunos aspectos interesantes sobre lua que vale la pena resaltar:
ipairs
es una funcionalidad estándar de lua que iterará todos los indices numéricos de una tabla. Lo usamos aquí porque darktable.database
tiene indices no-numéricos que son funciones para manipular la base de datos (por ejemplo, agregando o eliminando imágenes).
Realizar la iteración de una table retornará tanto las llaves como los valores utilizados. Es convencional utilizar en lua
una variable llamada “_
” para almacenar los valores que no nos interesan.
Note que utilizamos la función estándar de lua tostring
y no la específica de darktable darktable.debug.dump
. La función estándar retornará un nombre para el objeto, donde la función de depurar imprimirá el contenido. La función de
depurado sería demasiado verbosa para ser utilizada aquí. De nuevo, es una gran herramienta de depurado pero no debe ser utilizada
para mas nada.
Hasta ahora, todos nuestros scripts han realizado algo durante el inicio. Esto da un uso limitado y no nos permite reaccionar a acciones reales del usuario. Pare realizar cosas mas avanzadas necesitamos registrar una función que será llamada en un evento dado. El evento más común al cual reaccionar es un atajo de teclado.
darktable = require "darktable" local function hello_shortcut(event, shortcut) darktable.print("Hello, I just received '"..event.. "' with parameter '"..shortcut.."'") end darktable.register_event("shortcut",hello_shortcut, "A shortcut that print its parameters")
Now start darktable, go to
preferences => shortcut => lua => A shortcut that prints its parameters
assign a shortcut and try it. You should have a nice message printed on the screen.
Let's look at the code in detail. We first define a function with two parameters. These
parameters are strings. The first one is the type of event that is triggered (
"shortcut"
) and the second one is what shortcut specifically (
"A shortcut that print its parameters"
). The function itself calls
darktable.print
that will print the message as an overlay in the main window.
Una vez que la función sea definida, lo registraremos como un atajo de teclado. Para realizar esto, llamaremos a darktable.register_event
la cual es una función genérica para todos los tipos de eventos. Le diremos que estamos registrando un atajo como evento,
luego le daremos la llamada y por último, le indicaremos la cadena que se utilizará para describir el atajo en la ventana
de preferencias.
Let's try a shortcut that is a little more interactive. This one will look at the images the user is currently interested in (selected or moused-over) and will increase their rating.
darktable = require "darktable" darktable.register_event("shortcut",function(event,shortcut) local images = darktable.gui.action_images for _,v in pairs(images) do v.rating = v.rating + 1 end end,"Increase the rating of an image")
En este punto, la mayoría de este código debería ser auto explicativo. Solo un par de notas:
En vez de declarar una función y hacerle referencia, la declararemos directamente en la llamada a darktable.register_event
esto es estrictamente equivalente pero un poco más compacto.
image.rating
es el campo de cualquier imagen que otorga el puntaje (entre 0 y 5 estrellas, -1 significa rechazada).
darktable.gui.action_images
es una tabla que contiene todas las imágenes de interés. darktable actuará sobre las imágenes seleccionadas si cualquier
imagen es seleccionada, y sobre otras imágenes con el puntero del ratón sobre ellas si ninguna imagen ha sido seleccionada.
Esta función le permite seguir fácilmente la lógica en lua de la interfaz de darktable.
Si selecciona una imagen y presiona su atajo un par de veces, funcionará correctamente al principio, pero luego de que haya llegado a las cinco estrellas, darktable comenzará a mostrá un mensaje de error en la consola:
LUA ERROR : rating too high : 6 stack traceback: [C]: in ? [C]: in function '__newindex' ./configdir/luarc:10: in function <./configdir/luarc:7> LUA ERROR : rating too high : 6
Esta es la forma que tiene lua para reportar errores. Hemos estado tentados a ajustar un puntaje de 6 a una imagen, pero un puntaje solo puede subir hasta 5. Sería trivial agregar una revisión, pero en cambio vayamos a la forma complicada y obtengamos el error.
darktable.register_event("shortcut",function(event,shortcut) local images = darktable.gui.action_images for _,v in pairs(images) do result,message = pcall(function() v.rating = v.rating + 1 end) if not result then darktable.print_error("could not increase rating of image ".. tostring(v).." : "..message) end end end,"Increase the rating of an image")
pcall
correrá su primer argumento y atajará cualquier excepción que sea lanzada. Si no hay excepciones devolverá un true (verdadero)
además de cualquier resultado retornado por la función; si hay una excepción retornará false (falso)
y el mensaje de error de la excepción. Simplemente probaremos estos resultados y los imprimiremos en la consola...
We have learned to use lua to adapt darktable to our particular workflow, now let's look at how to use lua to easily export images. darktable can easily export images to some online services but there are always more. If you are able to upload an image to a service via the command line then you can use lua to integrate it into darktable's user interface.
En este nuevo ejemplo utilizaremos lua para exportar vía scp. Un nuevo almacenamiento aparecerá en la interfaz de darktable, la cual exportará las imágenes a un objetivo remoto vía el mecanismo de copia ssh.
darktable = require "darktable" darktable.preferences.register("scp_export","export_path", "string","target SCP path", "Complete path to copy to. Can include user and hostname","") darktable.register_storage("scp_export","Export via scp", function( storage, image, format, filename, number, total, high_quality, extra_data) if not darktable.control.execute("scp "..filename.." ".. darktable.preferences.read("scp_export", "export_path","string")) then darktable.print_error("scp failed for "..tostring(image)) end end)
darktable.preferences.register
will add a new preference to darktable's preference menu.
scp_export
and
export_path
allows us to uniquely identify our preference. These fields are reused when we read the
value of the preference. The
string
field tells the lua engine that the preference is a string. It could also be an integer,
a filename or any of the types detailed in API manual relating to
types_lua_pref_type
. We then have the label for the preference in the preference menu, the tooltip when
hovering over the value and a default value.
darktable.register_storage
es la llamada que realmente registra un nuevo almacenamiento. El primero argumento es el nombre del almacenamiento, el segundo
es la etiqueta que se mostrará en la Interfaz y el último es la función de llamada de cada imagen. Esta función tiene muchos
mas parámetros, pero filename
(nombre de archivo) es la única que utilizaremos en este ejemplo. Esta contiene el nombre del archivo temporal donde la imagen
fue exportada por el motor de darktable.
Este código funcionará pero tiene un par de limitaciones. Esto es un simple ejemplo después de todo:
Utilizamos preferencias para configurar la ruta del objetivo. Sería mucho mejor agregar un elemento a la Interfaz de exportado en darktable. Detallaremos como realizar esto en la próxima sección
Nosotros no revisamos el valor retornado del scp. Ese comando puede fallar, particularmente si el usuario no ha ajustado correctamente la preferencia.
Este script no puede leer las entradas del usuario. El scp remoto debe utilizar una copia menor de la clave. Scp no puede proveer una clave fácilmente, así que lo dejaremos tal como está
No se mostrará un mensaje una vez que el ejemplo se haya realizado, solo la barra de progreso a la izquierda le indicará al usuario que el trabajo ha sido realizado.
We use
darktable.control.execute
to call an external program. The normal
os.execute
would block other lua codes from happening.
Nuestro ejemplo anterior fue un poco limitado. Particularmente, el uso de una preferencia para una ruta de exportado no fue muy agradable. Podemos hacer algo mejor que eso al agregar elementos a la interfaz de usuario dentro del diálogo de exportar.
Los elementos de la interfaz son creados con la función darktable_new_widget
. Esta función toma un tipo de widget como un parámetro y retorna un nuevo objeto que le corresponderá a dicho widget. Puede
ajustar varios campos para dicho widget para ajustar sus parámetros. Entonces podrá utilizar objetos como parámetros de varias
funciones, las cuales se agregarán a la Interfaz de darktable. El siguiente ejemplo simple agrega una lib en la vista de mesa
de luz con una etiqueta sencilla
local my_label = darktable.new_widget("label") my_label.label = "Hello, world !" dt.register_lib("test","test",false,{ [dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER",20}, },my_label)
Aquí tiene un buen truco de sintaxis para que el código de los elementos de su Interfaz se pueda leer y escribir más fácilmente. Puede llamar estos objetos como funciones con una tabla de valores clave como un argumento. Esto permite que el siguiente ejemplo pueda funcionar. Crea un widget contenedor con dos sub-widgets. Una etiqueta y un campo de entrada de texto.
local my_widget = darktable.new_widget("box"){ orientation = "horizontal", darktable.new_widget("label"){ label = "here => " }, darktable.new_widget("entry"){ tooltip = "please enter text here" } }
Ahora que sabemos esto, mejoremos un poco nuestro script.
darktable = require "darktable" local scp_path = darktable.new_widget("entry"){ tooltip ="Complete path to copy to. Can include user and hostname", text = "", reset_callback = function(self) self.text = "" end } darktable.register_storage("scp_export","Export via scp", function( storage, image, format, filename, number, total, high_quality, extra_data) if not darktable.control.execute("scp "..filename.." ".. scp_path.text ) then darktable.print_error("scp failed for "..tostring(image)) end end, nil, --finalize nil, --supported nil, --initialize darktable.new_widget("box") { orientation ="horizontal", darktable.new_widget("label"){label = "target SCP PATH "}, scp_path, })
Hasta ahora, nuestro código lua estaba en luarc. Esa es una buena forma de desarrollar su script pero no muy práctico para ser distribuido. Necesitamos convertir esto en un módulo propio de lua. Para hacerlo, guardamos el código en un archivo separado (scp-storage.lua en nuestro caso):
--[[ SCP STORAGE a simple storage to export images via scp AUTHOR Jérémy Rosen (jeremy.rosen@enst-bretagne.fr) INSTALLATION * copy this file in $CONFIGDIR/lua/ where CONFIGDIR is your darktable configuration directory * add the following line in the file $CONFIGDIR/luarc require "scp-storage" USAGE * select "Export via SCP" in the storage selection menu * set the target directory * export your images LICENSE GPLv2 ]] darktable = require "darktable" darktable.configuration.check_version(...,{2,0,0}) local scp_path = darktable.new_widget("entry"){ tooltip ="Complete path to copy to. Can include user and hostname", text = "", reset_callback = function(self) self.text = "" end } darktable.register_storage("scp_export","Export via scp", function( storage, image, format, filename, number, total, high_quality, extra_data) if darktable.control.execute("scp "..filename.." ".. scp_path.text ) then darktable.print_error("scp failed for "..tostring(image)) end end, nil, --finalize nil, --supported nil, --initialize darktable.new_widget("box") { orientation ="horizontal", darktable.new_widget("label"){label = "target SCP PATH "}, scp_path, })
darktable will look for scripts (following the normal lua rules) in the standard
directories plus
$CONFIGDIR/lua/?.lua
. So our script can be called by simply adding
require "scp-storage"
in the luarc file. A couple of extra notes...
La función darktable.configuration.check_version
revisará la compatibilidad por usted. El ...
se convertirá en el nombre de su script {2,0,0}
es la versión de API con la que ha probado su script. Puede agregar múltiples versiones de API si actualiza su script para
múltiples versiones de darktable.
Asegúrese de declarar todas sus funciones como local
para no contaminar el nombre en general.
Make sure you do not leave debug prints in your code.
darktable.print_log
in particular allows you to leave debug prints in your final code without disturbing
the console.
Es libre de escoger cualquier licencia para su script, pero los scripts que son cargados en el sitio web de darktable necesitan ser GPLv2.
Once you have filled all the fields, checked your code, you can upload it to our script page here.
Es posible enviar un comando lua a darkable vía la interfaz del DBus. El método org.darktable.service.Remote.Lua toma una sola cadena de parámetro, la cua es interpretada como un comando lua. El comando será ejecutado en el contexto actual de lua y debería bien sea retornar un nil o una cadena. El resultado se devolverá como resultado del método DBus.
Si la llamada a Lua resulta en un error, la llamada del método DBus retornará un error org.darktable.Error.LuaError con el mensaje de error de lua como un mensaje adjunto al error de DBus.
Precaución: Esta propiedad es bastante experimental. Se sabe que varios elementos aún no funcionan en el modo de librería. Se recomienda tener mucho cuidado al realizar estas pruebas.
La interfaz de lua le permite utilizar darktable desde cualquier script lua. Esto cargará darktable como una librería y le proveerá con la mayoría de la API lua (darktable está configurado sin cabeceras, así que las funciones relacionadas a la interfaz no están disponibles).
Como ejemplo, el siguiente programa imprimirá la lista de todas las imágenes en su librería:
#!/usr/bin/env lua package = require "package" package.cpath=package.cpath..";./lib/darktable/lib?.so" dt = require("darktable")( "--library", "./library.db", "--datadir", "./share/darktable", "--moduledir", "./lib/darktable", "--configdir", "./configdir", "--cachedir","cachedir", "--g-fatal-warnings") require("darktable.debug") for k,v in ipairs(dt.database) do print(tostring(v)) end
Note que la tercera línea apunta a la ubicación del archivo libdarktable.so
.
También tenga en cuenta que la llamada a require
devuelve una función que solo se puede llamar una vez y le permite configurar el parámetro de la línea de comando de darktable.
El parámetro :memory:
para --library
es útil aquí si no desea trabajar en la librería personal.