Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • d198bc9
  • /
  • doc
  • /
  • usermanual
  • /
  • lua
  • /
  • lua.xml
Raw File Download
Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge Iframe embedding
swh:1:cnt:6018a049f4ab4b358a9541c7520518e77f099436
directory badge Iframe embedding
swh:1:dir:851b8170c8d444360245274ca3eb21f4829c8cc5
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
lua.xml
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
               "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
		<!ENTITY % darktable_dtd SYSTEM "../dtd/darktable.dtd">
		%darktable_dtd;
		]>
<chapter status="final" id="lua_chapter">

  <chapterinfo>
    <keywordset>
      <keyword>scripting</keyword>
      <keyword>lua</keyword>
    </keywordset>
  </chapterinfo>

  <title>Scripting with Lua</title>

  <indexterm>
    <primary>Lua</primary>
  </indexterm>

  <indexterm>
    <primary>scripting</primary>
  </indexterm>

  <para>
    darktable comes with a versatile scripting interface for functionality enhancement.
  </para>

  <para>
    <screenshot>

      <screeninfo>Scipting with Lua</screeninfo>

      <graphic fileref="images/lua_view.png" scalefit="1" width="80%"></graphic>

    </screenshot>
  </para>

  <sect1 id="lua_usage">

    <title>Lua usage</title>

    <para>
      Lua can be used to define actions which darktable will perform whenever a specified event
      is triggered. One example might be calling an external application during file export in
      order to apply additional processing steps outside of darktable.
    </para>

    <para>
      darktable uses <ulink url="http://www.lua.org/"><emphasis>Lua</emphasis></ulink>, which is
      an independent project founded in 1993, providing a powerful, fast, lightweight,
      embeddable scripting language. Lua is widely used by many open source applications, in
      commercial programs, and for games programming.
    </para>

    <para>
      darktable uses Lua version 5.2. Describing the principles and syntax of Lua is beyond the
      scope of this usermanual. For a detailed introduction see the
      <ulink url="http://www.lua.org/manual/5.2/manual.html">Lua reference manual</ulink>.
    </para>

    <sect2 id="lua_basic_principles">

      <title>Basic principles</title>

      <para>
        At startup, darktable will automatically run two Lua scripts:
      </para>

      <itemizedlist>

        <listitem><para>
          a script called <emphasis>luarc</emphasis> in
          <emphasis>$DARKTABLE/share/darktable</emphasis>
        </para></listitem>

        <listitem><para>
          a script called <emphasis>luarc</emphasis> in the user's configuration directory
        </para></listitem>

      </itemizedlist>

      <para>
        $DARKTABLE is used here to represent your system's darktable installation directory.
      </para>

      <para>
        This is the only time darktable will run Lua scripts by itself. The script can register
        callbacks to perform actions on various darktable events. This callback mechanism is the
        primary way to trigger lua actions.
      </para>

    </sect2>

    <sect2 id="lua_hello_world">

      <title>A simple lua example</title>

      <para>
        Let's start with a simple example. We will print some code on the console. Create a file
        called <emphasis>luarc</emphasis> in darktable's configuration directory (usually
        <command>~/.config/darktable/</command>) and add the following line to it:
      </para>

      <para>
<programlisting language="lua">print("Hello World !")</programlisting>
      </para>

      <para>
        Start darktable and you will see the sencence <emphasis>Hello World !</emphasis> printed
        on the console. Nothing fancy but it's a start...
      </para>

      <para>
        At this point, there is nothing specific to darktable in the script. We simply use the
        standard
        <code>print</code>
        function to print a string. That's nice and all, but we can do better than that. To
        access the darktable API you first need to
        <code>require</code>
        it and save the returned object in a variable. Once this is done you can access the
        darktable API as subfields of the returned object. All of this is documented in
        darktable's Lua API reference manual (see <xref linkend="lua_api"/>).
      </para>

      <para>
<programlisting language="lua">local darktable = require "darktable"
darktable.print_error("Hello World !")</programlisting>
      </para>

      <para>
        Run the script ... and nothing happens. The function
        <code>darktable.print_error</code>
        is just like print but will only print the message if you have enabled lua traces with
        <command>-d lua</command> on the command line. This is the recommended way to do traces
        in a darktable lua script.
      </para>

    </sect2>

    <sect2 id="lua_print_images">

      <title>Printing labeled images</title>

      <para>
        This first example showed us the very basics of lua and allowed us to check that
        everything is working properly. Let's do something a little bit more complex. Let's try
        to print the list of images that have a red label attached to them. But first of all,
        what is an image?
      </para>

      <para>
