How you would implement this stuff is right over my head, but I like the sound of this functionality. Could be very handy 
Back here again with a sample script and a lot of questions. Not sure itās the right place for them, sorry in advance.
The script works in the darkroom view triggered by a shortcut, it takes the exposure of the first image and the current image and then iterates over all the images in between adjusting their exposure value so that it changes smoothly in the image span.
local displayWait = 100
local maxDisplayWait = 1000
local function displayImg(img)
for j = 0, 10 do
dt.gui.views.darkroom.display_image(img)
dt.control.sleep(displayWait)
local curImg = dt.gui.views.darkroom.display_image()
if curImg.id == img.id then
return true
end
if displayWait < maxDisplayWait then
displayWait = displayWait + 100
end
end
return false
end
local function eq(a, b)
if a > b then
return a - b < 0.01
end
return b - a < 0.01
end
local exposureWait = 100
local maxExopsureWait = 800
local function setExposure(exp)
local v = 0.0
for i = 1, 10 do
dt.gui.action("iop/exposure/exposure", 0, "value", "set", exp)
dt.control.sleep(exposureWait)
v = dt.gui.action("iop/exposure/exposure", 0, "value", "set", "")
if eq(v, exp) then
return true
end
if exposureWait < maxExposureWait then
exposureWait = exposureWait + 100
end
end
return false
end
local function fixExposureShortcut(event, shortcut)
local startIdx = 1
local stopIdx = 2
local img = dt.gui.views.darkroom.display_image()
for i, cur in ipairs(dt.database) do
if img.id == cur.id then
stopIdx = i
break
end
end
local stopExp = dt.gui.action("iop/exposure/exposure", 0, "value", "set", "")
if not displayImg(dt.database[startIdx]) then
dt.print("ERROR: display image fail")
return
end
local startExp = dt.gui.action("iop/exposure/exposure", 0, "value", "set", "")
local delta = (stopExp - startExp)/(stopIdx - startIdx)
local curExp = startExp
displayWait = 100
for idx = startIdx + 1, stopIdx - 1 do
if not displayImg(dt.database[idx]) then
dt.print("ERROR: display image fail")
return
end
curExp = curExp + delta
if not setExposure(curExp) then
local v = dt.gui.action("iop/exposure/exposure", 0, "value", "set", "")
dt.print("ERROR: set exposure fail "..idx..", curExp: "..curExp..", v: "..v)
return
end
if idx%10 == 0 then
if displayWait > 200 then
displayWait = displayWait - 100
end
if exposureWait > 200 then
exposureWait = exposureWait - 100
end
end
end
dt.print("Idxs 1: "..startIdx.." - "..stopIdx..", delta: "..delta..", wait: "..displayWait)
end
dt.register_event("fix_exposure", "shortcut", fixExposureShortcut, "Sample timelapse helper")
Now the questions, if you donāt mind.
- I had to make it a shortcut supposed to be used in the darkroom view. The more straightforward way would be to have a button in the lighttable view, but It seems like @wpferguson is right and dt.action() requires the darkroom to be open. Is there a way to manipulate the image modules from the darkroom? And if possible in parallel, just like it applies styles for instance. Thatās another drawback of the dt.action(): it works only with āthe currentā image and therefore script can not handle multiple images at once.
- dt.display_image() and dt.action() are asynchronous, so I had to introduce a couple of functions to reliably execute them. The first and obvious question here: is there a less bulky way to do it? It is important to perform the operations reliably, as every single flaw in a long series of images will spoil the whole work. The second question: the methods work, but the amount of time required to switch the image and set the exposure grows rapidly from a fraction of a second for the first image to few seconds for the 30-th (never tried a longer series yet), why is it so and how to fix it?
- Thatās it so far, more will follow, I believe.
Today/tonightās project was select 10 images in lighttable, press a button and have each image opened in succession in darkroom, 0.5ev of exposure added, then return to lighttable after the last image was processed.
The good news is I got it to work in a way that gets rid of most of the sleeps. The bad news? It wont work for you, yet.
Issue number 1 was figuring out when the image was loaded in darkroom and ready to edit. There is an event, darkroom-image-loaded that gets triggered after an image is loaded. I set a flag that the image wasnāt loaded, then added an event handler to catch the darkroom-image-loaded and clear the flag. I polled the flag every 250ms until it cleared.
I discovered in my testing that just because the image is loaded, that doesnāt mean itās ready to process. Currently I have a 500ms sleep to account for that, but I probably need to add an event that fires when the history stack is ready. In my testing several times I changed the history stack of the previous image even though a new image had been loaded.
EDIT: I looked at where I had placed the event and it was early in the loading process. I moved it to the end, since timing is now critical, and that took care of the history stack being correctly loaded. I removed the sleeps I had added for the history stack and it worked fine. Processing 10 images took 10 seconds.
The biggest problem so far is figuring out how long it takes to process the image. The worst part is that this will vary from machine to machine and image to image, so sleeps arenāt a very robust solution. I added a pixelpipe-processing-complete event that fires when pixelpipe processing is complete. So now you can set a flag, then clear it when the event fires and just poll it to see when it clears.
Here is the output from the script. You can see where the polling takes place waiting for events to complete.
7.574088 LUA in display images
7.574165 LUA switched to darkroom view
7.574179 LUA waiting for loaded_flag
7.824306 LUA waiting for loaded_flag
8.074417 LUA waiting for loaded_flag
8.313299 LUA caught darkroom-image-loaded event
8.324526 LUA loaded
8.824639 LUA calling process_image
8.824700 LUA
in rel_adjust
8.824967 LUA status is 0.5 and adjusted value should be 1.0
8.884796 LUA waiting for complete_flag
9.134906 LUA waiting for complete_flag
9.157923 LUA caught pixelpipe-processing-complete
9.385026 LUA complete
9.385044 LUA loading next image
9.385065 LUA changing image to 102R7LR4_3J6A4938.cr3
9.595285 LUA caught darkroom-image-loaded event
9.595359 LUA loaded
9.595706 LUA caught darkroom-image-history-changed event
10.095500 LUA calling process_image
10.095583 LUA
in rel_adjust
10.095734 LUA status is 0.5 and adjusted value should be 1.0
10.139829 LUA waiting for complete_flag
10.198404 LUA caught pixelpipe-processing-complete
10.198552 LUA caught pixelpipe-processing-complete
10.390017 LUA complete
10.390036 LUA loading next image
10.390057 LUA changing image to 102R7LR4_3J6A4939.cr3
10.601893 LUA waiting for loaded_flag
10.602027 LUA caught darkroom-image-loaded event
10.602467 LUA caught darkroom-image-history-changed event
10.851988 LUA loaded
11.106151 LUA caught pixelpipe-processing-complete
11.352076 LUA calling process_image
11.352150 LUA
in rel_adjust
11.352271 LUA status is 0.5 and adjusted value should be 1.0
11.429675 LUA waiting for complete_flag
11.622348 LUA caught pixelpipe-processing-complete
11.679844 LUA complete
11.679864 LUA loading next image
11.679903 LUA changing image to 102R7LR4_3J6A4940.cr3
11.889857 LUA waiting for loaded_flag
11.890002 LUA caught darkroom-image-loaded event
11.890474 LUA caught darkroom-image-history-changed event
12.140002 LUA loaded
12.399668 LUA caught pixelpipe-processing-complete
12.640158 LUA calling process_image
12.640277 LUA
in rel_adjust
12.641546 LUA status is 0.5 and adjusted value should be 1.0
12.707925 LUA waiting for complete_flag
12.877807 LUA caught pixelpipe-processing-complete
12.958139 LUA complete
12.958184 LUA loading next image
12.958236 LUA changing image to 102R7LR4_3J6A4941.cr3
13.185218 LUA waiting for loaded_flag
13.185322 LUA caught darkroom-image-loaded event
13.185695 LUA caught darkroom-image-history-changed event
13.435309 LUA loaded
13.699331 LUA caught pixelpipe-processing-complete
13.935465 LUA calling process_image
13.935608 LUA
in rel_adjust
13.935937 LUA status is 0.5 and adjusted value should be 1.0
14.005350 LUA waiting for complete_flag
14.117753 LUA caught pixelpipe-processing-complete
14.255519 LUA complete
14.255541 LUA loading next image
14.255569 LUA changing image to 102R7LR4_3J6A4942.cr3
14.472142 LUA waiting for loaded_flag
14.472253 LUA caught darkroom-image-loaded event
14.472616 LUA caught darkroom-image-history-changed event
14.722258 LUA loaded
14.985876 LUA caught pixelpipe-processing-complete
15.222403 LUA calling process_image
15.222501 LUA
in rel_adjust
15.222661 LUA status is 0.5 and adjusted value should be 1.0
15.290053 LUA waiting for complete_flag
15.391182 LUA caught pixelpipe-processing-complete
15.540152 LUA complete
15.540183 LUA loading next image
15.540222 LUA changing image to 102R7LR4_3J6A4943.cr3
15.782496 LUA caught darkroom-image-loaded event
15.782597 LUA loaded
15.782891 LUA caught darkroom-image-history-changed event
16.282703 LUA calling process_image
16.282778 LUA
in rel_adjust
16.282890 LUA status is 0.5 and adjusted value should be 1.0
16.380693 LUA waiting for complete_flag
16.380798 LUA caught pixelpipe-processing-complete
16.433779 LUA caught pixelpipe-processing-complete
16.630878 LUA complete
16.630922 LUA loading next image
16.630976 LUA changing image to 102R7LR4_3J6A4944.cr3
16.859740 LUA waiting for loaded_flag
16.859856 LUA caught darkroom-image-loaded event
16.860285 LUA caught darkroom-image-history-changed event
17.109857 LUA loaded
17.363054 LUA caught pixelpipe-processing-complete
17.610019 LUA calling process_image
17.610136 LUA
in rel_adjust
17.610761 LUA status is 0.5 and adjusted value should be 1.0
17.676985 LUA waiting for complete_flag
17.857432 LUA caught pixelpipe-processing-complete
17.927157 LUA complete
17.927183 LUA loading next image
17.927214 LUA changing image to 102R7LR4_3J6A4945.cr3
18.188165 LUA caught darkroom-image-loaded event
18.188259 LUA loaded
18.188485 LUA caught darkroom-image-history-changed event
18.688374 LUA calling process_image
18.688446 LUA
in rel_adjust
18.688587 LUA status is 0.5 and adjusted value should be 1.0
18.799472 LUA waiting for complete_flag
18.799732 LUA caught pixelpipe-processing-complete
18.847498 LUA caught pixelpipe-processing-complete
19.049656 LUA complete
19.049707 LUA loading next image
19.049755 LUA changing image to 102R7LR4_3J6A4946.cr3
19.291240 LUA caught darkroom-image-loaded event
19.291316 LUA loaded
19.291681 LUA caught darkroom-image-history-changed event
19.791443 LUA calling process_image
19.791518 LUA
in rel_adjust
19.791674 LUA status is 0.5 and adjusted value should be 1.0
19.889182 LUA waiting for complete_flag
19.889462 LUA caught pixelpipe-processing-complete
19.937728 LUA caught pixelpipe-processing-complete
20.139343 LUA complete
20.139387 LUA loading next image
20.167388 LUA caught darkroom-image-history-changed event
Hereās the scriptā¦
display_in_darkroom.zip (1.1 KB)
If you want to experiment with the pixelpipe-processing-complete event, you can replace src/develop/deveop.c and src/lua/events.c with the ones attached
pixelpipe-event.zip (26.1 KB)
@wpferguson, thank you for the further info, events look handy, but do I get it right that the proper event placement is not in 4.1 yet? Are there any plans to push the fix to the next build?
Something I need to do on every picture is auto adjust white fulcrum in color balance rgb, and auto adjust filmic white and black relative sliders.
It would be brilliant to select multiple images in light table, then run a Lua script performing these actions for all selected. Is it possible? I havenāt used Lua yet, but the possibilities are fantastic.
Iām running a test on windows right now to make sure it works there. Once thatās done Iāll do a PR to get it merged into master (4.1) then it will be available if you build your own. Otherwise it should be part of 4.2
Yes, itās possible. Iām currently working out the rough edges in loading images in succession and applying a simple edit (add 0.5 ev of exposure). I think Iāve got that finally figured out. I may take your example and make that the next test subject.
Just to be sure, does it work in the lighttable or in the darkroom view?
You start in lighttable view. You select the images you want to change. You click the display in darkroom button to start the process. When itās finished, the script returns to lighttable view.
The PR with the event changes got merged a few hours ago, so this is now available in master. Iāll attach an updated script that uses the events I added.
display_in_darkroom.zip (1.1 KB)
First: This is a very exciting thread. Thank you for the many good ideas! I recently came up with a similar idea to simplify my often-same workflow. Iāve experimented a bit with scripts, but I quickly reach my limits.
So three questions:
- How do I find out how to address the GUI elements? How do I get code like this. Is there documentation for this?
- Concrete example: How do I click the āadjust the exposure correctionā magic wand in the exposure module?
- Is there a way to debug the Lua scripts while they are running?
Thank you and best regards,
Uli.
In master (Iām not sure about 4.0.1) you click the define shortcuts button (to the left of the gear icon at the top) and then hover over the item you wish to know about. A popup should appear with the necessary lua code.
darktable.gui.action("iop/exposure/exposure", 0, "button", "on", 1.0)
wait for a period of time or catch the pixelpipe-complete event
darktable.gui.action("iop/exposure/exposure", 0, "button", "off", 1.0)
print statements and run darktable with the -d lua flag.
Thank you for your prompt reply!
In 4.0.1 the define shortcut button shows a popup but without the LUA code. That will probably only come with 4.2. Not sure if I should switch to 4.1, letās see 
I had already suspected that the LUA scripts cannot be debugged comfortable from an IDE. Then it has to be via print.
Iāll keep experimenting with this and will post it here if it shows meaningful results.
Thanks and regards,
Uli.
@wpferguson @donda : After a few experiments I have now created my first script. You can find it attached.
It offers a new āinital workflowā module both in lighttable and darkroom view. This script can be used to do some configuration for an initial image workflow. It calls some automatisms of different modules in the darkroom view. If this suits your workflow, the script saves some clicks and time.
I am looking forward to your comments 
InitialWorkflowModule.zip (4.3 KB)
Best regards,
Uli.
Thanks for sharing your workā¦
I tried this and if works as intended from what I can seeā¦very cool. I wonderā¦it seems like it could be a bit slow for a large number of images⦠I wonder is there a way to have the changes applied as the script is process without each module needing to be activated and opened and updated in the UI⦠That might speed it up but I am not sure if that is something that could be done by altering the code⦠I guess @wpferguson might be the one to knowā¦
Very nice little script thanks for sharing ⦠I think it will give a lot of ideas to possible version of this for others
EDIT:
One thing if you open images as I do with legacy wb I donāt think your WB settings can be applied as you intendā¦you may say that ā¦but unchanged I assume would leave it at camera reference and you assume Cat16 is not set to bypass. If it is then none of your selections for CC illuminant work⦠I think all possible combinations work if you apply your script to an image with the default modern WB ā¦but not if it is set at legacy⦠to handle legacy you would need an option to select camera reference and a means to enable or detect the status of CAT ie bypass vs one of the transforms⦠Perhaps you mention this
@priort Thank you for the feedback 
If you are using dt4.1 you can speed it up by using the new event āpixelpipe-processing-completeā. To do that, edit function WaitForPixelPipe and replace darkroom-image-history-changed with pixelpipe-processing-complete.
The note on legacy white balance is correct. I use the modern workflow. I will try to add a check to the script.
Best regards,
Uli.
darktable.gui.action() only provides a way to manipulate the GUI.
My solution so far is to use a combination of styles (with a lot of modules turned off) and darktable.gui.action() to manipulate the style (turn modules on and off, manipulate the settings, etc). I built the equivalent style using darktable.gui.action() and it is slow.
You could have a script that caught the darkroom-image-loaded event, then checked to see if the image had been processed or not. If not, then a style could be applied using darktable.styles(), then manipulated using darktable.gui.action().
That is an interesting approach⦠I guess the only thing here is Ulrich is firing off auto exposure and black and white levels in filmic so no way to do that with your approach at least I donāt think. But it could be useful for other edits ā¦
You could do it the first time you open the image, after youāve applied the style. You could set a tag after youāve done the auto levels/exposure or just check the status of exposure and see if itās set to auto. I havenāt had time to look at the script yet, so those are just off the top of my head.
I think he is doing what I do now and using the picker with the spot exposure setting to 50% to give the base starting pointā¦. This works pretty well. I do this with new images and then if I donāt like it I can select the main area of interest and it will boost or drop exposureā¦. I donāt think he is using the auto setting of the exposure moduleā¦.