Developping lua scripts - restart required

I’m developing lua scripts for darktable. I have one problem at the moment I don’t know how to solve. It looks like I have to restart darktable every time I made a change to my script, for the new version to become active. This is a real pain in the butt !

Is there a way to get darktable to reload a script without restarting it ?

Is stopping it in the script manager not sufficient?

No, unfortunately not. has absolutely no effect.

No, not that I know. But perhaps @wpferguson knows more.

1 Like

@Tobias thanks for tagging me.

Only if the script tells script_manager how to stop it (and it’s not a lib).

Here’s a minimal script that can be stopped, edited, and restarted with the changes taking effect without having to restart darktable.

local dt = require "darktable"

local script_data = {}

local function destroy()
  dt.print_log("destroying shutdown")
end

script_data.destroy = destroy

dt.print("this is a different sentence to see if I can see the print")
dt.control.sleep(5000)

return script_data

You can start the script in darktable, open it in an editor, change the message, stop it, start it, and then see the changed message.

Any register_ function called in the script (register_action, register_selection, register_event, register_storage) must call the corresponding destroy_ function in the destroy() routine as part of removing the script from the running darktable. Scripts that show how to do that are:

  • contrib/clear_GPS.lua
  • contrib/gimp.lua
  • contrib/select_untagged.lua

The exception to this is register_lib. I haven’t figured out the darktable code for destroying a lib yet, so at present it gets hidden when it is turned off and shown when it is turned on (reference examples/moduleExample.lua).

Thank you very much !

This works fine for my module (which was initially based on the scp module).

One question:

local script_data = {}
script_data.destroy = destroy
return script_data

What is the script_data for ?

One more question / problem. I do use register_storage. There is one callback for initializing. I use that that the paramaters (target directory) actually work and are accessible.
The way it is supposed to work is that you return an empty array (of images). But I’m unable to do that. If I use

return {}

Darktable just crashes. How do I return an empty array to signal that there is nothing to do / export ?

Your example should be

local function destroy()
  -- destroy any register_ items
end

local script_data = {}
script_data.destroy = destroy
return script_data

script_data is a table of data about how to handle the script that gets returned to script_manager when it starts the script. I just chose script_data as the name, but you could name it anything.

Just do a return nil then test with if <return_value> then

I see. The script manage just needs an (empty ?) table with data.

I don’t understand the statement script_data.destroy = destroy, though.

Unfortunately that does not work as the return goes back to darktable and is part of the API. Returning nothing or nil tells darktable that everything is ok and that is should run the export subroutine for each exported image. Returning and empty table tells Darktable that there is nothing to export. This is how it is documented on the Darktable documentation.

It would be the same as

local script_data = {}
script_data.destroy = local function()
  -- destroy any register_ items
end
return script_data

In other words suppose you had

local function function_with_a_really_long_name_that_i_dont_want_to_type_every_time()
  -- do something
end

local short_name = function_with_a_really_long_name_that_i_dont_want_to_type_every_time

Now every time I call short_name() function_with_a_really_long_name_that_i_dont_want_to_type_every_time gets called

The darktable lua documentation is at darktable lua documentation - Home

Yes, I have the darktable lua documentation open. Unfortunately is has no snippets on how to use stuff and the is my first go at lua.

local function initialize_export(storage, format, images, high_quality, extra_data)
    if not file_exists(pa_path.text) then
        dt.print_hinter("Error: Target path '"..pa_path.text.."' does not exist !")
        return   -- This return should return an empty table
    end
end

dt.register_storage("pa_export","Export to photoarchive",
    export_file,
    --nil,
    nil,
    initialize_export,
    dt.new_widget("box") {
        orientation ="horizontal",
        dt.new_widget("label"){label = "Photoarchive path "}, pa_path,
    }
)

Relevant documentation: darktable lua documentation - darktable.register_storage

One detail from the documentation:

table of types.dt_lua_image_t

How do I attribute a type to a table ?

The best place for understanding how the scripts work, is the scripts themselves. Find scripts that use register_storage and see how they used it. Run the script and see how it works. The scripts are located in your darktable configuration directory (in the lua subdirectory) or at GitHub - darktable-org/lua-scripts.

Most of time you only need the finalize step for a storage, unless you are trying to do something tricky. See contrib/gimp.lua

Your other question

Tables are simply arrays of whatever you put in them.

And finally your script should be

local function export_file(storage, images, extra_data)
    if not file_exists(pa_path.text) then
        dt.print_hinter("Error: Target path '"..pa_path.text.."' does not exist !")
        return   -- This return should return without doing anything
  else
    for _, image in ipairs(images) do
     -- export the image
    end
end

dt.register_storage("pa_export","Export to photoarchive",
    export_file,
    nil,
    nil,
    dt.new_widget("box") {
        orientation ="horizontal",
        dt.new_widget("label"){label = "Photoarchive path "}, pa_path,
    }
)

I started testing with other (contrib) scripts doing the same thing. They crash, when the initial subrouting returns an empty array {}.

I’m going to submit a bug report…