<programlisting language="lua">
local darktable = require "darktable"
local debug = require "darktable.debug"
print(darktable.debug.dump(darktable.database[1]))</programlisting>
      </para>

      <para>
        Running the code above will produce a lot of output. We will look at it in a moment, but
        first let's look at the code itself.
      </para>

      <para>
        We know about requiring
        <code>darktable</code>
        . Here, we need to separately require
        <code>darktable.debug</code>
        which is an optional section of the API that provides helper functions to help debug lua
        scripts.
      </para>

      <para>
        <code>darktable.database</code>
        is a table provided by the API that contains all images in the database (currently
        visible or not, duplicate or not...). Each entry in the database is an image object.
        Image objects are complex objects that allow you to manipulate your image in various
        ways (it is all documented in the
        <code>types_dt_lua_image_t</code>
        section of the API manual). To display our images, we use
        <code>darktable.debug.dump</code>
        which is a function that will take anything as its parameter and recursively dump its
        content. Since images are complex objects that indirectly reference other complex
        objects, the resulting output is huge. Below is a cut down example of the output.
      </para>

      <para>
        <literallayout>

          <code>
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
   </code>

        </literallayout>
      </para>

      <para>
        As we can see, an image has a large number of fields which provide all sort of
        information about it. We are interested in the red label. This field is a boolean, and
        the documentation tells us that it can be written. We now just need to find all images
        with that field and print them out.
      </para>

      <para>
<programlisting language="lua">
darktable = require "darktable"
for _,v in ipairs(darktable.database) do
  if v.red then
    print(tostring(v))
  end
end</programlisting>
      </para>

      <para>
        This code should be quite simple to understand at this point, but it contains a few
        interesting aspects about lua that are worth highlighting:
        <itemizedlist>

          <listitem><para>
            <code>ipairs</code>
            is a standard lua function that will iterate through all numeric indices of a table.
            We use it here because
            <code>darktable.database</code>
            has non-numeric indices which are functions to manipulate the database itself
            (adding or deleting images, for example).
          </para></listitem>

          <listitem><para>
            Iterating through a table will return both the key and the value used. It is
            conventional in lua to use a variable named <quote>
            <code>_</code>
            </quote> to store values that we don't care about.
          </para></listitem>

          <listitem><para>
            Note that we use the standard lua function
            <code>tostring</code>
            here and not the darktable specific
            <code>darktable.debug.dump</code>
            . The standard function will return a name for the object whereas the debug function
            will print the content. The debug function would be too verbose here. Once again, it
            is a great debug tool but should not be used for anything else.
          </para></listitem>

        </itemizedlist>
      </para>

    </sect2>

    <sect2 id="lua_simple_shortcut">

      <title>Adding a simple shortcut</title>

      <para>
        So far, all our scripts have done things during startup. This is of limited use and
        doesn't allow us to react to real user actions. To do more advanced things we need to
        register a function that will be called on a given event. The most common event to react
        to is a keyboard shortcut.
      </para>

      <para>
<programlisting language="lua">
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")
      </programlisting>
      </para>

      <para>
        Now start darktable, go to
        <code> preferences => shortcut => lua => A shortcut that print its parameters </code>
        assign a shortcut and try it. You should have a nice message printed on the screen.
      </para>

      <para>
        Let's look at the code in details. We first define a function with two parameters. These
        parameters are strings. The first one is the type of event that is triggered (
        <code>"shortcut"</code>
        ) and the second one is what shortcut specifically (
        <code>"A shortcut that print its parameters"</code>
        ). The function itself calls
        <code>darktable.print</code>
        that will print the message as an overlay in the main window.
      </para>

      <para>
        Once that function is defined, we register it as a shortcut callback. To do that we call
        <code>darktable.register_event</code>
        which is a generic function for all types of events. We tell it that we are registering
        a shortcut event, then we give the callback to call and last, we give the string to use
        to describe the shortcut in the preference window.
      </para>

      <para>
        Let's try a shortcut 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.
      </para>

      <para>
<programlisting language="lua">
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")
      </programlisting>
      </para>

      <para>
        At this point, most of this code should be self explanatory. Just a couple of notes:
        <itemizedlist>

          <listitem><para>
            Instead of declaring a function and referencing it, we declare it directly in the
            call to
            <code>darktable.register_event</code>
            this is strictly equivalent but slightly more compact.
          </para></listitem>

          <listitem><para>
            <code>image.rating</code>
            is a field of any image that gives its rating (between 0 and 5 stars, -1 means
            rejected).
          </para></listitem>

          <listitem><para>
            <code>darktable.gui.action_images</code>
            is a table containing all the images of interest. darktable will act on selected
            images if any image is selected, and on the image under the mouse if no image is
            selected. This function allows to easily follow darktable's UI logic in lua.
          </para></listitem>

        </itemizedlist>
      </para>

      <para>
        If you select an image and press your shortcut a couple of time, it will work correctly
        at first but when you have reached five stars, darktable will start showing the
        following error on the console:
      </para>

      <para>
        <literallayout>

          <code>

<![CDATA[
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
  ]]>

          </code>

        </literallayout>
      </para>

      <para>
        This is lua's way of reporting errors. We have attempted to set a rating of 6 to an
        image, but a rating can only go as high as 5. It would be trivial to add a check, but
        let's go the complicated way and catch the error instead.
      </para>

      <para>
<programlisting language="lua">
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")</programlisting>
      </para>

      <para>
        <code>pcall</code>
        will run its first argument and catch any exception thrown by it. If there is no
        exception it will return
        <code>true</code>
        plus any result returned by the function; if there is an exception it will return
        <code>false</code>
        and the error message of the exception. We simply test these results and print them on
        the console...
      </para>

    </sect2>

    <sect2 id="lua_storage_example">

      <title>Exporting images with Lua</title>

      <para>
        We have learned to use lua to adapt darktable to our particular workflow, let's look at
        how to use lua to easily export images. darktable can easily export images to quite a
        few 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.
      </para>

      <para>
        In this next example we will use lua to export via <command>scp</command>. A new storage
        will appear in darktable's UI which will export images to a remote target via ssh's copy
        mechanism.
      </para>

      <para>
<programlisting language="lua">
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 coroutine.yield("RUN_COMMAND","scp "..filename.." "..
      darktable.preferences.read("scp_export",
         "export_path","string")) then
      darktable.print_error("scp failed for "..tostring(image))
    end
end)</programlisting>
      </para>

      <para>
        <code>darktable.preferences.register</code>
        will add a new preference to darktable's preference menu.
        <code>scp_export</code>
        and
        <code>export_path</code>
        allow us to uniquely identify our preference. These fields are reused when we read the
        value of the preference. The
        <code>string</code>
        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
        <code>types_lua_pref_type</code>
        . We then have the label for the preference in the preference menu, the tooltip when
        hovering over the value and a default value.
      </para>

      <para>
        <code>darktable.register_storage</code>
        is the call that actually registers a new storage. The first argument is a name for the
        storage, the second is the label that will be displayed in the UI and last is a function
        to call on each image. This function has a lot of parameters, but
        <code>filename</code>
        is the only one we use in this example. It contains the name of a temporary file where
        the image was exported by darktable's engine.
      </para>

      <para>
        This code will work but it has a couple of limitations. This is just a simple example
        after all:
        <itemizedlist>

          <listitem><para>
            We use preferences to configure the target path. It would be nicer to add an element
            to the export UI in darktable. We will detail how to do that in the next section
          </para></listitem>

          <listitem><para>
            We do not check the returned value of scp. That command might fail, in particular if
            the user has not correctly set the preference.
          </para></listitem>

          <listitem><para>
            This script can't read input from the user. The remote scp must use password-less
            copy. Scp can't be provided a password easily, so we will leave it that way
          </para></listitem>

          <listitem><para>
            There is no message displayed once the example is done, only the progressbar on the
            lower left side tells the user that the job is done.
          </para></listitem>

          <listitem><para>
            We use
            <code>coroutine.yield</code>
            to call an external program. The normal
            <code>os.execute</code>
            would block other lua codes from happening.
          </para></listitem>

        </itemizedlist>
      </para>

    </sect2>

    <sect2 id="lua_gui_example">

      <title>Building User Interface elements</title>

      <para>
        Our previous example was a bit limited. In particular the use of a preference for the
        export path wasn't very nice. We can do better than that by adding elements to the user
        interface in the export dialog.
      </para>

      <para>
        UI elements are created via the
        <code>darktable_new_widget</code>
        function. This function takes a type of widget as a parameter and returns a new object
        corresponding to that widget. You can then set various fields to that widget to set its
        parameters. You will then use that object as a parameter to various functions that will
        add it to the darktable UI. The following simple example adds a lib in the lighttable
        view with a simple label
      </para>

<programlisting language="lua">
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)
    </programlisting>

      <para>
        There is a nice syntactic trick to make UI element code easier to read and write. You
        can call these objects as functions with a table of key values as an argument. This
        allows the following example to work. It creates a container widget with two
        sub-widgets. A label and a text entry field.
      </para>

<programlisting language="lua">
	local my_widget = darktable.new_widget("box"){
		orientation = "horizontal",
		darktable.new_widget("label"){ label = "here => " },
		darktable.new_widget("entry"){ tooltip = "please enter text here" }
	}
</programlisting>

      <para>
        Now that we know that, let's improve our script a bit.
      </para>

<programlisting language="lua">
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 coroutine.yield("RUN_COMMAND","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,
})
</programlisting>

    </sect2>

    <sect2 id="lua_sharing_scripts">

      <title>Sharing scripts</title>

      <para>
        So far, our lua code was in <emphasis>luarc</emphasis>. That's a good way to develop
        your script but not very practical for distribution. We need to make this into a proper
        lua module. To do that, we save the code in a separate file
        (<command>scp-storage.lua</command> in our case):
      </para>

      <para>
<programlisting language="lua">
--[[
SCP STORAGE
a simple storage to export images via scp

AUTHOR
J&eacute;r&eacute;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 coroutine.yield("RUN_COMMAND","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,
})
</programlisting>
      </para>

      <para>
        darktable will look for scripts (following the normal lua rules) in the standard
        directories plus
        <code>$CONFIGDIR/lua/?.lua</code>
        . So our script can be called by simply adding
        <code>require "scp-storage"</code>
        in the <emphasis>luarc</emphasis> file. A couple of extra notes...
        <itemizedlist>

          <listitem><para>
            The function
            <code>darktable.configuration.check_version</code>
            will check compatibility for you. The
            <code>...</code>
            will turn into your script's name and
            <code>{2,0,0}</code>
            is the API version you have tested your script with. You can add multiple API
            versions if you update your script for multiple versions of darktable.
          </para></listitem>

          <listitem><para>
            Make sure to declare all your functions as
            <code>local</code>
            to not pollute the general namespace.
          </para></listitem>

          <listitem><para>
            Make sure you do not leave debug prints in your code.
            <code>darktable.print_error</code>
            in particular allows you to leave debug prints in your final code without disturbing
            the console.
          </para></listitem>

          <listitem><para>
            You are free to choose any license for your script but scripts that are uploaded on
            darktable's website need to be GPLv2.
          </para></listitem>

        </itemizedlist>
        Once you have filled all the fields, checked your code, you can upload it to our script
        page
        <ulink url="http://darktable.org/redmine/projects/darktable/wiki/LuaScripts">here</ulink>.
      </para>

    </sect2>

    <sect2 id="lua_dbus">

      <title>Calling Lua from DBus</title>

      <para>
        It is possible to send a lua command to darktable via its DBus interface. The method
        <emphasis>org.darktable.service.Remote.Lua</emphasis> takes a single string parameter
        which is interpreted as a lua command. The command will be executed in the current lua
        context and should return either <emphasis>nil</emphasis> or a string. The result will
        be passed back as the result of the DBus method.
      </para>

      <para>
        If the Lua call results in an error, the DBus method call will return an error
        <emphasis>org.darktable.Error.LuaError</emphasis> with the lua error message as the
        message attached to the DBus error.
      </para>

    </sect2>

    <sect2 id="lua_lib">

      <title>Using darktable from a lua script</title>

      <para>
        <emphasis>Warning: This feature is very experimental. It is known that several elements
        don't work in library mode yet. Careful testing is highly recommended.</emphasis>
      </para>

      <para>
        The lua interface allows you to use darktable from any lua script. This will load
        darktable as a library and provide you with most of the lua API (darktable is configured
        headless, so the functions related to the user interface are not available).
      </para>

      <para>
        As an example, the following program will print the list of all images in your library:
      </para>

      <para>
<programlisting language="lua">#!/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
</programlisting>
      </para>

      <para>
        Note the third line that points to the location of the
        <filename>libdarktable.so</filename> file.
      </para>

      <para>
        Also note that the call to
        <function>require</function>
        returns a function that can be called only once and allows you to set darktable's
        command line parameter. The
        <function>:memory:</function>
        parameter to
        <function>--library</function>
        is usefull here if you don't want to work on your personal library.
      </para>

    </sect2>

  </sect1>

  <sect1 status="final" id="lua_api">

    <title>Lua API</title>

    <indexterm>
      <primary>Lua API</primary>
    </indexterm>

    <para>
      darktable's Lua API is documented in its own manual with a detailed description of all
      data structures and functions. You can download the API manual from
      <ulink url="http://www.darktable.org/resources/">darktable's webpage</ulink>.
    </para>

  </sect1>

</chapter>

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API

back to top